Hi folks!
I have a piece of low hanging fruit I'd like to suggest and contribute: ALL subqueries.
Here's the
PG docs on ALL subqueries but basically it causes the subquery expression to evaluate to true only if all rows themselves evaluate to true when compared against the supplied value & comparison operator.
The syntax is:
There are a couple of points to note:
1. Postgres, MySQL & SQL Server all support ALL subquerying but unfortunately SQLite does not. A cursory search about Oracle suggests it may support it but couldn't see anything in the official docs.
2. An equivalent expression is to use NOT EXISTS and move the comparison logic into the subquery & invert it (but leaving any correlating logic uninverted).
A couple of further points on the NOT EXISTS approach:
- It uses double negatives and hinders the readability.
- It's not always obvious what the correct way to invert the subquery is – I found I had to be careful with filters involving many relationship lookups.
That being said I found that getting ALL working in Django only took a few lines. From my initial experimentation I saw 3 possible ways of doing this:
1. Lookups: A lookup could be defined for each operator supported.
eg using Exact:
class All(Exact):
lookup_name = "all"
def get_rhs_op(self, connection, rhs):
return connection.operators[super().lookup_name] % f"ALL {rhs}"
Parent.objects.annotate(test=Value("test")).filter(test__all=Subquery(subquery))
2. Subclass Subquery
This is my favoured approach as it resembles Python's all() builtin. It uses almost identical code to Exists except for one line that defines the exists expression. The idea is that subqueries supply a single boolean column created with annotate:
class All(Subquery):
template = "'t' = ALL (%(subquery)s)"
output_field = fields.BooleanField()
# similar methods to Exists omitted
Parent.objects.filter(
All(
Child.objects.annotate(is_result=Q(<expression evaluating to bool>)).filter(something=OuterRef('something').values('is_result')
)
)
I found this work well for my codebase and I'd love to explore contributing it back to Django. I'd suggest the subquery subclass as it results in more readable code – but there is the question about lack of support for SQLite. One approach would be to note in the documentation on how to write the equivalent using NOT EXISTS.
What do folks think?
Cheers,
David