Fix crash when Meta.ordering contains expressions in parent

Skip expressions in Meta.ordering during recursive ordering
resolution to prevent TypeError when ordering by related models.
Regression test added for ordering by ForeignKey to such models.
This commit is contained in:
utkarsh.arya@zomato.com 2025-11-15 23:15:05 +00:00
parent 8dd5877f58
commit e432ec4a3b
3 changed files with 87 additions and 1 deletions

View file

@ -704,6 +704,16 @@ class SQLCompiler:
not be) and column name for ordering by the given 'name' parameter.
The 'name' is of the form 'field1__field2__...__fieldN'.
"""
# If name is an expression (e.g., OrderBy or F().asc() from Meta.ordering),
# we can't process it through find_ordering_name as it's not a simple field name.
# Expressions from Meta.ordering are meant for direct use in the model's own queries,
# not for recursive processing when ordering by a ForeignKey.
# Simply skip them to avoid crashes.
if hasattr(name, 'resolve_expression'):
# Skip expressions entirely - they're not applicable when recursively
# processing related model ordering.
return []
name, order = get_order_dir(name, default_order)
descending = order == 'DESC'
pieces = name.split(LOOKUP_SEP)

View file

@ -59,3 +59,24 @@ class Reference(models.Model):
class Meta:
ordering = ('article',)
class ChildArticle(Article):
"""
Child model that inherits Article's Meta.ordering which contains expressions.
"""
class Meta:
pass
class RelatedToArticleWithExpression(models.Model):
"""
This model has a ForeignKey to Article (which has expressions in Meta.ordering)
and orders by that relation. This tests the bug where expressions in parent
model's Meta.ordering crash when processing the related ordering.
"""
article = models.ForeignKey(Article, models.CASCADE)
name = models.CharField(max_length=100)
class Meta:
ordering = ('article',)

View file

@ -9,7 +9,10 @@ from django.db.models.functions import Upper
from django.test import TestCase
from django.utils.deprecation import RemovedInDjango31Warning
from .models import Article, Author, OrderedByFArticle, Reference
from .models import (
Article, Author, ChildArticle, OrderedByFArticle, Reference,
RelatedToArticleWithExpression,
)
class OrderingTests(TestCase):
@ -471,3 +474,55 @@ class OrderingTests(TestCase):
)
with self.assertRaisesMessage(RemovedInDjango31Warning, msg):
list(Article.objects.values('author').annotate(Count('headline')))
def test_related_ordering_with_expression_in_parent(self):
"""
Ordering by a ForeignKey to a model with Meta.ordering containing
expressions (like OrderBy or F().asc()) should not crash.
Regression test for #XXXXX.
"""
# Create test data
a1 = Article.objects.create(
headline="Test Article 1",
pub_date=datetime(2020, 1, 1),
author=self.author_1
)
a2 = Article.objects.create(
headline="Test Article 2",
pub_date=datetime(2020, 1, 2),
author=self.author_2
)
r1 = RelatedToArticleWithExpression.objects.create(article=a1, name="Related 1")
r2 = RelatedToArticleWithExpression.objects.create(article=a2, name="Related 2")
# This should not crash with: TypeError: 'OrderBy' object is not subscriptable
# The bug occurred because find_ordering_name didn't handle OrderBy expressions
# from Meta.ordering when recursively processing related model ordering
queryset = RelatedToArticleWithExpression.objects.all()
list(queryset) # Force evaluation
# Verify the queryset can be evaluated multiple times
self.assertEqual(queryset.count(), 2)
def test_child_inherits_parent_ordering_with_expressions(self):
"""
A child model that inherits Meta.ordering with expressions from its
parent should handle ordering correctly.
"""
# Create test data
ChildArticle.objects.create(
headline="Child Article 1",
pub_date=datetime(2020, 1, 1),
author=self.author_1
)
ChildArticle.objects.create(
headline="Child Article 2",
pub_date=datetime(2020, 1, 2),
author=self.author_2
)
# This should not crash
queryset = ChildArticle.objects.all()
list(queryset) # Force evaluation
self.assertEqual(queryset.count(), 2)