#29637: KeyError on empty_form.fields in InlineAdminFormSet when user don't have
'add' permission for the child model
-------------------------------------+-------------------------------------
Reporter: Clément Mangin | Owner: nobody
Type: Bug | Status: new
Component: contrib.admin | Version: 2.1
Severity: Normal | Resolution:
Keywords: admin, permission, | Triage Stage:
inline | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by Clément Mangin:
Old description:
> The following use case works with Django 2.0 but breaks with Django 2.1:
> * A Parent model and a Child model with a ForeignKey to the Parent model,
> * A Parent admin form with an inline formset for adding and editing
> Children,
> * A user with change permission for the Parent and Child models, but no
> add permission for the Child model, trying to edit a Parent object and
> its children.
>
> The following error occurs:
> {{{
> KeyError at /admin/demo/parent/1/change/
> 'name'
> Request Method: GET
> Request URL: /admin/demo/parent/1/change/
> Django Version: 2.1
> Exception Type: KeyError
> Exception Value:
> 'name'
> Exception Location: .../python3.7/site-
> packages/django/contrib/admin/helpers.py in fields, line 287
> Python Version: 3.7.0
>
> Error during template rendering
> In template .../python3.7/site-
> packages/django/contrib/admin/templates/admin/edit_inline/tabular.html,
> error at line 13
>
> name
> 3 data-inline-type="tabular"
> 4 data-inline-formset="{{
> inline_admin_formset.inline_formset_data }}">
> 5 <div class="tabular inline-related {% if forloop.last %}last-
> related{% endif %}">
> 6 {{ inline_admin_formset.formset.management_form }}
> 7 <fieldset class="module {{ inline_admin_formset.classes }}">
> 8 <h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst
> }}</h2>
> 9 {{ inline_admin_formset.formset.non_form_errors }}
> 10 <table>
> 11 <thead><tr>
> 12 <th class="original"></th>
> 13 {% for field in inline_admin_formset.fields %}
> 14 {% if not field.widget.is_hidden %}
> 15 <th{% if field.required %} class="required"{% endif
> %}>{{ field.label|capfirst }}
> 16 {% if field.help_text %} <img src="{% static
> "admin/img/icon-unknown.svg" %}" class="help help-tooltip" width="10"
> height="10" alt="({{ field.help_text|striptags }})" title="{{
> field.help_text|striptags }}">{% endif %}
> 17 </th>
> 18 {% endif %}
> 19 {% endfor %}
> 20 {% if inline_admin_formset.formset.can_delete %}<th>{% trans
> "Delete?" %}</th>{% endif %}
> 21 </tr></thead>
> 22
> 23 <tbody>
> }}}
>
> Trace:
> {{{
> Internal Server Error: /admin/demo/parent/1/change/
> Traceback (most recent call last):
> File ".../python3.7/site-packages/django/core/handlers/exception.py",
> line 34, in inner
> response = get_response(request)
> File ".../python3.7/site-packages/django/core/handlers/base.py", line
> 156, in _get_response
> response = self.process_exception_by_middleware(e, request)
> File ".../python3.7/site-packages/django/core/handlers/base.py", line
> 154, in _get_response
> response = response.render()
> File ".../python3.7/site-packages/django/template/response.py", line
> 106, in render
> self.content = self.rendered_content
> File ".../python3.7/site-packages/django/template/response.py", line
> 83, in rendered_content
> content = template.render(context, self._request)
> File ".../python3.7/site-packages/django/template/backends/django.py",
> line 61, in render
> return self.template.render(context)
> File ".../python3.7/site-packages/django/template/base.py", line 171,
> in render
> return self._render(context)
> File ".../python3.7/site-packages/django/template/base.py", line 163,
> in _render
> return self.nodelist.render(context)
> File ".../python3.7/site-packages/django/template/base.py", line 937,
> in render
> bit = node.render_annotated(context)
> File ".../python3.7/site-packages/django/template/base.py", line 904,
> in render_annotated
> return self.render(context)
> File ".../python3.7/site-packages/django/template/loader_tags.py", line
> 150, in render
> return compiled_parent._render(context)
> File ".../python3.7/site-packages/django/template/base.py", line 163,
> in _render
> return self.nodelist.render(context)
> File ".../python3.7/site-packages/django/template/base.py", line 937,
> in render
> bit = node.render_annotated(context)
> File ".../python3.7/site-packages/django/template/base.py", line 904,
> in render_annotated
> return self.render(context)
> File ".../python3.7/site-packages/django/template/loader_tags.py", line
> 150, in render
> return compiled_parent._render(context)
> File ".../python3.7/site-packages/django/template/base.py", line 163,
> in _render
> return self.nodelist.render(context)
> File ".../python3.7/site-packages/django/template/base.py", line 937,
> in render
> bit = node.render_annotated(context)
> File ".../python3.7/site-packages/django/template/base.py", line 904,
> in render_annotated
> return self.render(context)
> File ".../python3.7/site-packages/django/template/loader_tags.py", line
> 62, in render
> result = block.nodelist.render(context)
> File ".../python3.7/site-packages/django/template/base.py", line 937,
> in render
> bit = node.render_annotated(context)
> File ".../python3.7/site-packages/django/template/base.py", line 904,
> in render_annotated
> return self.render(context)
> File ".../python3.7/site-packages/django/template/loader_tags.py", line
> 62, in render
> result = block.nodelist.render(context)
> File ".../python3.7/site-packages/django/template/base.py", line 937,
> in render
> bit = node.render_annotated(context)
> File ".../python3.7/site-packages/django/template/base.py", line 904,
> in render_annotated
> return self.render(context)
> File ".../python3.7/site-packages/django/template/defaulttags.py", line
> 209, in render
> nodelist.append(node.render_annotated(context))
> File ".../python3.7/site-packages/django/template/base.py", line 904,
> in render_annotated
> return self.render(context)
> File ".../python3.7/site-packages/django/template/loader_tags.py", line
> 188, in render
> return template.render(context)
> File ".../python3.7/site-packages/django/template/base.py", line 173,
> in render
> return self._render(context)
> File ".../python3.7/site-packages/django/template/base.py", line 163,
> in _render
> return self.nodelist.render(context)
> File ".../python3.7/site-packages/django/template/base.py", line 937,
> in render
> bit = node.render_annotated(context)
> File ".../python3.7/site-packages/django/template/base.py", line 904,
> in render_annotated
> return self.render(context)
> File ".../python3.7/site-packages/django/template/defaulttags.py", line
> 165, in render
> values = list(values)
> File ".../python3.7/site-packages/django/contrib/admin/helpers.py",
> line 287, in fields
> form_field = empty_form.fields[field_name]
> KeyError: 'name'
> }}}
>
> GitHub project to see the difference in behaviour between Django 2.0 and
> Django 2.1 on this use case, using `tox`:
> https://github.com/clementmangin/django-21-error-demo
New description:
The following use case works with Django 2.0 but breaks with Django 2.1:
* A Parent model and a Child model with a ForeignKey to the Parent model,
* A Parent admin form with an inline formset for adding and editing
Children,
* A user with change permission for the Parent and Child models, but no
add permission for the Child model, trying to edit a Parent object and its
children.
The following error occurs:
{{{
KeyError at /admin/demo/parent/1/change/
'name'
Request Method: GET
Request URL: /admin/demo/parent/1/change/
Django Version: 2.1
Exception Type: KeyError
Exception Value:
'name'
Exception Location: .../python3.7/site-
packages/django/contrib/admin/helpers.py in fields, line 287
Python Version: 3.7.0
Error during template rendering
In template .../python3.7/site-
packages/django/contrib/admin/templates/admin/edit_inline/tabular.html,
error at line 13
name
3 data-inline-type="tabular"
4 data-inline-formset="{{
inline_admin_formset.inline_formset_data }}">
5 <div class="tabular inline-related {% if forloop.last %}last-
related{% endif %}">
6 {{ inline_admin_formset.formset.management_form }}
7 <fieldset class="module {{ inline_admin_formset.classes }}">
8 <h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst
}}</h2>
9 {{ inline_admin_formset.formset.non_form_errors }}
10 <table>
11 <thead><tr>
12 <th class="original"></th>
13 {% for field in inline_admin_formset.fields %}
14 {% if not field.widget.is_hidden %}
15 <th{% if field.required %} class="required"{% endif %}>{{
field.label|capfirst }}
16 {% if field.help_text %} <img src="{% static
"admin/img/icon-unknown.svg" %}" class="help help-tooltip" width="10"
height="10" alt="({{ field.help_text|striptags }})" title="{{
field.help_text|striptags }}">{% endif %}
17 </th>
18 {% endif %}
19 {% endfor %}
20 {% if inline_admin_formset.formset.can_delete %}<th>{% trans
"Delete?" %}</th>{% endif %}
21 </tr></thead>
22
23 <tbody>
}}}
Trace:
{{{
Internal Server Error: /admin/demo/parent/1/change/
Traceback (most recent call last):
File ".../python3.7/site-packages/django/core/handlers/exception.py",
line 34, in inner
response = get_response(request)
File ".../python3.7/site-packages/django/core/handlers/base.py", line
156, in _get_response
response = self.process_exception_by_middleware(e, request)
File ".../python3.7/site-packages/django/core/handlers/base.py", line
154, in _get_response
response = response.render()
File ".../python3.7/site-packages/django/template/response.py", line
106, in render
self.content = self.rendered_content
File ".../python3.7/site-packages/django/template/response.py", line 83,
in rendered_content
content = template.render(context, self._request)
File ".../python3.7/site-packages/django/template/backends/django.py",
line 61, in render
return self.template.render(context)
File ".../python3.7/site-packages/django/template/base.py", line 171, in
render
return self._render(context)
File ".../python3.7/site-packages/django/template/base.py", line 163, in
_render
return self.nodelist.render(context)
File ".../python3.7/site-packages/django/template/base.py", line 937, in
render
bit = node.render_annotated(context)
File ".../python3.7/site-packages/django/template/base.py", line 904, in
render_annotated
return self.render(context)
File ".../python3.7/site-packages/django/template/loader_tags.py", line
150, in render
return compiled_parent._render(context)
File ".../python3.7/site-packages/django/template/base.py", line 163, in
_render
return self.nodelist.render(context)
File ".../python3.7/site-packages/django/template/base.py", line 937, in
render
bit = node.render_annotated(context)
File ".../python3.7/site-packages/django/template/base.py", line 904, in
render_annotated
return self.render(context)
File ".../python3.7/site-packages/django/template/loader_tags.py", line
150, in render
return compiled_parent._render(context)
File ".../python3.7/site-packages/django/template/base.py", line 163, in
_render
return self.nodelist.render(context)
File ".../python3.7/site-packages/django/template/base.py", line 937, in
render
bit = node.render_annotated(context)
File ".../python3.7/site-packages/django/template/base.py", line 904, in
render_annotated
return self.render(context)
File ".../python3.7/site-packages/django/template/loader_tags.py", line
62, in render
result = block.nodelist.render(context)
File ".../python3.7/site-packages/django/template/base.py", line 937, in
render
bit = node.render_annotated(context)
File ".../python3.7/site-packages/django/template/base.py", line 904, in
render_annotated
return self.render(context)
File ".../python3.7/site-packages/django/template/loader_tags.py", line
62, in render
result = block.nodelist.render(context)
File ".../python3.7/site-packages/django/template/base.py", line 937, in
render
bit = node.render_annotated(context)
File ".../python3.7/site-packages/django/template/base.py", line 904, in
render_annotated
return self.render(context)
File ".../python3.7/site-packages/django/template/defaulttags.py", line
209, in render
nodelist.append(node.render_annotated(context))
File ".../python3.7/site-packages/django/template/base.py", line 904, in
render_annotated
return self.render(context)
File ".../python3.7/site-packages/django/template/loader_tags.py", line
188, in render
return template.render(context)
File ".../python3.7/site-packages/django/template/base.py", line 173, in
render
return self._render(context)
File ".../python3.7/site-packages/django/template/base.py", line 163, in
_render
return self.nodelist.render(context)
File ".../python3.7/site-packages/django/template/base.py", line 937, in
render
bit = node.render_annotated(context)
File ".../python3.7/site-packages/django/template/base.py", line 904, in
render_annotated
return self.render(context)
File ".../python3.7/site-packages/django/template/defaulttags.py", line
165, in render
values = list(values)
File ".../python3.7/site-packages/django/contrib/admin/helpers.py", line
287, in fields
form_field = empty_form.fields[field_name]
KeyError: 'name'
}}}
GitHub project to see the difference in behaviour between Django 2.0 and
Django 2.1 on this use case, using `tox`:
https://github.com/clementmangin/django-21-error-demo
EDIT: potential origin of issue:
https://github.com/django/django/commit/825f0beda804e48e9197fcf3b0d909f9f548aa47
#diff-3c42de3e53aba87b32c494f995a728dfR2023
--
--
Ticket URL: <https://code.djangoproject.com/ticket/29637#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/071.8fcb6a7cf3f2a88cd9f96bba2825007b%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.