Opened 16 years ago

Closed 11 years ago

#8387 closed Bug (fixed)

Returning a subset of the models fields in ModelAdmin.get_fieldsets only works when also overriding get_form

Reported by: Owned by: nobody
Component: Documentation Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

If you return only a subset of your models fields in get_fieldsets you need to override get_form as well and set the fields or exclude attribute. Otherwise the form will expect *all* fields to be present and will either raise validation errors on the missing fields or clear them if they have blank=True.

This won't work:

class MyModelAdmin(admin.ModelAdmin):
    def get_fieldsets(self, request, obj=None):
        if obj:
            return [(None, {'fields': ('field_c', 'field_b')})]
        return [(None, {'fields': ('field_a', 'field_b', 'field_c')})]

But this will:

class MyModelAdmin(admin.ModelAdmin):
    def get_fieldsets(self, request, obj=None):
        if obj:
            return [(None, {'fields': ('field_c', 'field_b')})]
        return [(None, {'fields': ('field_a', 'field_b', 'field_c')})]

    def get_form(self, request, obj=None, **kwargs):
        if obj:
            defaults = {'exclude': ('field_a',)}
        else:
            defaults = {}
        defaults.update(kwargs)
        return super(MyModelAdmin, self).get_form(request, obj, **defaults)

The reason for this is that Django cannot provide a sensible default for both cases:

  1. Just override get_form
  2. Just override get_fieldsets

In the first case get_fieldsets calls the overriden get_form and can so determine a default value for fieldsets. But this means that the default implementation of get_form cannot call get_fieldsets as this would create an infinite loop.

Which function calls the other could be discussed -- maybe overriding get_fieldsets is much more common? But in the case it stays like it is the attached patch documents the behavior.

Attachments (4)

get_form+get_fieldsets-docs-00.diff (2.5 KB ) - added by 16 years ago.
Documents get_fieldsets and get_form, gives an example each and adds a note documenting the gotcha
get_form+get_fieldsets-docs-01.diff (2.6 KB ) - added by 16 years ago.
Updated the patch for the docs refactor
get_form+get_fieldsets-docs-02.2.diff (2.6 KB ) - added by 16 years ago.
get_form+get_fieldsets-docs-02.diff (2.6 KB ) - added by 16 years ago.
Alternate version using flatten_fieldsets()

Download all attachments as: .zip

Change History (14)

by , 16 years ago

Documents get_fieldsets and get_form, gives an example each and adds a note documenting the gotcha

comment:1 by , 16 years ago

I don't know if we want to recommend flatten_fieldsets() for public use. But if we would this is the much cleaner method to use if you need to override get_form() just to work around the get_fieldsets() gotcha:

from django.contrib.admin.util import flatten_fieldsets

    def get_form(self, request, obj=None, **kwargs): 
        defaults = {
            'fields': flatten_fieldsets(self.get_fieldsets(request, obj)),
        }
        defaults.update(kwargs)
        return super(FrontpageImageOptions, self).get_form(request, obj, **defaults)

by , 16 years ago

Updated the patch for the docs refactor

by , 16 years ago

Alternate version using flatten_fieldsets()

comment:2 by Jacob, 16 years ago

milestone: 1.0post-1.0

comment:3 by (none), 16 years ago

milestone: post-1.0

Milestone post-1.0 deleted

comment:4 by Jacob, 16 years ago

Triage Stage: UnreviewedAccepted

comment:5 by Yeago, 14 years ago

Another annoying inconsistency is that a form returned with get_form will not be respected by fieldsets argument on the ModelAdmin.

comment:6 by Luke Plant, 14 years ago

Severity: Normal
Type: Bug

comment:7 by Aymeric Augustin, 13 years ago

UI/UX: unset

Change UI/UX from NULL to False.

comment:8 by Aymeric Augustin, 13 years ago

Easy pickings: unset

Change Easy pickings from NULL to False.

comment:9 by Tim Graham, 11 years ago

Component: contrib.adminDocumentation
Has patch: set

comment:10 by Tim Graham, 11 years ago

Resolution: fixed
Status: newclosed

"If you return only a subset of your models fields in get_fieldsets you need to override get_form as well and set the fields or exclude attribute."

I think this has been fixed since the original report. I just tried the example and a field with blank=True that was excluded using get_fieldsets was not cleared as originally reported, nor was there a ValidationError if the field had blank=False.

Documentation for get_form and get_fieldsets has also been added.

"Another annoying inconsistency is that a form returned with get_form will not be respected by fieldsets argument on the ModelAdmin."

This was fixed in #18681.

Note: See TracTickets for help on using tickets.
Back to Top