diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index 2968e2cfee..b2a90c1a3f 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -1041,11 +1041,11 @@ class Exists(Subquery): def __invert__(self): return type(self)(self.queryset, negated=(not self.negated), **self.extra) - def resolve_expression(self, query=None, **kwargs): + def resolve_expression(self, query=None, *args, **kwargs): # As a performance optimization, remove ordering since EXISTS doesn't # care about it, just whether or not a row matches. self.queryset = self.queryset.order_by() - return super().resolve_expression(query, **kwargs) + return super().resolve_expression(query, *args, **kwargs) def as_sql(self, compiler, connection, template=None, **extra_context): sql, params = super().as_sql(compiler, connection, template, **extra_context) diff --git a/docs/releases/2.0.3.txt b/docs/releases/2.0.3.txt index 277cab7573..252e51fcf0 100644 --- a/docs/releases/2.0.3.txt +++ b/docs/releases/2.0.3.txt @@ -15,3 +15,5 @@ Bugfixes * Prioritized the datetime and time input formats without ``%f`` for the Thai locale to fix the admin time picker widget displaying "undefined" (:ticket:`29109`). + +* Fixed crash with ``QuerySet.order_by(Exists(...))`` (:ticket:`29118`). diff --git a/tests/expressions/tests.py b/tests/expressions/tests.py index 218e540bd9..c2e3fb391a 100644 --- a/tests/expressions/tests.py +++ b/tests/expressions/tests.py @@ -38,10 +38,8 @@ class BasicExpressionsTests(TestCase): name="Foobar Ltd.", num_employees=3, num_chairs=4, ceo=Employee.objects.create(firstname="Frank", lastname="Meyer", salary=20) ) - cls.gmbh = Company.objects.create( - name="Test GmbH", num_employees=32, num_chairs=1, - ceo=Employee.objects.create(firstname="Max", lastname="Mustermann", salary=30) - ) + cls.max = Employee.objects.create(firstname='Max', lastname='Mustermann', salary=30) + cls.gmbh = Company.objects.create(name='Test GmbH', num_employees=32, num_chairs=1, ceo=cls.max) def setUp(self): self.company_query = Company.objects.values( @@ -399,6 +397,14 @@ class BasicExpressionsTests(TestCase): ) self.assertEqual(str(qs.query).count('JOIN'), 2) + def test_order_by_exists(self): + mary = Employee.objects.create(firstname='Mary', lastname='Mustermann', salary=20) + mustermanns_by_seniority = Employee.objects.filter(lastname='Mustermann').order_by( + # Order by whether the employee is the CEO of a company + Exists(Company.objects.filter(ceo=OuterRef('pk'))).desc() + ) + self.assertSequenceEqual(mustermanns_by_seniority, [self.max, mary]) + def test_outerref(self): inner = Company.objects.filter(point_of_contact=OuterRef('pk')) msg = (