#20702: Using ModelAdmin.get_formsets() to filter inlines is broken.
----------------------------------+----------------------------------------
Reporter: | Owner: nobody
stanislas.guerra@… | Status: new
Type: Bug | Version: 1.4
Component: contrib.admin | Keywords: admin inlines get_formsets
Severity: Normal | Has patch: 0
Triage Stage: Unreviewed | UI/UX: 0
Easy pickings: 0 |
----------------------------------+----------------------------------------
Hi,
In the 1.4 admin documentation
[https://docs.djangoproject.com/en/1.4/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_formsets]
it is said :
''For example if you wanted to display a particular inline only in the
change view, [...]''
By extension, I tried to use that logic to display a particular inline
only if the instance met a particular condition:
{{{
class BarInline(admin.TabularInline):
model = Bar
fields = ("name", "surname")
class BazInline(admin.TabularInline):
model = Baz
fields = ("country", "zipcode")
class FooAdmin(admin.ModelAdmin):
inlines = [BarInline, BazInline]
def get_formsets(self, request, obj=None):
for inline in self.get_inline_instances(request):
# Only in change view.
if obj is None:
continue
if isinstance(inline, BarInline) and obj.condition():
yield inline.get_formset(request, obj)
elif isinstance(inline, BazInline) and
obj.another_condition():
yield inline.get_formset(request, obj)
}}}
Which raise an exception :
{{{
KeyError at /admin/app/foo/22/
"name"
...
/Library/Python/2.7/site-packages/django/contrib/admin/helpers.py in
fields
240. yield self.formset.form.base_fields[field]
}}}
The reason is because ''ModelAdmin.add_view() / .change_view()'' use
''zip(self.get_formsets(request), inline_instances)'' fonction to
construct the formsets from the Formset class and the inline instance and
so expects two ordered listes of matching Formset and Inline.
So if in get_formsets() you don't yield something you create a shift in
the lists which explain the exception raised (use fields from BarInline to
instanciate BazFormset).
A workaround is to override change_view() and change self.inline locally:
{{{
class FooAdmin(admin.ModelAdmin):
inlines = []
def change_view(self, request, object_id, form_url='',
extra_context=None):
from django.contrib.admin.util import unquote
obj = self.get_object(request, unquote(object_id))
if obj:
if obj.condition():
self.inlines = [BarInline]
elif obj.another_condition():
self.inlines = [BazInline]
return super(FooAdmin, self).change_view(request, object_id,
form_url, extra_context)
}}}
Is get_formsets() not supposed to be used like that?
Maybe this is implicitly fixed in 1.5 with the addition of the ''obj''
parameter in ''get_inline_instances()''?
Thanks.
--
Ticket URL: <https://code.djangoproject.com/ticket/20702>
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/069.32d03f7743cc9bbad92633956ffbc07c%40djangoproject.com.
For more options, visit https://groups.google.com/groups/opt_out.