Opened 3 years ago
Closed 3 years ago
#34012 closed Cleanup/optimization (fixed)
QuerySet.order_by() silently skips non-existing fields on related fields with Meta.ordering.
| Reported by: | Klaas van Schelven | Owned by: | David Sanders |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | 4.1 |
| Severity: | Normal | Keywords: | |
| Cc: | David Sanders | Triage Stage: | Ready for checkin |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
Compare the following desirable behavior:
>>> SomeModel.objects.all().order_by("non_existing_field")
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "......lib/python3.10/site-packages/django/db/models/query.py", line 1149, in order_by
obj.query.add_ordering(*field_names)
File "......lib/python3.10/site-packages/django/db/models/sql/query.py", line 2016, in add_ordering
self.names_to_path(item.split(LOOKUP_SEP), self.model._meta)
File "......lib/python3.10/site-packages/django/db/models/sql/query.py", line 1562, in names_to_path
raise FieldError("Cannot resolve keyword '%s' into field. "
django.core.exceptions.FieldError: Cannot resolve keyword 'non_existing_field' into field. Choices are: [redacted]
with the following undesirable behavior:
>>> SomeModel.objects.all().order_by("some_foreign_key__non_existing_field")
<QuerySet .... [ i.e. shows some results ]
Change History (11)
comment:1 by , 3 years ago
| Component: | Uncategorized → Database layer (models, ORM) |
|---|---|
| Summary: | order_by silently skips non-existing fields of existing related fields → QuerySet.order_by() silently skips non-existing fields on related fields. |
| Triage Stage: | Unreviewed → Accepted |
| Type: | Uncategorized → Cleanup/optimization |
comment:2 by , 3 years ago
| Cc: | added |
|---|
fwiw I can't reproduce the issue on 4.1.1 🤔 …
Here's what I tried (full tracebacks omitted for brevity):
class Parent(Model):
...
class Child(Model):
parent = ForeignKey(Parent, on_delete=CASCADE)
Child.objects.all().order_by('asdf')
FieldError: Cannot resolve keyword 'asdf' into field. Choices are: id, parent, parent_id
Child.objects.all().order_by('parent__id')
<QuerySet []>
Child.objects.all().order_by('parent__asdf')
FieldError: Unsupported lookup 'asdf' for BigAutoField or join on the field not permitted, perhaps you meant df?
During handling of the above exception, another exception occurred:
FieldError: Cannot resolve keyword 'asdf' into field. Choices are: child, id
comment:3 by , 3 years ago
Good catch, this only happens when an explicit ordering is defined on the Parent's Meta:
from django.db import models
class Parent(models.Model):
class Meta:
ordering = ["id"]
class Child(models.Model):
parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
Reproducable up to Django 4.1.1
comment:4 by , 3 years ago
felixx this looks like an issue with overriding the foreign model's ordering.
If I repeat the test with Klaas' models and print the query it reveals it still attempts to use parent.id:
class Parent(Model):
class Meta:
ordering = ["id"]
class Child(Model):
parent = ForeignKey(Parent, on_delete=CASCADE)
print(Child.objects.all().order_by('parent__asdf').query)
SELECT "sample_child"."id", "sample_child"."parent_id" FROM "sample_child" INNER JOIN "sample_parent" ON ("sample_child"."parent_id" = "sample_parent"."id") ORDER BY "sample_parent"."id" ASC
Edit: (I guess the question is whether Django should raise an error or continue onto ordering)
comment:5 by , 3 years ago
| Has patch: | set |
|---|---|
| Owner: | changed from to |
| Status: | new → assigned |
comment:6 by , 3 years ago
| Version: | 4.0 → 4.1 |
|---|
comment:7 by , 3 years ago
| Summary: | QuerySet.order_by() silently skips non-existing fields on related fields. → QuerySet.order_by() silently skips non-existing fields on related fields with Meta.ordering. |
|---|
comment:8 by , 3 years ago
| Patch needs improvement: | set |
|---|
comment:10 by , 3 years ago
| Patch needs improvement: | unset |
|---|---|
| Triage Stage: | Accepted → Ready for checkin |
Tenatively accepted, I'm not sure it's worth the additional complexity (similar to the #29408, see 440505cb2cadbe1a5b9fba246bcde6c04f51d07e).