Reverse One-to-Many-Relationship in Django - django-models

My app links invoices, contracts and services with Many-to-One-Relationships:
class Invoice(models.Model):
contract = models.ForeignKey(Contract, on_delete=models.CASCADE)
class Contract(models.Model):
service = models.ForeignKey(Service, on_delete=models.CASCADE)
Whenever a new invoice is registered, it can be linked to a service and split/billed internally. Unfortunately, some contracts/invoices need to be linked to more than one service according to a fixed split (e.g. 30/70).
For this to work on the surface, I could to reverse the relationship between contracts and services –
class Service(models.Model):
contract = models.ForeignKey(Contract, on_delete=models.CASCADE)
– or change the ForeignKey field on the Contract class to a ManyToManyField.
But in both cases, I will not be able to get back from the invoice to the service easily anymore, as with the following statement:
invoices = Invoice.objects.filter(models.Q(contract__service__building=self.tenant.unit.building), models.Q(begin__lte=self.begin, end__gt=self.begin) | models.Q(begin__gt=self.begin, begin__lt=self.end))
Is it wise to insert an intermediate helper model (ContractService) with two ForeignKey fields to keep the current app logic and add the option to link a contract to more than one service?

Ok just to clarify one example:
Contract is "Cleaning of House"
Services are "Cleaning of first floor" and "Cleaning of second floor"
Invoices are "Invoice1", "Invoice2", ...
You want a relationship that "Invoice1" can be linked to "Cleaning of first floor" AND "Cleaning of second floor".
models.py
class Contract(models.Model):
"""Can hold multiple Services"""
pass
class Service(models.Model):
"""Is linked to one specific Contract"""
contract = models.ForeignKey(Contract, on_delete=models.CASCADE)
class Invoice(models.Model):
"""Can hold multiple Services and one Service can hold multiple Invoices"""
service = models.ManyToManyField(Service)
Now your question is:
"I can easily get the contract when I have the Service object, but how can I get the Service when I have the Contract object?
con = Contract.objects.all().first()
queryset = con.service_set.all() # gives you all related Services for that specific Contract
Read more about ManytoOne
And:
"How can I get the Invoice when I have the Service object?"
ser = Service.objects.all().first()
queryset = ser.invoice_set.all() # gives you all related Invoices for that specific Service
Read more about ManyToMany
Let me know how it goes

Thanks to #Tarquinius for your help – I found a solution based on his suggestion. The three models in question are connected as follows:
class Service(models.Model):
pass
class Contract(models.Model):
services = models.ManyToManyField(Service)
class Invoice(models.Model):
contract = models.ForeignKey(Contract, on_delete=models.CASCADE)
The query quoted above did not even need to be modified (apart from the different fieldname) to reflect the possibility of more than one service per contract, I just had to add the distinct() function:
invoices = Invoice.objects.filter(models.Q(contract__services__building=self.tenancy_agreement.unit.building), models.Q(begin__lte=self.begin, end__gt=self.begin) | models.Q(begin__gt=self.begin, begin__lt=self.end)).distinct()
In hindsight, this is quite obvious (and simple).

Related

Django Rest returning related items for each item

