[Fixed]-Django – Overriding get_form to customize admin forms based on request

15πŸ‘

βœ…

I have no idea why printing the property doesn’t give you want you just assigned (I guess may be that depends on where you print, exactly), but try overriding get_fieldsets instead. The base implementation looks like this:

def get_fieldsets(self, request, obj=None):
    if self.declared_fieldsets:
        return self.declared_fieldsets
    form = self.get_formset(request).form
    return [(None, {'fields': form.base_fields.keys()})]

I.e. you should be able to just return your tuples.

EDIT by andybak. 4 years on and I found my own question again when trying to do something similar on another project. This time I went with this approach although modified slightly to avoid having to repeat fieldsets definition:

def get_fieldsets(self, request, obj=None):
    # Add 'item_type' on add forms and remove it on changeforms.
    fieldsets = super(ItemAdmin, self).get_fieldsets(request, obj)
    if not obj: # this is an add form
        if 'item_type' not in fieldsets[0][1]['fields']:
            fieldsets[0][1]['fields'] += ('item_type',)
    else: # this is a change form
        fieldsets[0][1]['fields'] = tuple(x for x in fieldsets[0][1]['fields'] if x!='item_type')
    return fieldsets
πŸ‘€miracle2k

26πŸ‘

I have some sample code from a recent project of mine that I believe may help you. In this example, super users can edit every field, while everyone else has the β€œdescription” field excluded.

Note that I think it’s expected that you return a Form class from get_form, which could be why yours was not working quite right.

Here’s the example:

class EventForm(forms.ModelForm):
    class Meta:
        model = models.Event
        exclude = ['description',]

class EventAdminForm(forms.ModelForm):
    class Meta:
        model = models.Event

class EventAdmin(admin.ModelAdmin):

    def get_form(self, request, obj=None, **kwargs):
        if request.user.is_superuser:
            return EventAdminForm
        else:
            return EventForm 

admin.site.register(models.Event, EventAdmin)
πŸ‘€Ryan Duffield

8πŸ‘

This is my solution:

class MyModelAdmin(admin.ModelAdmin):  

    def get_form(self, request, obj=None, **kwargs):
        if request.user.is_superuser:
            self.exclude = ()
        else:
            self.exclude = ('field_to_exclude',) 
        return super(MyModelAdmin, self).get_form(request, obj=None, **kwargs) 

Hope can help

6πŸ‘

For creating customized admin forms we have defined a new class which can be used as mixin. The approach is quite flexible:

  • ModelAdmin: define a fieldset containing all fields

  • ModelForm: narrow the fields being shown

  • FlexibleModelAdmin: overriding get_fieldsets-method of ModelAdmin; returns a reduced fieldset that only contains the fields defined in the admin form

class FlexibleModelAdmin(object):
    '''
    adds the possibility to use a fieldset as template for the generated form
    this class should be used as mix-in
    '''

    def _filterFieldset(self, proposed, form):
        '''
        remove fields from a fieldset that do not
        occur in form itself.
        '''

        allnewfields = []
        fields = form.base_fields.keys()
        fieldset = []
        for fsname, fdict in proposed:
            newfields = []
            for field in fdict.get('fields'):
                if field in fields:
                    newfields.append(field)
                allnewfields.extend(newfields)
            if newfields:
                newentry = {'fields': newfields}
                fieldset.append([fsname,  newentry])

        # nice solution but sets are not ordered ;) 
        # don't forget fields that are in a form but were forgotten
        # in fieldset template
        lostfields = list(set(fields).difference(allnewfields))
        if len(lostfields):
            fieldset.append(['lost in space', {'fields': lostfields}])

        return fieldset

    def get_fieldsets(self, request, obj=None):
        '''
        Hook for specifying fieldsets for the add form.
        '''

        if hasattr(self, 'fieldsets_proposed'):
            form = self.get_form(request, obj)
            return self._filterFieldset(self.fieldsets_proposed, form)
        else:
            return super(FlexibleModelAdmin, self).get_fieldsets(request, obj)

In the admin model you define fieldsets_proposed which serves as template and contains all fields.

class ReservationAdmin(FlexibleModelAdmin, admin.ModelAdmin):

    list_display = ['id', 'displayFullName']
    list_display_links = ['id', 'displayFullName']
    date_hierarchy = 'reservation_start'
    ordering = ['-reservation_start', 'vehicle']
    exclude = ['last_modified_by']

    # considered by FlexibleModelAdmin as template
    fieldsets_proposed = (
        (_('General'), {
           'fields': ('vehicle', 'reservation_start', 'reservation_end', 'purpose') # 'added_by'
        }),
        (_('Report'), {
            'fields': ('mileage')
        }),
        (_('Status'), {
            'fields': ('active', 'editable')
        }),
        (_('Notes'), {
            'fields': ('note')
        }),
    )
    ....        

    def get_form(self, request, obj=None, **kwargs):
        '''
        set the form depending on the role of the user for the particular group
        '''

        if request.user.is_superuser:
            self.form = ReservationAdminForm
        else:
            self.form = ReservationUserForm

        return super(ReservationAdmin, self).get_form(request, obj, **kwargs)

admin.site.register(Reservation, ReservationAdmin)

In your model forms you can now define the fields to be excluded/included. get_fieldset() of the mixin-class makes sure that only the fields defined in the form are being returned.

class ReservationAdminForm(ModelForm):
    class Meta:
        model = Reservation
        exclude = ('added_by', 'last_modified_by')

class ReservationUserForm(BaseReservationForm):
    class Meta:
        model = Reservation
        fields = ('vehicle', 'reservation_start', 'reservation_end', 'purpose', 'note') 
πŸ‘€user774953

4πŸ‘

Don’t change the value of self attributes because it’s not thread-safe. You need to use whatever hooks to override those values.

2πŸ‘

In my case, with Django 2.1 you could do the following

In forms.py

class ObjectAddForm(forms.ModelForm):

    class Meta:
        model = Object
        exclude = []

class ObjectChangeForm(forms.ModelForm):
    class Meta:
        model = Object
        exclude = []

And then in the admin.py

from your.app import ObjectAddForm, ObjectChangeForm

class ObjectAdmin(admin.ModelAdmin):
    ....
    def get_form(self, request, obj=None, **kwargs):
        if obj is None: 
            kwargs['form'] = ObjectAddForm
        else:
            kwargs['form'] = ObjectChangeForm
        return super().get_form(request, obj, **kwargs)
πŸ‘€DekaS

1πŸ‘

You can use get_fields or get_fieldset methods for that purpose

0πŸ‘

You could make fieldsetsand form properties and have them emit signals to get the desired forms/fieldsets.

Leave a comment