47

This is bound to be a duplicate question but I can't find any others. I'm trying to get a list of photos that have complaints. I can't simply get complaints and deal with the related photos - I need a queryset of photos.

This should work but doesn't seem right:

Photo.objects.filter(complaint__id__gte=0)

This doesn't seem like the most efficient way:

Photo.objects.annotate(Count('complaint')).exclude(complaint__count=0)

Is there a better way?

3 Answers 3

90

how about ...

Photo.objects.filter(complaint__isnull=False)

from https://docs.djangoproject.com/en/dev/topics/db/queries/

Sign up to request clarification or add additional context in comments.

3 Comments

You might also need a .distinct() in the end to replicate the same as an annotate does. At least I did.
@Christoffer you saved my life, thanks. It is a deadly issue, so i want to write a comment and improve emphasis.
This is less code, even its SQL is, but to my experience the query can get extremely slow. It generates a LEFT JOIN with the referencing table and a WHERE clause checking for NULL. This doesn't let the query planner use an index that may be in place for the foreign key. Using an Exists() subquery is way faster.
24

I'm not sure which variant is the best, but this works as well:

Photo.objects.exclude(complaint=None)

Generated SQL query is not exactly like in the case of .filter(complaint__isnull=False), but sense is identical.

5 Comments

This is better than the accepted answer, because it doesn't require a .distinct() call to work with multi-valued relationships.
This answer also works when using Q objects. This code works: Q(related_object=None) whether this doesn't: Q(related_object__is_null=True)
this is shorter but also slower.
@ezdookie I tested it locally (50 k rows), speed is almost the same, the difference is about 5%.
@ezdookie see my comment on the accepted answer. This kind of filtering can prevent potential index-only scans.
4

Depending on the complexity of the relationship and filter logic you might need this (or this can turn out to be more readable):

complaints = Complaint.objects.filter(
    # some complex filter here
   Q(...) & Q(...) | Q(...)
)

Photo.objects.annotate(
    has_complaints=Exists(complaints)
).filter(has_complaints=True)

1 Comment

You likely want to use OuterRef in those Q(..) parts so that the complaints are linked to the photos you are querying . See: docs.djangoproject.com/en/5.0/ref/models/expressions/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.