#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.


Reply via email to