【问题标题】:django admin make a field read-only when modifying obj but required when adding new objdjango admin 在修改 obj 时将字段设为只读,但在添加新 obj 时需要
【发布时间】:2011-05-19 14:47:16
【问题描述】:

在管理员中,我想在修改对象时禁用一个字段,但在添加新对象时使其成为必需。

django 的方法是什么?

【问题讨论】:

    标签: django django-admin


    【解决方案1】:

    对 Bernhard 和 Mario 的出色解决方案的更可插拔的解决方案,添加了对 readonly_fields 模拟的 createonly_fields 的支持:

    class MyModelAdmin(admin.ModelAdmin):
    
        # ModelAdmin configuration as usual goes here
    
        createonly_fields = ['title', ]
    
        def get_readonly_fields(self, request, obj=None):
            readonly_fields = list(super(MyModelAdmin, self).get_readonly_fields(request, obj))
            createonly_fields = list(getattr(self, 'createonly_fields', []))
                
            if obj:  # editing an existing object
                readonly_fields.extend(createonly_fields)
            return readonly_fields
    

    【讨论】:

      【解决方案2】:

      内联表单的情况对于 Django 2.2.x 仍然没有解决,但 solution from John 实际上非常聪明。

      根据我的情况稍微调整了代码:

      class NoteListInline(admin.TabularInline):
      """ Notes list, readonly """
          model = Note
          verbose_name = _('Note')
          verbose_name_plural = _('Notes')
          extra = 0
          fields = ('note', 'created_at')
          readonly_fields = ('note', 'created_at')
      
          def has_add_permission(self, request, obj=None):
          """ Only add notes through AddInline """
          return False
      
      class NoteAddInline(admin.StackedInline):
          """ Notes edit field """
          model = Note
          verbose_name = _('Note')
          verbose_name_plural = _('Notes')
          extra = 1
          fields = ('note',)
          can_delete = False
      
          def get_queryset(self, request):
              queryset = super().get_queryset(request)
              return queryset.none()  # no existing records will appear
      
      @admin.register(MyModel)
      class MyModelAdmin(admin.ModelAdmin):
          # ...
          inlines = (NoteListInline, NoteAddInline)
          # ...
      

      【讨论】:

        【解决方案3】:

        基于 Bernhard Vallant 先前出色建议的变体,它还保留了基类提供的任何可能的自定义(如果有的话):

        class MyModelAdmin(BaseModelAdmin):
        
            def get_readonly_fields(self, request, obj=None):
                readonly_fields = super(MyModelAdmin, self).get_readonly_fields(request, obj)
                if obj: # editing an existing object
                    return readonly_fields + ['field1', ..]
                return readonly_fields
        

        【讨论】:

          【解决方案4】:

          如果您只想在更改视图上将所有字段设置为只读,请覆盖管理员的 get_readonly_fields:

          def get_readonly_fields(self, request, obj=None):
              if obj: # editing an existing object
                  # All model fields as read_only
                  return self.readonly_fields + tuple([item.name for item in obj._meta.fields])
              return self.readonly_fields
          

          并且如果您想在更改视图上隐藏保存按钮

          1. 换个角度

            def change_view(self, request, object_id, form_url='', extra_context=None):
                ''' customize edit form '''
                extra_context = extra_context or {}
                extra_context['show_save_and_continue'] = False
                extra_context['show_save'] = False
                extra_context['show_save_and_add_another'] = False # this not works if has_add_permision is True
                return super(TransferAdmin, self).change_view(request, object_id, extra_context=extra_context)
            
          2. 如果用户尝试编辑,请更改权限:

            def has_add_permission(self, request, obj=None):
               # Not too much elegant but works to hide show_save_and_add_another button
                if '/change/' in str(request):
                    return False
                return True
            

            此解决方案已在 Django 1.11

            上进行了测试

          【讨论】:

            【解决方案5】:

            您可以覆盖管理员的get_readonly_fields 方法:

            class MyModelAdmin(admin.ModelAdmin):
            
                def get_readonly_fields(self, request, obj=None):
                    if obj: # editing an existing object
                        return self.readonly_fields + ('field1', 'field2')
                    return self.readonly_fields
            

            【讨论】:

            • 次要/主要警告:这不适用于内联。动态的“添加另一个 X”按钮将只读字段显示为“(无)”,而不是您期望的表单字段。
            【解决方案6】:

            仅供参考:以防其他人遇到与我遇到的相同的两个问题:

              1234563 /p>
            1. 这不适用于内联,因为传递给 get_readonly_fields() 的 obj 是父 obj(我有两个使用 css 或 js 的相当老套且安全性较低的解决方案)

            【讨论】:

            • 2.点 - 这是由于管理员中的错误:#15602 似乎它不会很快修复(上次活动 2 年前),所以看起来我们留给 CSS/JS 解决方案。
            【解决方案7】:

            遇到了类似的问题。我在 ModelAdmin 中使用“add_fieldsets”和“restricted_fieldsets”解决了这个问题。

            from django.contrib import admin  
            class MyAdmin(admin.ModelAdmin):
             declared_fieldsets = None
             restricted_fieldsets = (
                (None, {'fields': ('mod_obj1', 'mod_obj2')}),
                ( 'Text', {'fields': ('mod_obj3', 'mod_obj4',)}),
             )
            
             add_fieldsets = (
                        (None, {
                         'classes': ('wide',),
                         'fields': ('add_obj1', 'add_obj2', )}),
                         )
            

            请参见例如:http://code.djangoproject.com/svn/django/trunk/django/contrib/auth/admin.py

            但这并不能保护您的模型免受“add_objX”以后的更改。 如果你也想要这个,我认为你必须通过 Model 类的“保存”功能并检查那里的变化。

            参见:www.djangoproject.com/documentation/models/save_delete_hooks/

            格里兹,尼克

            【讨论】:

              【解决方案8】:

              您可以通过覆盖 ModelAdmin 的 formfield_for_foreignkey 方法来做到这一点:

              from django import forms
              from django.contrib import admin
              
              from yourproject.yourapp.models import YourModel
              
              class YourModelAdmin(admin.ModelAdmin):
              
                  class Meta:
                      model = YourModel
              
                  def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
                      # Name of your field here
                      if db_field.name == 'add_only':
                          if request:
                              add_opts = (self._meta.app_label, self._meta.module_name)
                              add = u'/admin/%s/%s/add/' % add_opts
                              if request.META['PATH_INFO'] == add:
                                  field = db_field.formfield(**kwargs)
                              else:
                                  kwargs['widget'] = forms.HiddenInput()
                                  field = db_field.formfield(**kwargs)
                          return field
                      return admin.ModelAdmin(self, db_field, request, **kwargs)
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2018-06-12
                • 2014-01-25
                • 2013-07-10
                • 2013-01-11
                • 2018-11-10
                • 2019-04-14
                • 2021-10-29
                • 1970-01-01
                相关资源
                最近更新 更多