summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEd Rivas <ed@jerivas.com>2022-05-04 18:10:53 -0600
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-05-12 07:19:16 +0200
commit2798c937deb6625a4e6a36e70d4d60ce5faac954 (patch)
tree0a07339d337eb923642cfd9ea6930c8a6f4e4bab
parent34e2148fc725e7200050f74130d7523e3cd8507a (diff)
Fixed #29538 -- Fixed crash of ordering by related fields when Meta.ordering contains expressions.
Thanks Simon Charette for the review.
-rw-r--r--django/db/models/expressions.py12
-rw-r--r--django/db/models/sql/compiler.py9
-rw-r--r--tests/ordering/models.py18
-rw-r--r--tests/ordering/tests.py38
4 files changed, 74 insertions, 3 deletions
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py
index 4bc55a1c89..148b19b462 100644
--- a/django/db/models/expressions.py
+++ b/django/db/models/expressions.py
@@ -402,6 +402,18 @@ class BaseExpression:
def copy(self):
return copy.copy(self)
+ def prefix_references(self, prefix):
+ clone = self.copy()
+ clone.set_source_expressions(
+ [
+ F(f"{prefix}{expr.name}")
+ if isinstance(expr, F)
+ else expr.prefix_references(prefix)
+ for expr in self.get_source_expressions()
+ ]
+ )
+ return clone
+
def get_group_by_cols(self, alias=None):
if not self.contains_aggregate:
return [self]
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index 13b606255c..9c7bd8ea1a 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -912,10 +912,15 @@ class SQLCompiler:
):
item = item.desc() if descending else item.asc()
if isinstance(item, OrderBy):
- results.append((item, False))
+ results.append(
+ (item.prefix_references(f"{name}{LOOKUP_SEP}"), False)
+ )
continue
results.extend(
- self.find_ordering_name(item, opts, alias, order, already_seen)
+ (expr.prefix_references(f"{name}{LOOKUP_SEP}"), is_ref)
+ for expr, is_ref in self.find_ordering_name(
+ item, opts, alias, order, already_seen
+ )
)
return results
targets, alias, _ = self.query.trim_joins(targets, joins, path)
diff --git a/tests/ordering/models.py b/tests/ordering/models.py
index fce8b9cd42..c365da7642 100644
--- a/tests/ordering/models.py
+++ b/tests/ordering/models.py
@@ -62,3 +62,21 @@ class Reference(models.Model):
class Meta:
ordering = ("article",)
+
+
+class OrderedByExpression(models.Model):
+ name = models.CharField(max_length=30)
+
+ class Meta:
+ ordering = [models.functions.Lower("name")]
+
+
+class OrderedByExpressionChild(models.Model):
+ parent = models.ForeignKey(OrderedByExpression, models.CASCADE)
+
+ class Meta:
+ ordering = ["parent"]
+
+
+class OrderedByExpressionGrandChild(models.Model):
+ parent = models.ForeignKey(OrderedByExpressionChild, models.CASCADE)
diff --git a/tests/ordering/tests.py b/tests/ordering/tests.py
index 37106aa5dd..e4f7d75992 100644
--- a/tests/ordering/tests.py
+++ b/tests/ordering/tests.py
@@ -14,7 +14,16 @@ from django.db.models import (
from django.db.models.functions import Upper
from django.test import TestCase
-from .models import Article, Author, ChildArticle, OrderedByFArticle, Reference
+from .models import (
+ Article,
+ Author,
+ ChildArticle,
+ OrderedByExpression,
+ OrderedByExpressionChild,
+ OrderedByExpressionGrandChild,
+ OrderedByFArticle,
+ Reference,
+)
class OrderingTests(TestCase):
@@ -550,3 +559,30 @@ class OrderingTests(TestCase):
{"author": self.author_2.pk, "count": 1},
],
)
+
+ def test_order_by_parent_fk_with_expression_in_default_ordering(self):
+ p3 = OrderedByExpression.objects.create(name="oBJ 3")
+ p2 = OrderedByExpression.objects.create(name="OBJ 2")
+ p1 = OrderedByExpression.objects.create(name="obj 1")
+ c3 = OrderedByExpressionChild.objects.create(parent=p3)
+ c2 = OrderedByExpressionChild.objects.create(parent=p2)
+ c1 = OrderedByExpressionChild.objects.create(parent=p1)
+ self.assertSequenceEqual(
+ OrderedByExpressionChild.objects.order_by("parent"),
+ [c1, c2, c3],
+ )
+
+ def test_order_by_grandparent_fk_with_expression_in_default_ordering(self):
+ p3 = OrderedByExpression.objects.create(name="oBJ 3")
+ p2 = OrderedByExpression.objects.create(name="OBJ 2")
+ p1 = OrderedByExpression.objects.create(name="obj 1")
+ c3 = OrderedByExpressionChild.objects.create(parent=p3)
+ c2 = OrderedByExpressionChild.objects.create(parent=p2)
+ c1 = OrderedByExpressionChild.objects.create(parent=p1)
+ g3 = OrderedByExpressionGrandChild.objects.create(parent=c3)
+ g2 = OrderedByExpressionGrandChild.objects.create(parent=c2)
+ g1 = OrderedByExpressionGrandChild.objects.create(parent=c1)
+ self.assertSequenceEqual(
+ OrderedByExpressionGrandChild.objects.order_by("parent"),
+ [g1, g2, g3],
+ )