Django - Overriding get_form to customize admin forms based on request

后端 未结 8 956
眼角桃花
眼角桃花 2020-12-04 18:15

I\'ve tried various methods to achieve this.

I decided against overriding formfield_for_dbfield as it\'s doesn\'t get a copy of the request object and I was hoping t

相关标签:
8条回答
  • 2020-12-04 18:59

    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)
    
    0 讨论(0)
  • 2020-12-04 18:59

    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') 
    
    0 讨论(0)
  • 2020-12-04 19:01

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

    0 讨论(0)
  • 2020-12-04 19:04

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

    0 讨论(0)
  • 2020-12-04 19:06

    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

    0 讨论(0)
  • 2020-12-04 19:09

    You can use get_fields or get_fieldset methods for that purpose

    0 讨论(0)
提交回复
热议问题