From fd3cd5de59dfa5b6f899f7cbebfd00904c7c9df1 Mon Sep 17 00:00:00 2001 From: Ayush khatri Date: Mon, 2 Dec 2024 23:59:52 +0530 Subject: [PATCH] Fixed #34533 -- OuterRef not resolved as part of ORDER BY clause --- django/db/models/sql/query.py | 28 ++++++++++++++++++++++++++++ tests/expressions/models.py | 2 +- tests/expressions/tests.py | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index cca11bfcc2..ceeee473a1 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -2779,3 +2779,31 @@ class JoinPromoter: query.promote_joins(to_promote) query.demote_joins(to_demote) return to_demote + + +def resolve_order_by_expressions(self, order_by_expressions): + """ + Resolve expressions in the order_by clause containing OuterRef by cloning + and resolving them. Other expressions are appended as is. + """ + resolved_expressions = [] + for expr in order_by_expressions: + if ( + hasattr(expr, "contains_outer_reference") + and expr.contains_outer_reference() + ): + clone = expr.clone() + clone.outer_query = self.outer_query + resolved_expressions.append(clone.resolve_expression(self)) + else: + resolved_expressions.append(expr) + return resolved_expressions + + +def get_compiler(self, *args, **kwargs): + """ + Override get_compiler to resolve order_by expressions containing OuterRef. + """ + if self.order_by: + self.order_by = self.resolve_order_by_expressions(self.order_by) + return super().get_compiler(*args, **kwargs) diff --git a/tests/expressions/models.py b/tests/expressions/models.py index 3909bdf0cd..bb3e04bf68 100644 --- a/tests/expressions/models.py +++ b/tests/expressions/models.py @@ -32,7 +32,7 @@ class RemoteEmployee(Employee): class Company(models.Model): name = models.CharField(max_length=100) num_employees = models.PositiveIntegerField() - num_chairs = models.PositiveIntegerField() + num_chairs = models.PositiveIntegerField(default=0) ceo = models.ForeignKey( Employee, models.CASCADE, diff --git a/tests/expressions/tests.py b/tests/expressions/tests.py index af4cf01fca..8f5ddf73a1 100644 --- a/tests/expressions/tests.py +++ b/tests/expressions/tests.py @@ -2849,3 +2849,36 @@ class OrderByTests(SimpleTestCase): F("field").asc(nulls_first=False) with self.assertRaisesMessage(ValueError, msg): F("field").desc(nulls_last=False) + + +class OuterRefOrderByTestCase(TestCase): + @classmethod + def setUpTestData(cls): + cls.ceo = Employee.objects.create( + firstname="John", lastname="Doe", salary=100000 + ) + cls.gmbh = Company.objects.create( + name="GmbH", + num_employees=50, + ceo=cls.ceo, + point_of_contact=cls.ceo, + based_in_eu=True, + ) + cls.foobar_ltd = Company.objects.create( + name="Foobar Ltd", + num_employees=100, + ceo=cls.ceo, + point_of_contact=cls.ceo, + based_in_eu=False, + ) + + def test_order_by_with_outerref(self): + inner = Company.objects.filter( + num_employees__lt=OuterRef("num_employees") + ).order_by("num_employees") + + qs = Company.objects.annotate(next_bigger=Subquery(inner.values("pk")[:1])) + foobar_ltd = qs.get(pk=self.foobar_ltd.pk) + + self.assertIsNotNone(foobar_ltd.next_bigger) + self.assertEqual(foobar_ltd.next_bigger, self.gmbh.pk)