Skip to content

Commit 2798c93

Browse files
jerivasfelixxm
authored andcommitted
Fixed #29538 -- Fixed crash of ordering by related fields when Meta.ordering contains expressions.
Thanks Simon Charette for the review.
1 parent 34e2148 commit 2798c93

File tree

4 files changed

+74
-3
lines changed

4 files changed

+74
-3
lines changed

django/db/models/expressions.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,18 @@ def replace_references(self, references_map):
402402
def copy(self):
403403
return copy.copy(self)
404404

405+
def prefix_references(self, prefix):
406+
clone = self.copy()
407+
clone.set_source_expressions(
408+
[
409+
F(f"{prefix}{expr.name}")
410+
if isinstance(expr, F)
411+
else expr.prefix_references(prefix)
412+
for expr in self.get_source_expressions()
413+
]
414+
)
415+
return clone
416+
405417
def get_group_by_cols(self, alias=None):
406418
if not self.contains_aggregate:
407419
return [self]

django/db/models/sql/compiler.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -912,10 +912,15 @@ def find_ordering_name(
912912
):
913913
item = item.desc() if descending else item.asc()
914914
if isinstance(item, OrderBy):
915-
results.append((item, False))
915+
results.append(
916+
(item.prefix_references(f"{name}{LOOKUP_SEP}"), False)
917+
)
916918
continue
917919
results.extend(
918-
self.find_ordering_name(item, opts, alias, order, already_seen)
920+
(expr.prefix_references(f"{name}{LOOKUP_SEP}"), is_ref)
921+
for expr, is_ref in self.find_ordering_name(
922+
item, opts, alias, order, already_seen
923+
)
919924
)
920925
return results
921926
targets, alias, _ = self.query.trim_joins(targets, joins, path)

tests/ordering/models.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,21 @@ class Reference(models.Model):
6262

6363
class Meta:
6464
ordering = ("article",)
65+
66+
67+
class OrderedByExpression(models.Model):
68+
name = models.CharField(max_length=30)
69+
70+
class Meta:
71+
ordering = [models.functions.Lower("name")]
72+
73+
74+
class OrderedByExpressionChild(models.Model):
75+
parent = models.ForeignKey(OrderedByExpression, models.CASCADE)
76+
77+
class Meta:
78+
ordering = ["parent"]
79+
80+
81+
class OrderedByExpressionGrandChild(models.Model):
82+
parent = models.ForeignKey(OrderedByExpressionChild, models.CASCADE)

tests/ordering/tests.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,16 @@
1414
from django.db.models.functions import Upper
1515
from django.test import TestCase
1616

17-
from .models import Article, Author, ChildArticle, OrderedByFArticle, Reference
17+
from .models import (
18+
Article,
19+
Author,
20+
ChildArticle,
21+
OrderedByExpression,
22+
OrderedByExpressionChild,
23+
OrderedByExpressionGrandChild,
24+
OrderedByFArticle,
25+
Reference,
26+
)
1827

1928

2029
class OrderingTests(TestCase):
@@ -550,3 +559,30 @@ def test_default_ordering_does_not_affect_group_by(self):
550559
{"author": self.author_2.pk, "count": 1},
551560
],
552561
)
562+
563+
def test_order_by_parent_fk_with_expression_in_default_ordering(self):
564+
p3 = OrderedByExpression.objects.create(name="oBJ 3")
565+
p2 = OrderedByExpression.objects.create(name="OBJ 2")
566+
p1 = OrderedByExpression.objects.create(name="obj 1")
567+
c3 = OrderedByExpressionChild.objects.create(parent=p3)
568+
c2 = OrderedByExpressionChild.objects.create(parent=p2)
569+
c1 = OrderedByExpressionChild.objects.create(parent=p1)
570+
self.assertSequenceEqual(
571+
OrderedByExpressionChild.objects.order_by("parent"),
572+
[c1, c2, c3],
573+
)
574+
575+
def test_order_by_grandparent_fk_with_expression_in_default_ordering(self):
576+
p3 = OrderedByExpression.objects.create(name="oBJ 3")
577+
p2 = OrderedByExpression.objects.create(name="OBJ 2")
578+
p1 = OrderedByExpression.objects.create(name="obj 1")
579+
c3 = OrderedByExpressionChild.objects.create(parent=p3)
580+
c2 = OrderedByExpressionChild.objects.create(parent=p2)
581+
c1 = OrderedByExpressionChild.objects.create(parent=p1)
582+
g3 = OrderedByExpressionGrandChild.objects.create(parent=c3)
583+
g2 = OrderedByExpressionGrandChild.objects.create(parent=c2)
584+
g1 = OrderedByExpressionGrandChild.objects.create(parent=c1)
585+
self.assertSequenceEqual(
586+
OrderedByExpressionGrandChild.objects.order_by("parent"),
587+
[g1, g2, g3],
588+
)

0 commit comments

Comments
 (0)