Fixed #27021 -- Allowed lookup expressions in annotations, aggregations, and QuerySet.filter().

Thanks Hannes Ljungberg and Simon Charette for reviews.

Co-authored-by: Mariusz Felisiak <felisiak.mariusz@gmail.com>
This commit is contained in:
Ian Foote 2021-04-02 18:25:20 +01:00 committed by Mariusz Felisiak
parent f5dccbafb9
commit f42ccdd835
11 changed files with 268 additions and 48 deletions

View file

@ -48,6 +48,10 @@ objects that have an ``output_field`` that is a
:class:`~django.db.models.BooleanField`. The result is provided using the
``then`` keyword.
.. versionchanged:: 4.0
Support for lookup expressions was added.
Some examples::
>>> from django.db.models import F, Q, When
@ -68,6 +72,13 @@ Some examples::
... account_type=OuterRef('account_type'),
... ).exclude(pk=OuterRef('pk')).values('pk')
>>> When(Exists(non_unique_account_type), then=Value('non unique'))
>>> # Condition can be created using lookup expressions.
>>> from django.db.models.lookups import GreaterThan, LessThan
>>> When(
... GreaterThan(F('registered_on'), date(2014, 1, 1)) &
... LessThan(F('registered_on'), date(2015, 1, 1)),
... then='account_type',
... )
Keep in mind that each of these values can be an expression.

View file

@ -25,6 +25,7 @@ Some examples
from django.db.models import Count, F, Value
from django.db.models.functions import Length, Upper
from django.db.models.lookups import GreaterThan
# Find companies that have more employees than chairs.
Company.objects.filter(num_employees__gt=F('num_chairs'))
@ -76,6 +77,13 @@ Some examples
Exists(Employee.objects.filter(company=OuterRef('pk'), salary__gt=10))
)
# Lookup expressions can also be used directly in filters
Company.objects.filter(GreaterThan(F('num_employees'), F('num_chairs')))
# or annotations.
Company.objects.annotate(
need_chairs=GreaterThan(F('num_employees'), F('num_chairs')),
)
Built-in Expressions
====================

View file

@ -177,16 +177,21 @@ following methods:
comparison between ``lhs`` and ``rhs`` such as ``lhs in rhs`` or
``lhs > rhs``.
The notation to use a lookup in an expression is
``<lhs>__<lookup_name>=<rhs>``.
The primary notation to use a lookup in an expression is
``<lhs>__<lookup_name>=<rhs>``. Lookups can also be used directly in
``QuerySet`` filters::
This class acts as a query expression, but, since it has ``=<rhs>`` on its
construction, lookups must always be the end of a lookup expression.
Book.objects.filter(LessThan(F('word_count'), 7500))
…or annotations::
Book.objects.annotate(is_short_story=LessThan(F('word_count'), 7500))
.. attribute:: lhs
The left-hand side - what is being looked up. The object must follow
the :ref:`Query Expression API <query-expression>`.
The left-hand side - what is being looked up. The object typically
follows the :ref:`Query Expression API <query-expression>`. It may also
be a plain value.
.. attribute:: rhs
@ -213,3 +218,8 @@ following methods:
.. method:: process_rhs(compiler, connection)
Behaves the same way as :meth:`process_lhs`, for the right-hand side.
.. versionchanged:: 4.0
Support for using lookups in ``QuerySet`` annotations, aggregations,
and directly in filters was added.