Пользовательское упорядочивание Django вызывает ошибку для связанной модели?
У меня есть две следующие модели:
class A(models.Model):
# more fields here
finished_at = models.DateTimeField(db_index=True, null=True, blank=True)
class Meta:
ordering = [
models.F("finished_at").desc(nulls_first=True),
"other_field",
]
class B(models.Model):
related = models.ForeignKey(A, on_delete=models.CASCADE)
class Meta:
ordering = [
"a"
]
Затем, когда я пытаюсь удалить A экземпляры через админку сайта, или при доступе к B экземплярам либо через админку, либо через командную строку, он выдает ошибку: Cannot resolve keyword into field finished_at.
Но если я удалю упорядочивание на модели A, она работает нормально.
Я смог сузить проблему, удалив либо "a" из B упорядочивания, либо не используя models.F выражение из A упорядочивания решит проблему.
Полная трассировка стека:
Вместо того, чтобы поместить имя связанной модели, вы должны обратиться к id связанной модели.
class B(models.Model):
a = models.ForeignKey(A, on_delete=models.CASCADE)
class Meta:
ordering = [
"a_id"
]
Это известная ошибка: #29538 (Query Expression в упорядочивании связанного объекта не работает) - Django
Это происходит потому, что:
Bимеетon_delete=models.CASCADEнаA.BимеетorderingнаA.AимеетorderingнаFобъект.SQLCompilerfind_ordering_nameне преобразует должным образомFобъекты отношений.
Существует устаревший PR (закрыт из-за неактивности), который пытался исправить это: django/django#10146
Вот патч, который работает для простых случаев, таких как в вопросе и сообщении об ошибке:
from django.db.models.constants import LOOKUP_SEP
from django.db.models.expressions import F
from django.db.models.sql.datastructures import Join
from django.db.models.sql.query import get_field_names_from_opts
def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
name = self.name
if self.name not in get_field_names_from_opts(query.get_meta()):
for datastructure in query.alias_map.values():
if (
isinstance(datastructure, Join) and
datastructure.join_field.opts == query.get_meta() and
self.name in get_field_names_from_opts(datastructure.join_field.related_model._meta)
):
name = datastructure.join_field.name + LOOKUP_SEP + self.name
break
return query.resolve_ref(name, allow_joins, reuse, summarize)
F.resolve_expression = resolve_expression
Пример случая, когда это не работает: В дополнение к вышесказанному, A имеет ordering на другой модели C, которая имеет ordering на объекте F.
Правильное исправление (определение полного пути) должно быть сделано в SQLCompiler find_ordering_name.