Fixed #34012 -- Made QuerySet.order_by() apply transforms on related fields for models with Meta.ordering.
This makes QuerySet.order_by() no longer ignore trailing transforms for models with Meta.ordering. As a consequence, FieldError is raised in such cases for non-existent fields. Thanks to Klaas van Schelven for the report and Mariusz Felisiak for the review and advice.
This commit is contained in:
parent
649b28eab6
commit
4771a1694b
|
@ -1001,12 +1001,14 @@ class SQLCompiler:
|
||||||
|
|
||||||
# If we get to this point and the field is a relation to another model,
|
# If we get to this point and the field is a relation to another model,
|
||||||
# append the default ordering for that model unless it is the pk
|
# append the default ordering for that model unless it is the pk
|
||||||
# shortcut or the attribute name of the field that is specified.
|
# shortcut or the attribute name of the field that is specified or
|
||||||
|
# there are transforms to process.
|
||||||
if (
|
if (
|
||||||
field.is_relation
|
field.is_relation
|
||||||
and opts.ordering
|
and opts.ordering
|
||||||
and getattr(field, "attname", None) != pieces[-1]
|
and getattr(field, "attname", None) != pieces[-1]
|
||||||
and name != "pk"
|
and name != "pk"
|
||||||
|
and not getattr(transform_function, "has_transforms", False)
|
||||||
):
|
):
|
||||||
# Firstly, avoid infinite loops.
|
# Firstly, avoid infinite loops.
|
||||||
already_seen = already_seen or set()
|
already_seen = already_seen or set()
|
||||||
|
|
|
@ -1819,6 +1819,7 @@ class Query(BaseExpression):
|
||||||
final_transformer = functools.partial(
|
final_transformer = functools.partial(
|
||||||
transform, name=name, previous=final_transformer
|
transform, name=name, previous=final_transformer
|
||||||
)
|
)
|
||||||
|
final_transformer.has_transforms = True
|
||||||
# Then, add the path to the query's joins. Note that we can't trim
|
# Then, add the path to the query's joins. Note that we can't trim
|
||||||
# joins at this stage - we will need the information about join type
|
# joins at this stage - we will need the information about join type
|
||||||
# of the trimmed joins.
|
# of the trimmed joins.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
|
||||||
|
from django.core.exceptions import FieldError
|
||||||
from django.db.models import (
|
from django.db.models import (
|
||||||
CharField,
|
CharField,
|
||||||
Count,
|
Count,
|
||||||
|
@ -91,6 +92,18 @@ class OrderingTests(TestCase):
|
||||||
attrgetter("headline"),
|
attrgetter("headline"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_default_ordering_override_unknown_field(self):
|
||||||
|
"""
|
||||||
|
Attempts to override default ordering on related models with an unknown
|
||||||
|
field should result in an error.
|
||||||
|
"""
|
||||||
|
msg = (
|
||||||
|
"Cannot resolve keyword 'unknown_field' into field. Choices are: "
|
||||||
|
"article, author, editor, editor_id, id, name"
|
||||||
|
)
|
||||||
|
with self.assertRaisesMessage(FieldError, msg):
|
||||||
|
list(Article.objects.order_by("author__unknown_field"))
|
||||||
|
|
||||||
def test_order_by_override(self):
|
def test_order_by_override(self):
|
||||||
"""
|
"""
|
||||||
Only the last order_by has any effect (since they each override any
|
Only the last order_by has any effect (since they each override any
|
||||||
|
|
|
@ -68,6 +68,9 @@ class Annotation(models.Model):
|
||||||
class DateTimePK(models.Model):
|
class DateTimePK(models.Model):
|
||||||
date = models.DateTimeField(primary_key=True, default=datetime.datetime.now)
|
date = models.DateTimeField(primary_key=True, default=datetime.datetime.now)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ["date"]
|
||||||
|
|
||||||
|
|
||||||
class ExtraInfo(models.Model):
|
class ExtraInfo(models.Model):
|
||||||
info = models.CharField(max_length=100)
|
info = models.CharField(max_length=100)
|
||||||
|
|
Loading…
Reference in New Issue