Opened 21 months ago
Closed 20 months ago
#34481 closed Bug (fixed)
Admin check for reversed foreign key used in "list_display"
Reported by: | Natalia Bidart | Owned by: | Baha Sdtbekov |
---|---|---|---|
Component: | contrib.admin | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Baha Sdtbekov | 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
Currently the admin site checks and reports an admin.E109
system check error when a ManyToManyField
is declared within the list_display
values.
But, when a reversed foreign key is used there is no system error reported:
System check identified no issues (0 silenced). April 10, 2023 - 19:04:29 Django version 5.0.dev20230410064954, using settings 'projectfromrepo.settings' Starting development server at http://0.0.0.0:8000/ Quit the server with CONTROL-C.
and visiting the admin site for the relevant model results in the following error:
TypeError: create_reverse_many_to_one_manager.<locals>.RelatedManager.__call__() missing 1 required keyword-only argument: 'manager' [10/Apr/2023 19:30:40] "GET /admin/testapp/question/ HTTP/1.1" 500 415926
Ideally, using a reversed foreign key would also result in a system check error instead of a 500 response.
To reproduce, follow the Question
and Choice
models from the Django Tutorial and add the following to the QuestionAdmin
:
list_display = ["question_text", "choice_set"]
Then, visit /admin/testapp/question/
and the following traceback is returned:
Internal Server Error: /admin/testapp/question/ Traceback (most recent call last): File "/some/path/django/django/db/models/options.py", line 681, in get_field return self.fields_map[field_name] ~~~~~~~~~~~~~~~^^^^^^^^^^^^ KeyError: 'choice_set' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/some/path/django/django/contrib/admin/utils.py", line 288, in lookup_field f = _get_non_gfk_field(opts, name) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/contrib/admin/utils.py", line 319, in _get_non_gfk_field field = opts.get_field(name) ^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/db/models/options.py", line 683, in get_field raise FieldDoesNotExist( django.core.exceptions.FieldDoesNotExist: Question has no field named 'choice_set' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/some/path/django/django/core/handlers/exception.py", line 55, in inner response = get_response(request) ^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/core/handlers/base.py", line 220, in _get_response response = response.render() ^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/response.py", line 111, in render self.content = self.rendered_content ^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/response.py", line 89, in rendered_content return template.render(context, self._request) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/backends/django.py", line 61, in render return self.template.render(context) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 171, in render return self._render(context) ^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 163, in _render return self.nodelist.render(context) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 1001, in render return SafeString("".join([node.render_annotated(context) for node in self])) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 1001, in <listcomp> return SafeString("".join([node.render_annotated(context) for node in self])) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 962, in render_annotated return self.render(context) ^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/loader_tags.py", line 157, in render return compiled_parent._render(context) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 163, in _render return self.nodelist.render(context) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 1001, in render return SafeString("".join([node.render_annotated(context) for node in self])) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 1001, in <listcomp> return SafeString("".join([node.render_annotated(context) for node in self])) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 962, in render_annotated return self.render(context) ^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/loader_tags.py", line 157, in render return compiled_parent._render(context) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 163, in _render return self.nodelist.render(context) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 1001, in render return SafeString("".join([node.render_annotated(context) for node in self])) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 1001, in <listcomp> return SafeString("".join([node.render_annotated(context) for node in self])) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 962, in render_annotated return self.render(context) ^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/loader_tags.py", line 63, in render result = block.nodelist.render(context) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 1001, in render return SafeString("".join([node.render_annotated(context) for node in self])) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 1001, in <listcomp> return SafeString("".join([node.render_annotated(context) for node in self])) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 962, in render_annotated return self.render(context) ^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/loader_tags.py", line 63, in render result = block.nodelist.render(context) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 1001, in render return SafeString("".join([node.render_annotated(context) for node in self])) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 1001, in <listcomp> return SafeString("".join([node.render_annotated(context) for node in self])) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/base.py", line 962, in render_annotated return self.render(context) ^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/contrib/admin/templatetags/base.py", line 45, in render return super().render(context) ^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/template/library.py", line 258, in render _dict = self.func(*resolved_args, **resolved_kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/contrib/admin/templatetags/admin_list.py", line 340, in result_list "results": list(results(cl)), ^^^^^^^^^^^^^^^^^ File "/some/path/django/django/contrib/admin/templatetags/admin_list.py", line 316, in results yield ResultList(None, items_for_result(cl, res, None)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/contrib/admin/templatetags/admin_list.py", line 307, in __init__ super().__init__(*items) File "/some/path/django/django/contrib/admin/templatetags/admin_list.py", line 217, in items_for_result f, attr, value = lookup_field(field_name, result, cl.model_admin) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/some/path/django/django/contrib/admin/utils.py", line 301, in lookup_field value = attr() ^^^^^^ TypeError: create_reverse_many_to_one_manager.<locals>.RelatedManager.__call__() missing 1 required keyword-only argument: 'manager' [10/Apr/2023 19:30:40] "GET /admin/testapp/question/ HTTP/1.1" 500 415926
Change History (8)
comment:1 by , 21 months ago
Component: | Uncategorized → contrib.admin |
---|---|
Triage Stage: | Unreviewed → Accepted |
Type: | Uncategorized → Bug |
comment:2 by , 21 months ago
Cc: | added |
---|
comment:3 by , 21 months ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:5 by , 20 months ago
Has patch: | set |
---|
comment:6 by , 20 months ago
Needs documentation: | set |
---|---|
Patch needs improvement: | set |
comment:7 by , 20 months ago
Needs documentation: | unset |
---|---|
Patch needs improvement: | unset |
Triage Stage: | Accepted → Ready for checkin |
Thanks for the report.