Opened 3 years ago
Closed 3 years 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 , 3 years ago
| Component: | Uncategorized → contrib.admin |
|---|---|
| Triage Stage: | Unreviewed → Accepted |
| Type: | Uncategorized → Bug |
comment:2 by , 3 years ago
| Cc: | added |
|---|
comment:3 by , 3 years ago
| Owner: | changed from to |
|---|---|
| Status: | new → assigned |
comment:5 by , 3 years ago
| Has patch: | set |
|---|
comment:6 by , 3 years ago
| Needs documentation: | set |
|---|---|
| Patch needs improvement: | set |
comment:7 by , 3 years ago
| Needs documentation: | unset |
|---|---|
| Patch needs improvement: | unset |
| Triage Stage: | Accepted → Ready for checkin |
Thanks for the report.