Django 自定义排序会导致相关模型出错?
Django custom ordering causes error for related model?
我有两个模型如下:
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):
a = models.ForeignKey(A, on_delete=models.CASCADE)
class Meta:
ordering = [
"a"
]
然后当我尝试通过管理站点删除 A
个实例时,或者当通过管理站点或命令行访问 B
个实例时,它给出错误:Cannot resolve keyword into field
finished_at
.
但是,如果我删除模型 A
上的顺序,它就可以正常工作。
我可以缩小问题范围,从 B
排序中删除“a”或不使用 A
排序中的 models.F
表达式将解决问题.
完整堆栈跟踪:
Django Version: 3.2.11
Python Version: 3.8.12
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
'my-apps',
]
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback (most recent call last):
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/contrib/admin/options.py", line 616, in wrapper
return self.admin_site.admin_view(view)(*args, **kwargs)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/contrib/admin/sites.py", line 232, in inner
return view(request, *args, **kwargs)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/utils/decorators.py", line 43, in _wrapper
return bound_method(*args, **kwargs)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1815, in changelist_view
'selection_note': _('0 of %(cnt)s selected') % {'cnt': len(cl.result_list)},
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/query.py", line 262, in __len__
self._fetch_all()
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/query.py", line 1324, in _fetch_all
self._result_cache = list(self._iterable_class(self))
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/query.py", line 51, in __iter__
results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1162, in execute_sql
sql, params = self.as_sql()
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 513, in as_sql
extra_select, order_by, group_by = self.pre_sql_setup()
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 56, in pre_sql_setup
order_by = self.get_order_by()
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 372, in get_order_by
resolved = expr.resolve_expression(self.query, allow_joins=True, reuse=None)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/expressions.py", line 247, in resolve_expression
c.set_source_expressions([
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/expressions.py", line 248, in <listcomp>
expr.resolve_expression(query, allow_joins, reuse, summarize)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/expressions.py", line 578, in resolve_expression
return query.resolve_ref(self.name, allow_joins, reuse, summarize)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1756, in resolve_ref
join_info = self.setup_joins(field_list, self.get_meta(), self.get_initial_alias(), can_reuse=reuse)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1625, in setup_joins
path, final_field, targets, rest = self.names_to_path(
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1539, in names_to_path
raise FieldError("Cannot resolve keyword '%s' into field. "
Exception Type: FieldError at /admin/some/path/
Exception Value: Cannot resolve keyword 'finished_at' into field. Choices are
不是输入相关模型的名称,而是必须解决相关模型的id。
class B(models.Model):
a = models.ForeignKey(A, on_delete=models.CASCADE)
class Meta:
ordering = [
"a_id"
]
这是一个已知错误:#29538 (Query Expression in ordering of a related object fails) – Django
发生这种情况是因为:
B
在 A
上有 on_delete=models.CASCADE
。
B
在 A
上有 ordering
。
A
在 F
对象上有 ordering
。
SQLCompiler
find_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
在另一个模型 C
上有 ordering
,ordering
在 F
对象。
需要在 SQLCompiler
find_ordering_name
.
中完成正确的修复(确定完整路径)
我有两个模型如下:
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):
a = models.ForeignKey(A, on_delete=models.CASCADE)
class Meta:
ordering = [
"a"
]
然后当我尝试通过管理站点删除 A
个实例时,或者当通过管理站点或命令行访问 B
个实例时,它给出错误:Cannot resolve keyword into field
finished_at
.
但是,如果我删除模型 A
上的顺序,它就可以正常工作。
我可以缩小问题范围,从 B
排序中删除“a”或不使用 A
排序中的 models.F
表达式将解决问题.
完整堆栈跟踪:
Django Version: 3.2.11
Python Version: 3.8.12
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
'my-apps',
]
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback (most recent call last):
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/contrib/admin/options.py", line 616, in wrapper
return self.admin_site.admin_view(view)(*args, **kwargs)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/contrib/admin/sites.py", line 232, in inner
return view(request, *args, **kwargs)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/utils/decorators.py", line 43, in _wrapper
return bound_method(*args, **kwargs)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1815, in changelist_view
'selection_note': _('0 of %(cnt)s selected') % {'cnt': len(cl.result_list)},
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/query.py", line 262, in __len__
self._fetch_all()
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/query.py", line 1324, in _fetch_all
self._result_cache = list(self._iterable_class(self))
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/query.py", line 51, in __iter__
results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1162, in execute_sql
sql, params = self.as_sql()
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 513, in as_sql
extra_select, order_by, group_by = self.pre_sql_setup()
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 56, in pre_sql_setup
order_by = self.get_order_by()
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 372, in get_order_by
resolved = expr.resolve_expression(self.query, allow_joins=True, reuse=None)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/expressions.py", line 247, in resolve_expression
c.set_source_expressions([
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/expressions.py", line 248, in <listcomp>
expr.resolve_expression(query, allow_joins, reuse, summarize)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/expressions.py", line 578, in resolve_expression
return query.resolve_ref(self.name, allow_joins, reuse, summarize)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1756, in resolve_ref
join_info = self.setup_joins(field_list, self.get_meta(), self.get_initial_alias(), can_reuse=reuse)
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1625, in setup_joins
path, final_field, targets, rest = self.names_to_path(
File "BASE_DIR/.venv/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1539, in names_to_path
raise FieldError("Cannot resolve keyword '%s' into field. "
Exception Type: FieldError at /admin/some/path/
Exception Value: Cannot resolve keyword 'finished_at' into field. Choices are
不是输入相关模型的名称,而是必须解决相关模型的id。
class B(models.Model):
a = models.ForeignKey(A, on_delete=models.CASCADE)
class Meta:
ordering = [
"a_id"
]
这是一个已知错误:#29538 (Query Expression in ordering of a related object fails) – Django
发生这种情况是因为:
B
在A
上有on_delete=models.CASCADE
。B
在A
上有ordering
。A
在F
对象上有ordering
。SQLCompiler
find_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
在另一个模型 C
上有 ordering
,ordering
在 F
对象。
需要在 SQLCompiler
find_ordering_name
.