Hello everyone,
I have noticed an issue with how Django validates ordering in the admin.
The Problem
If we use a related field in ordering, like →
ordering = ["author__name"]
Django does not validate it during manage.py check.
Even if we make a mistake like:
ordering = ["author__nam"] # typo
check still shows no errors.
But when we open the admin page, it crashes at runtime.
Where this happens
This happens in Django admin system checks.
There is a condition where fields containing __ are skipped:
elif LOOKUP_SEP in field_name:
return []
So related fields are not validated at all.
Why this is a problem
-
Developers get no warning during development
-
Errors only appear at runtime
-
This creates confusion because check says everything is fine
Possible solution
Instead of skipping validation completely, we can do a simple check:
This would catch common mistakes like typos without needing full ORM validation.
Questions
-
Is this behavior intentional due to complexity?
-
Would a basic validation like this be acceptable?
-
Is there a preferred way to approach this kind of improvement?
Happy to work on this if it makes sense.
Thanks!
1 Like
When looking at an issue like this, I always like to try to dig into the history to see if there are some breadcrumbs explaining why things are the way they are.
First, I noticed you didn’t include the full code you’re referencing.
The full block (django/django/contrib/admin/checks.py at 9b2cc7a950b14cdde4c85d8ed0ebc59784a68725 · django/django · GitHub)
elif LOOKUP_SEP in field_name:
# Skip ordering in the format field1__field2 (FIXME: checking
# this format would be nice, but it's a little fiddly).
return []
else:
That comment seems very “telling” to me -
- Yes, it’s intentional
- A true fix is likely to have edge cases making this more difficult than it may appear.
Looking at the blame for this file shows that the comment was added 12 years ago when the checks were added.
However, the origin of this phrasing for this comment goes back to at least April 2008 (Merged the queryset-refactor branch into trunk. · django/django@9c52d56 · GitHub).
At this point the rabbit hole goes deeper than I care to dig - but, it leads me to believe that the comment itself could possibly be obsolete due to other work having been done since then.
You might want to create a PoC and run it through the tests just to see if anything obvious pops up - and work forward from there.
I have no idea where the problems would show up, but I’m guessing they might be in the areas of many-to-many fields and reverse references to Foreign Key fields (including OneToOne fields).
2 Likes
After a __ there can be any of: a related field, a lookup, or a transform. They can be chained repeatedly, too, like book__author__name__lower. To sensibly validate the behaviour here, we’d need the ORM to have an API to check if a given field_name is valid or not, which I don’t believe it has—instead some internals work out the details at query time. So that would be the first step, hence it’s “a little fiddly” 
Thanks for doing the historical research there, @KenWhitesell !
Thanks @adamchainz and @KenWhitesell that makes the complexity much clearer.
I went ahead and built a PoC using get_fields_from_path and catching FieldDoesNotExist for typos and NotRelationField for transforms. The full Django test suite passes 19,049 tests, zero failures.
But based on your explanation, I understand this approach may not handle deeper chains like book__author__name__lower correctly since get_fields_from_path stops at the first non-relation field.
Two questions:
-
Would a partial fix catching obvious typos in the first relation hop still be useful even if it doesn’t handle every case?
-
Is there a deeper ORM API you would recommend looking at for full validation, or is this genuinely something that needs new infrastructure?
We don’t have a standalone utility for that as it’s hard to generalize get_lookup and get_transform calls for most cases but I suggest you look at how django.db.models.base.Model._check_ordering is implemented particularly this part.
You’ll likely have to make sure to strip leading - as well.
Cheers.