【问题标题】:Django - Overriding get_form to customize admin forms based on requestDjango - 覆盖 get_form 以根据请求自定义管理表单
【发布时间】:2010-10-15 19:52:16
【问题描述】:

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

我决定不覆盖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 中打印fieldsetsdeclared_fieldsets 时,我得到None(或我在PageOptions 中设置的任何初始值)。

为什么这不起作用?有更好的方法吗?

【问题讨论】:

  • 您是否尝试在 PageOptions 类的某处声明一个字段集以查看是否有效?
  • 是的,无论 get_form 中的内容是什么,该节目都结束了

标签: django django-admin


【解决方案1】:

我有一些来自我最近的项目的示例代码,我相信它们可能会对您有所帮助。在此示例中,超级用户可以编辑每个字段,而其他所有人都排除了“描述”字段。

请注意,我认为您应该从 get_form 返回一个 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)

【讨论】:

  • 这看起来真的很有帮助。我现在正处于最后期限的中间,但我会在下周尝试一下。 PS我的超级调用不会导致返回表单吗?我只是在修改参数并返回 get_form 本身会返回的内容。
  • 在这个解决方案中使用字段集怎么样?如果我们使用 get_forms,我们似乎会丢失字段集的配置以及 DateTimeField javascript 小部件。谢谢。
  • docs.djangoproject.com/en/1.4/ref/contrib/admin/… 似乎是更好的解决方案。 @ryan-duffield
  • 我收到一个错误:EventAdminForm' object has no attribute 'instance'。 Django 2.1
【解决方案2】:

我不知道为什么打印属性不会让您希望您刚刚分配(我猜这可能取决于您打印的位置,确切地说),但请尝试覆盖 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

【讨论】:

    【解决方案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) 
    

    希望能帮到你

    【讨论】:

    • 您可能希望将其设为只读(Django 1.2 及更高版本)而不是删除该字段 - 只需使用 self.readonly_fields 而不是 self.exclude。
    • 这不是线程安全的。或者它是线程安全的,因为你有其他条件。尝试修改 kwargs["exclude"] 而不是 self.exclude.
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      为了创建自定义的管理表单,我们定义了一个可以用作 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 类的 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') 
      

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          【解决方案7】:

          在我的情况下,使用 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)
          

          【讨论】:

            【解决方案8】:

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

            【讨论】:

              猜你喜欢
              • 2012-05-28
              • 1970-01-01
              • 1970-01-01
              • 2012-08-26
              • 2023-03-22
              • 1970-01-01
              • 2014-02-11
              • 1970-01-01
              • 2012-06-23
              相关资源
              最近更新 更多