Fixed #29538 -- Fixed crash of ordering by related fields when Meta.ordering contains expressions.
Thanks Simon Charette for the review.
This commit is contained in:
parent
34e2148fc7
commit
2798c937de
|
@ -402,6 +402,18 @@ class BaseExpression:
|
||||||
def copy(self):
|
def copy(self):
|
||||||
return copy.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):
|
def get_group_by_cols(self, alias=None):
|
||||||
if not self.contains_aggregate:
|
if not self.contains_aggregate:
|
||||||
return [self]
|
return [self]
|
||||||
|
|
|
@ -912,10 +912,15 @@ class SQLCompiler:
|
||||||
):
|
):
|
||||||
item = item.desc() if descending else item.asc()
|
item = item.desc() if descending else item.asc()
|
||||||
if isinstance(item, OrderBy):
|
if isinstance(item, OrderBy):
|
||||||
results.append((item, False))
|
results.append(
|
||||||
|
(item.prefix_references(f"{name}{LOOKUP_SEP}"), False)
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
results.extend(
|
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
|
return results
|
||||||
targets, alias, _ = self.query.trim_joins(targets, joins, path)
|
targets, alias, _ = self.query.trim_joins(targets, joins, path)
|
||||||
|
|
|
@ -62,3 +62,21 @@ class Reference(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ("article",)
|
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)
|
||||||
|
|
|
@ -14,7 +14,16 @@ from django.db.models import (
|
||||||
from django.db.models.functions import Upper
|
from django.db.models.functions import Upper
|
||||||
from django.test import TestCase
|
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):
|
class OrderingTests(TestCase):
|
||||||
|
@ -550,3 +559,30 @@ class OrderingTests(TestCase):
|
||||||
{"author": self.author_2.pk, "count": 1},
|
{"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],
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue