Code

Opened 6 years ago

Closed 8 months 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: jarrow Owned by: nobody
Component: Documentation Version: master
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 jarrow 6 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 jarrow 6 years ago.
Updated the patch for the docs refactor
get_form+get_fieldsets-docs-02.2.diff (2.6 KB) - added by jarrow 6 years ago.
get_form+get_fieldsets-docs-02.diff (2.6 KB) - added by jarrow 6 years ago.
Alternate version using flatten_fieldsets()

Download all attachments as: .zip

Change History (14)

Changed 6 years ago by jarrow

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

comment:1 Changed 6 years ago by jarrow

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

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)

Changed 6 years ago by jarrow

Updated the patch for the docs refactor

Changed 6 years ago by jarrow

Changed 6 years ago by jarrow

Alternate version using flatten_fieldsets()

comment:2 Changed 6 years ago by jacob

  • milestone changed from 1.0 to post-1.0

comment:3 Changed 5 years ago by anonymous

  • milestone post-1.0 deleted

Milestone post-1.0 deleted

comment:4 Changed 5 years ago by jacob

  • Triage Stage changed from Unreviewed to Accepted

comment:5 Changed 3 years ago by subsume

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

comment:6 Changed 3 years ago by lukeplant

  • Severity set to Normal
  • Type set to Bug

comment:7 Changed 2 years ago by aaugustin

  • UI/UX unset

Change UI/UX from NULL to False.

comment:8 Changed 2 years ago by aaugustin

  • Easy pickings unset

Change Easy pickings from NULL to False.

comment:9 Changed 9 months ago by timo

  • Component changed from contrib.admin to Documentation
  • Has patch set

comment:10 Changed 8 months ago by timo

  • Resolution set to fixed
  • Status changed from new to closed

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

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.