Django has extensive documentation for it’s ORM but somehow I still
end up surprised by some of the queries it builds. The default logging
configuration doesn’t log queries in the way Rails does (in its
development environment) so when a query appears to give the correct
results, I don’t usually check the SQL. But I recently had a page
start to fail; I finally tracked it down to a specific query but
couldn’t immediately see why I was not getting the row I expected so I
printed the query and Django was not building the query I thought it
had been. The python is:
This produces the following SQL (edited slightly to make it easier to read):
Hmmm that’s not what I want. I don’t want 2 subqueries, one for each
condition. I want one subquery, with two two conditions. If I had
wanted 2 subqueries, I would have written 2 excludes, like this:
But both of those QuerySet definitions produce the same SQL. So how
can I produce the following SQL using the Django ORM:
I tried a couple of things using Q but mostly ended up with syntax
errors. Fortunately I finally found this Stack Overflow thread
with references the bug report for this problem AND the solution. You
can force Django to build the desired subquery by writing the subquery
explicitly:
It’s a little verbose, but it is actually a little clearer in some
respects - it is more like a direct python translation of the desired SQL.