Alexander Zot
Alexander Zot

Reputation: 65

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.

Upvotes: 0

Views: 491

Answers (1)

ruddra
ruddra

Reputation: 52028

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'))

Upvotes: 1

Related Questions