I have two models like this:
class User(models.Model):
email = models.EmailField()
class Report(models.Model):
user = models.ForeignKey(User)
New in Django 1.11 you can add EXISTS subqueries:
User.objects.annotate(
no_reports=~Exists(Reports.objects.filter(user__eq=OuterRef('pk')))
).filter(
email__startswith='a',
no_reports=True
)
This generates SQL something like this:
SELECT
user.pk,
user.email,
NOT EXISTS (SELECT U0.pk FROM reports U0 WHERE U0.user = user.pk) AS no_reports
FROM user
WHERE email LIKE 'a%' AND NOT EXISTS (SELECT U0.pk FROM reports U0 WHERE U0.user = user.pk);
A NOT EXISTS clause is almost always the most efficient way to do a "not exists" filter.
As of Django 3.0 you can now use expressions directly in a filter(), removing the unnecessary SQL clause:
User.objects.filter(
email__startswith='a',
~Exists(Reports.objects.filter(user__eq=OuterRef('pk')))
)
SELECT user.pk, user.email
FROM user
WHERE email LIKE 'a%' AND NOT EXISTS (SELECT U0.pk FROM reports U0 WHERE U0.user = user.pk);