﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
20702	Using ModelAdmin.get_formsets() to filter inlines is broken.	stanislas.guerra@…	Tim Schilling	"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.
"	Bug	closed	contrib.admin	dev	Normal	fixed	admin inlines get_formsets	Tim Schilling Tim Graham	Accepted	1	0	0	0	0	0
