Django - переопределение get_form для настройки форм администратора на основе запроса

Я пробовал различные методы для достижения этого.

Я решил не переопределять formfield_for_dbfield, поскольку он не получает копию объекта запроса, и я надеялся избежать взлома thread_locals.

Я решил переопределить get_form в моем классе ModelAdmin и попробовал следующее:

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)

Когда я печатаю поля или decl_fieldsets из get_form, я получаю None (или независимо от того, что я установил как начальное значение в PageOptions).

Почему это не работает, и есть ли лучший способ сделать это?

Ответы

Ответ 1

Я понятия не имею, почему печать свойства не дает вам желать, чтобы вы только что назначили (возможно, это зависит от того, где вы печатаете), но вместо этого попробуйте переопределить 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()})]

т.е. вы должны просто вернуть свои кортежи.

ИЗМЕНИТЬ by 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

У меня есть образец кода из недавнего моего проекта, который, я считаю, может вам помочь. В этом примере суперпользователи могут редактировать каждое поле, в то время как у всех остальных исключено поле описания.

Обратите внимание, что я думаю, что ожидается, что вы вернете класс 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)

Ответ 3

Это мое решение:

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) 

Надежда может помочь

Ответ 4

Для создания настраиваемых форм администратора мы определили новый класс, который можно использовать в качестве mixin. Этот подход довольно гибкий:

  • ModelAdmin: укажите поле, содержащее поля all

  • ModelForm: сузить отображаемые поля

  • FlexibleModelAdmin: переопределение метода get_fieldsets ModelAdmin; возвращает уменьшенный набор полей, который содержит только поля, определенные в форме администратора


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)

В модели admin вы определяете 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)

В ваших модельных формах вы можете теперь определить поля, которые должны быть исключены/включены. get_fieldset() класса mixin гарантирует, что возвращаются только поля, определенные в форме.

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

Ответ 5

Не изменяйте значение атрибутов self, потому что оно не является потокобезопасным. Для переопределения этих значений вам нужно использовать любые перехватчики.

Ответ 6

Вы можете сделать свойства fieldsets и form и заставить их испускать сигналы для получения желаемых форм/полей.