Django Queryset with filtering on reverse foreign key

DjangoModelFilterDjango Queryset

Django Problem Overview


I have the following Django model:

class Make:
   name = models.CharField(max_length=200)

class MakeContent:
   make = models.ForeignKey(Make)
   published = models.BooleanField()

I'd like to know if it's possible (without writing SQL directly) for me to generate a queryset that contains all Makes and each one's related MakeContents where published = True.

Django Solutions


Solution 1 - Django

Yes, I think you want

make = Make.objects.get(pk=1)
make.make_content_set.filter(published=True)

or maybe

make_ids = MakeContent.objects.filter(published=True).values_list('make_id', flat=True)
makes = Make.objects.filter(id__in=make_ids)

Solution 2 - Django

I know this is very old question, but I am answering. As I think my answer can help others. I have changed the model a bit as follows. I have used Django 1.8.

class Make(models.Model):
    name = models.CharField(max_length=200)

class MakeContent(models.Model):
    make = models.ForeignKey(Make, related_name='makecontent')
    published = models.BooleanField()

I have used the following queryset.

Make.objects.filter(makecontent__published=True)

Hope it will help.

Solution 3 - Django

Django doesn't support the select_related() method for reverse foreign key lookups, so the best you can do without leaving Python is two database queries. The first is to grab all the Makes that contain MakeContents where published = True, and the second is to grab all the MakeContents where published = True. You then have to loop through and arrange the data how you want it. Here's a good article about how to do this:

http://blog.roseman.org.uk/2010/01/11/django-patterns-part-2-efficient-reverse-lookups/

Solution 4 - Django

Let me translate Spike's worded answer into codes for future viewers. Please note that each 'Make' can have zero to multiple 'MakeContent'

If the asker means to query 'Make' with AT LEAST ONE 'MakeContent' whose published=True, then Jason Christa's 2nd snippet answers the question.

The snippet is equivalent to

makes = Make.objects.select_related().filter(makecontent__published=True).distinct()

But if the asker means to query 'Make' with ALL 'MakeContent' whose published=True, then following the 'makes' above,

import operator
make_ids = [m.id for m in makes if 
    reduce(operator.and_, [c.published for c in m.makecontent_set.all()] ) 
]
makes_query = Make.objects.filter(id__in=make_ids)

contains the desired query.

Solution 5 - Django

One more time, it is not clear what was the question, but if it was desired that all related MakeContent objects must have been published this can work:

Make.objects.exclude(MakeContent_set__published=False)

And if at least one of them (as it was in other answers):

Make.objects.filter(MakeContent_set__published=True)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJulian A.View Question on Stackoverflow
Solution 1 - DjangoJason ChristaView Answer on Stackoverflow
Solution 2 - Djangouser1012513View Answer on Stackoverflow
Solution 3 - DjangoSpikeView Answer on Stackoverflow
Solution 4 - DjangoSYKView Answer on Stackoverflow
Solution 5 - DjangoGhraView Answer on Stackoverflow