1
0
mirror of https://github.com/django/django.git synced 2025-06-05 11:39:13 +00:00

Fixed #34533 -- OuterRef not resolved as part of ORDER BY clause

This commit is contained in:
Ayush khatri 2024-12-02 23:59:52 +05:30
parent 58cc91275a
commit dbc30234be
3 changed files with 91 additions and 1 deletions

View File

@ -2779,3 +2779,31 @@ class JoinPromoter:
query.promote_joins(to_promote) query.promote_joins(to_promote)
query.demote_joins(to_demote) query.demote_joins(to_demote)
return 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)

View File

@ -32,7 +32,7 @@ class RemoteEmployee(Employee):
class Company(models.Model): class Company(models.Model):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
num_employees = models.PositiveIntegerField() num_employees = models.PositiveIntegerField()
num_chairs = models.PositiveIntegerField() num_chairs = models.PositiveIntegerField(default=0)
ceo = models.ForeignKey( ceo = models.ForeignKey(
Employee, Employee,
models.CASCADE, models.CASCADE,
@ -122,3 +122,10 @@ class JSONFieldModel(models.Model):
class Meta: class Meta:
required_db_features = {"supports_json_field"} required_db_features = {"supports_json_field"}
class NamedCategory(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
def __str__(self):
return self.name

View File

@ -6,6 +6,7 @@ from collections import namedtuple
from copy import deepcopy from copy import deepcopy
from decimal import Decimal from decimal import Decimal
from unittest import mock from unittest import mock
from .models import NamedCategory
from django.core.exceptions import FieldError from django.core.exceptions import FieldError
from django.db import DatabaseError, NotSupportedError, connection from django.db import DatabaseError, NotSupportedError, connection
@ -2849,3 +2850,57 @@ class OrderByTests(SimpleTestCase):
F("field").asc(nulls_first=False) F("field").asc(nulls_first=False)
with self.assertRaisesMessage(ValueError, msg): with self.assertRaisesMessage(ValueError, msg):
F("field").desc(nulls_last=False) 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)
class SubqueryTests(TestCase):
def test_outer_ref_order_by(self):
NamedCategory.objects.create(id=1, name="first")
NamedCategory.objects.create(id=4, name="fourth")
NamedCategory.objects.create(id=2, name="second")
NamedCategory.objects.create(id=3, name="third")
outer_query = NamedCategory.objects.all()
subquery = (
NamedCategory.objects.filter(id=OuterRef("id"))
.order_by("name")
.values("name")
)
values = outer_query.annotate(sorted_name=Subquery(subquery)).order_by(
"sorted_name"
)
sorted_names = list(values.values_list("sorted_name", flat=True))
self.assertListEqual(sorted_names, ["first", "fourth", "second", "third"])