I have two models like this:
class User(models.Model):
email = models.EmailField()
class Report(models.Model):
user = models.ForeignKey(User)
In reality each model has more fields which are of no consequence to this question.
I want to filter all users who have an email which starts with 'a' and have no reports. There will be more .filter()
and .exclude()
criteria based on other fields.
I want to approach it like this:
users = User.objects.filter(email__like = 'a%')
users = users.filter(<other filters>)
users = ???
I would like ??? to filter out users who do not have reports associated with them. How would I do this? If this is not possible as I have presented it, what is an alternate approach?
To filter users who do not have reports associated with them try this:
users = User.objects.exclude(id__in=[elem.user.id for elem in Report.objects.all()])
New in Django 1.11 you can add
EXISTS
subqueries:This generates SQL something like this:
A
NOT EXISTS
clause is almost always the most efficient way to do a "not exists" filter.Once #25367 is released, you'll be able to use
~Exists()
directly in a.filter()
, avoiding the duplicate clause.The only way to get native SQL EXISTS/NOT EXISTS without extra queries or JOINs is to add it as raw SQL in the .extra() clause:
In fact, it's a pretty obvious and efficient solution and I sometimes wonder why it wasn't built in to Django as a lookup. Also it allows to refine the subquery to find e.g. only users with[out] a report during last week, or with[out] an unanswered/unviewed report.
Alasdair's answer is helpful, but I don't like using
distinct()
. It can sometimes be useful, but it's usually a code smell telling you that you messed up your joins.Luckily, Django's queryset lets you filter on subqueries.
Here are a few ways to run the queries from your question:
If you put that into a Python file and run it, you should see something like this:
You can see that the final query uses all inner joins.
Use
isnull
.When you use
isnull=False
, thedistinct()
is required to prevent duplicate results.