I was trying to find answer in similiar questions, but none was meeting my expectations.
I have 2 models:
class Artist(models.Model):
name = models.CharField(max_length=255)
music_type = models.CharField(max_lenght=50)
def __str__(self):
return self.name
class Event(models.Model):
event_name = models.CharField(max_length=255)
...
artists = models.ManyToManyField(Artist)
def __str__(self):
return self.event_name
I also have serializers.py file:
class EventSerializer(serializers.ModelSerializer):
class Meta:
model = Event
fields = '__all__'
class ArtistSerializer(serializers.ModelSerializer):
events = EventSerializer(source='event_set', many=True)
class Meta:
model = Artist
fields ='__all__'
The event in ArtistSerializer allows me to return all events where artist takes part.
Now for each artist I would like to get list of all artists if they ever were taking part in the same event.
For example I have 5 Artists (A1...A5) and 3 Events (E1...E3)
In Event 1: [A1,A3]
In Event 2: [A3,A4,A5,A2]
In Event 3: [A2, A3]
So for A3 I would like to get list [A1,A4,A5,A2]
For A1: [A3]
For A2: [A3,A4,A5]
Unfortunately I have huge problem to create this query as SQL-query and ORM mechanism looks more complicated in this situation. Can somebody help me with this problem or give hints how to solve this?
If it's needed I'm gonna share more code
You can query the through model to get the artists related to an event. This is the intermediate model which django will have created to make that M2M relationship.
Where you have a ManyToManyField it has an attribute of through which is the M2M model.
So from your event model you could do something like Event.artists.through.objects.all() and you'd see all the instances in your M2M model.
So to find out the artists which are linked to a given event you could query that same table;
Event.artists.through.objects.filter(event_id=1).select_related('artist')
This would then return all the objects in the M2M which belong to Event 1. You could then get the artists from there, or just grab the artist IDs Event.artists.through.objects.filter(event_id=1).values_list('artist_id, flat=True)
Given the scenario in your comment...
If you have an artist, then you can run a query to get the events they've been in, and then run another query with those event ids. In that second query you are then looking to get the artist ids that aren't the current artist you're already looking at.
# First get the events that the current artist has been in
event_ids = Event.artists.through.objects.filter(artist_id=1).values_list('event_id, flat=True)
# Then get the other artists who have been in the same events
other_artist_ids = Event.artists.through.objects.filter(event_id__in =event_ids).exclude(artist_id=1).values_list('artist_id, flat=True)
# or the full instances
other_artists = Event.artists.through.objects.filter(event_id__in =event_ids).exclude(artist_id=1).select_related('artist')

How to Implement ManyToManyField in Django

I am trying to create a small django project of a football application to show the stats & stuff.. to start with I created two classes in my models.py.. with a many to many relation.. but for some reason it's throwing a strange Database Error: no such table: football_league_team
please any help is appreciated, thanks in advance.
from django.db import models
# Create your models here.
class Team(models.Model):
team_name = models.CharField(max_length=30, unique=True)
team_code = models.CharField(max_length=4, unique=True)
team_home = models.CharField(max_length=30, unique=True)
team_registry_date = models.DateTimeField('Date of Registry')
def __unicode__(self):
return self.team_name
class League(models.Model):
league_name = models.CharField(max_length=30)
league_code = models.CharField(max_length=4)
league_division = models.IntegerField()
team = models.ManyToManyField(Team)
def __unicode__(self):
return self.league_name
You removed the field football_league_team in one of your model. Django doesn't know it and is still expecting said field. Depending your Django version, there are several ways to reset the corresponding model.
Django 1.4 and lower
> ./manage.py reset <appname>
I believe it works for earlier versions of Django, not sure though. Keep in mind that this option will reset each models of your application as opposed to the below method which allow single table drops.
Django 1.5 and higher
> ./manage.py sqlclear <appname>
will print out the commands to clear the database from the application's models.
> ./manage.py dbshell
Will allow you to use the sqlclear commands in order to drop the tables yopu want to be reseted.

Django: Single model for multiple tables

I have a main table
Slideshow
then a site specific table that captures a few extra details for that site.
Site1_Slideshow
In a web app (specific to a site) i want a single model i.e. Slideshow that combines the 2 tables above.
Currently i have the code below, but i dont think this is correct. I cant do things like
s = Slideshow.objects.get(slideshowId=1) as Slideshows only has the properties featurecategory and slideshow. So how can i have an model called Slideshow that is composed of these 2 tables but looks like it was a single db table.
class SlideshowAbstract(models.Model):
slideshowid = models.IntegerField(primary_key=True, db_column=u'SlideshowId') # Field name made lowercase.
headline = models.TextField(db_column=u'Headline') # Field name made lowercase.
class Meta:
db_table = u'Slideshow'
class Slideshow(models.Model):
slideshow = models.OneToOneField(SlideshowAbstract, primary_key=True,db_column=u'SlideshowId')
def __unicode__(self):
return self.slideshow.headline
class Meta:
db_table = u'Site1_Slideshow'
Think i found the solution.
On the Site1_Slideshow you need to add a column for django to use, that i presume is always the same as primary key value.
Its name is SlideshowAbstract_ptr_id
Once that is added you can change the Slideshow model to be
class Slideshow(SlideshowAbstract):
featureCategory = models.ForeignKey(Featurecategory,db_column=u'FeatureCategoryId')
def __unicode__(self):
return self.headline
class Meta:
db_table = u'Site1_Slideshow'
So doable but not the nicest if you are not doing "model first" and already have the schema. Would be good to be able to override the name of the _ptr_id column.
I did try adding the following to Slideshow too see if i could map this ptr col to the primary key
slideshowabstract_ptr_id = models.IntegerField(primary_key=True, db_column=u'SlideshowId')
but no cigar.
I havent tested inserts either but ...objects.all() works

Editing Data From a Domain Service

I need to edit data in a WCF RIA Domain Service. all examples I can find are using a datagrid.
I have the following but it is retuning a null value, where am I going wrong???
var mytask = from v in DomainRentDetail.tblRentDetails
where v.CustID == xCustID
select v;
tblRentDetail t = mytask.FirstOrDefault<tblRentDetail>();
t.ReturnDate = DateTime.Now;
DomainRentDetail.SubmitChanges();
Please give us more detail but,
I think it must be like this, In a domain service class (I suppose you are on web project)
from v in this.ObjectContext.tblRentDetails
where v.....
select v
If there are some records on your table tblRentDetails
you may check it by FirstOrDefault << Type >> ()== null
When you create your domain service class you must choose your domain(edmx). Then ObjectContext carries your entitiy objects. Ask your Entities to this ObjectContext member.
Note: The below state is an extreme stuation. Maybe you may face off laterly.
If you need diffrent model you are not created domain service class for
there are some other technics,
//Scope level domain service class definition. Not offered.
//Generally views solves this issue
using(XDomainService service=new XDomainService())
{
from one in service
from two in this.objectContext
where...
select new member
}

App Engine Messaging System with Message Status - Design Pattern

I'm building a Threaded Messaging System that will be hosted on Google AppEngine
I've modeled it after the technique described by Brett Slatkin in Building Scalable, Complex Apps on App Engine
class Message(db.Model):
sender = db.StringProperty()
body = db.TextProperty()
class MessageIndex(db.Model):
receivers = db.StringListProperty()
The issue I'm having to determining the most efficient way to track the message state for a User.
For example is a message read, archived, deleted for a particular user.
Here are the solution I have come up with so far.
I'm using Datastore+'s StructuredProperty to add a state to the message MessageIndex
class Message(model.Model):
sender = model.StringProperty()
body = model.TextProperty()
class _DisplayState(model.Model):
user_key = model.KeyProperty()
state = model.IntegerProperty(default=0) # 0-unread, 1-read, 2-archived
class MessageIndex(model.Model):
receivers = model.StructuredProperty(_DisplayState, repeated=True)
This solution, while simple, negates the benefit of the MessageIndex. Additionally since the the MessageIndex is in the same entity group as the message datastore writes will be limited.
Full Source Code
What would be the most efficient way to accomplish this? Would adding an additional entity group be a better solution?
class MessageState(model.Model):
user_key = model.KeyProperty()
message_key = model.KeyPropery()
message_state = model.IntegerProperty(default=0) # 0-unread, 1-read, 2-archived
For the easiest querying - split your 'receivers' list into four different lists - 'unread', 'read', 'archived', 'deleted' and shuffle the receiver record between the lists as appropriate.

Resources