Django - 覆盖get_form以根据请求自定义管理表单

时间:2009-03-26 22:38:00

标签: django django-admin

我尝试了各种方法来实现这一目标。

我决定不覆盖formfield_for_dbfield,因为它没有得到请求对象的副本,我希望避免使用thread_locals hack。

我决定在我的ModelAdmin类中覆盖get_form并尝试以下操作:

class PageOptions(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        if request.user.is_superuser:
            self.fieldsets = ((None, {'fields': ('title','name',),}),)
        else:
            self.fieldsets = ((None, {'fields': ('title',),}),)
        return super(PageOptions,self).get_form(request, obj=None, **kwargs)

当我从get_form中打印fieldsets或declared_fieldsets时,我得到None(或者我在PageOptions中设置的初始值)。

为什么这不起作用,是否有更好的方法来做到这一点?

8 个答案:

答案 0 :(得分:22)

我从我最近的一个项目中得到了一些示例代码,我相信这些代码可以帮到你。在此示例中,超级用户可以编辑每个字段,而其他所有人都排除了“描述”字段。

请注意,我认为您需要从Form返回get_form课程,这可能就是您的工作不正常的原因。

以下是示例:

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)

答案 1 :(得分:9)

我不知道为什么打印属性并不会让你想要你刚刚分配(我猜这可能取决于你打印的位置,确切地说),但是请尝试覆盖get_fieldsets。基本实现如下所示:

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()})]

即。你应该能够归还你的元组。

由andybak编辑。 4年后,当我尝试在另一个项目上做类似的事情时,我再次发现了自己的问题。这次我采用了这种方法,虽然略有修改,以避免重复字段集定义:

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

答案 2 :(得分:6)

这是我的解决方案:

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) 

希望可以提供帮助

答案 3 :(得分:6)

为了创建自定义管理表单,我们定义了一个可用作mixin的新类。这种方法非常灵活:

  • ModelAdmin:定义包含所有字段的字段集

  • ModelForm:缩小显示的字段

  • FlexibleModelAdmin:覆盖ModelAdmin的get_fieldsets方法;返回仅包含管理表单中定义的字段的简化字段集


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)

在管理模型中,您可以定义fieldsets_proposed作为模板并包含所有字段。

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)

在模型表单中,您现在可以定义要排除/包含的字段。 mixin-class的get_fieldset()确保只返回表单中定义的字段。

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') 

答案 4 :(得分:4)

不要更改self属性的值,因为它不是线程安全的。你需要使用任何钩子来覆盖这些值。

答案 5 :(得分:1)

就我而言,使用Django 2.1,您可以执行以下操作

在forms.py

class ObjectAddForm(forms.ModelForm):

    class Meta:
        model = Object
        exclude = []

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

然后在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)

答案 6 :(得分:0)

您可以制作fieldsetsform属性并让它们发出信号以获得所需的表单/字段集。

答案 7 :(得分:0)

您可以为此目的使用get_fields或get_fieldset方法