Django association between three models - django

I have these three models :
Person.
Project.
Role.
Using Django's models system, how to represent the fact that a person is participating in a particular project with a specific role ?
general problem : What is the proper way to handle "ternary associations" using Django ?

I would do it using an intermediary model for m2m relationship and add a field there.
Something like this:
class Role(models.Model):
name = models.CharField(max_lenth=32)
class Project(models.Model):
name = models.CharField(max_lenth=32)
class PersonProject(models.Model):
person = models.ForeignKey('.Person')
project = models.ForeignKey(Project)
role = models.ForeignKey(Role)
class Person(models.Model):
projects = models.ManyToManyField(Project, through=PersonProject)

Related

Multiple many-to-many relations through the same model in Django problem

i have a problem i don't know how to make multiple many-to-many relations through the same model.
This is my DB relations :
Db architecture
and this is my code, i want to know if what I did is right or not ?
class Projects(models.Model):
Project_name = models.CharField(max_length=50)
Poject_key = models.CharField(max_length=50)
CITools = models.ForeignKey(CITools,null=True,on_delete=models.CASCADE)
UsersO = models.ManyToManyField(User,through='OwnerShips') # for user Ownerships
UserF = models.ManyToManyField(User,through='Favorites') # for user Favorites
This is my OwnerSHips class :
class OwnerShips(models.Model):
user = models.ForeignKey(User,on_delete=models.CASCADE)
project = models.ForeignKey(Projects,on_delete=models.CASCADE)
And this is my Favorites Class:
class Favorites(models.Model):
user = models.ForeignKey(User,on_delete=models.CASCADE)
project = models.ForeignKey(Projects,on_delete=models.CASCADE)
I don't think you can use foreign keys (ownerships and favorites) in Project before those classes are created.
Actually i found the solution, i used the attribute related_name, so Django to create this two tables automatically

Django Filtering Many-to-Many relationship without ManyToManyField "through" relationship

Given these models:
class Event(models.Model):
name = models.CharField()
class Project(models.Model):
name = models.CharField()
class EventProject(models.Model):
event= models.ForeignKey(Event)
project= models.ForeignKey(Project)
Is there a way to get all the Events for each Project like this: with the property project.events = [<array of Event>]?
I found a similar question, however one of the tables has the members = models.ManyToManyField(Person, through='Membership') relationship so it's easier to do. I can't do as in the related question and add a relationship as I cannot change the model.
It is straight Forward
Event.objects.filter(eventproject__project=project)
Filtering through ForeignKey works both forward and backward in Django. You can use the model name in lower case for backward filtering.
https://docs.djangoproject.com/en/3.1/topics/db/queries/#lookups-that-span-relationships

Django many-to-many lookup from different models

I have some models that represents some companies and their structure. Also all models can generate some Notifications (Notes). User can see own Notes, and, of course, can't see others.
class Note(models.Model):
text = models.CharField(...)
class Company(models.Model):
user = models.ForeignKey(User)
note = models.ManyToManyField(Note, blank='True', null='True')
class Department(models.Model):
company = models.ForeignKey(Company)
note = models.ManyToManyField(Note, blank='True', null='True')
class Worker(models.Model):
department = models.ForeignKey(Department)
note = models.ManyToManyField(Note, blank='True', null='True')
class Document(models.Model)
company = models.ForeignKey(Company)
note = models.ManyToManyField(Note, blank='True', null='True')
The question is how I can collect all Notes for particular user to show them?
I can do:
Note.objects.filter(worker__company__user=2)
But its only for Notes that was generated by Workers. What about another? I can try hardcoded all existing models, but if do so dozen of kittens will die!
I also tried to use backward lookups but got "do not support nested lookups". May be I did something wrong.
EDIT:
As I mentioned above I know how to do this by enumerating all models (Company, Worker, etc. ). But if I will create a new model (in another App for example) that also can generate Notes, I have to change code in the View in another App, and that's not good.
You can get the Notes of a user by using the following query:
For example let us think that a user's id is 1 and we want to keep it in variable x so that we can use it in query. So the code will be like this:
>>x = 1
>>Note.objects.filter(Q(**{'%s_id' % 'worker__department__company__user' : x})|Q(**{'%s_id' % 'document__company__user' : x})|Q(**{'%s_id' % 'company__user' : x})|Q(**{'%s_id' % 'department__company__user' : x})).distinct()
Here I am running OR operation using Q and distinct() at the end of the query to remove duplicates.
EDIT:
As I mentioned above I know how to do this by enumerating all models
(Company, Worker, etc. ). But if I will create a new model (in another
App for example) that also can generate Notes, I have to change code
in the View in another App, and that's not good.
In my opinion, if you write another model, how are you suppose to get the notes from that model without adding new query? Here each class (ie. Department, Worker) are separately connected to Company and each of the classes has its own m2m relation with Note and there is no straight connection to User with Note's of other classes(except Company). Another way could be using through but for that you have change the existing model definitions.
Another Solution:
As you have mentioned in comments, you are willing to change the model structure if it makes your query easier, then you can try the following solution:
class BaseModel(models.Model):
user = models.Foreignkey(User)
note = models.ManyToManyField(Note)
reports_to = models.ForeignKey('self', null=True, default=None)
class Company(BaseModel):
class Meta:
proxy = True
class Document(BaseModel):
class Meta:
proxy = True
#And so on.....
Advantages: No need to create separate table for document/company etc.
object creation:
>>c= Company.objects.create(user_id=1)
>>c.note.add(Note.objects.create(text='Hello'))
>>d = Document.objects.create(user_id=1, related_to=c)
>>d.note.add(Note.objects.create(text='Hello World'))

Use fields between OneToOneField in a model

I am in a situation where I cannot use inheritance. Say I have two models, a Person and a Passport, for this example assume they have a One-to-one relationship:
class Passport(models.Model):
full_name = models.CharField()
class Person(models.Model):
passport = models.OneToOneField(Passport)
Now from here, I want to get a Person's full_name. In my application, a Person cannot exist without a unique Passport, so how do I access the full_name attribute through a Person object. I ultimately want to be able to do something like this:
class Person(models.Model):
passport = models.OneToOneField(Passport)
def __unicode__(self):
return self.passport.full_name
Is there a simple solution to this is Django?
Yes, the simple and correct way of doing this is exactly what you did: self.passport.full_name returns the full name of the person, which is an attribute of the passport.
Notice that because the passport ForeignKey cannot be null as you defined it, the self.passport will always exist.

Django: Access model through another model

class Project(models.Model):
title = models.CharField()
class Job(models.Model):
name = models.CharField()
user = models.ForeignKey(User)
project = models.ForeignKey(Project)
I have many jobs for each project. How do i get a list of all users of all jobs of a project?
I came up with this:
users = set()
for job in project.job_set.all():
users.add(job.user)
Is there an easier way without explicitely looping through every job?
Use Django's join syntax and start from your User model instead:
users = User.objects.filter(job_set__project=project).distinct()
Alternatively, you could use a ManyToMany relation from Project to User through the Job model which is in effect a join model:
class Project(models.Model):
users = models.ManyToManyField(User, through='Job')
...
And then simply do:
project.users.all()