【问题标题】:Django admin - make all fields readonlyDjango admin - 使所有字段只读
【发布时间】:2012-11-28 20:16:25
【问题描述】:

我正在尝试将所有字段设为只读而不明确列出它们。

类似:

class CustomAdmin(admin.ModelAdmin):
    def get_readonly_fields(self, request, obj=None):
        if request.user.is_superuser:
            return self.readonly_fields

        return self.fields

问题是CustomAdmin.fields此时没有设置。

有什么想法吗?

【问题讨论】:

标签: python django django-admin


【解决方案1】:

从 django 2.1 开始,您可以通过从 ModelAdminhas_change_permission 方法返回 False 来防止编辑,同时允许查看,如下所示:

class CustomAdmin(admin.ModelAdmin):
    def has_change_permission(self, request, obj=None):
        return False

(这在 django 2.1 之前不起作用,因为它还会拒绝任何仅尝试查看的用户的权限。)

【讨论】:

  • 这样一个漂亮又漂亮的解决方案。为什么不多投赞成票?
  • @ErikKalkoken:这适用于only since version 2.1 of django,其中添加了view permission。在此之前(直到并包括 django 2.0),此答案建议的解决方案将使管理站点拒绝所有尝试在管理员中查看相关模型的用户的权限。同意这是最近 django 最简洁的解决方案。
  • 很好的答案!对于某些应用程序,您可能还需要添加 has_delete_permissionhas_add_permission 方法
  • 并不是不能添加内联
【解决方案2】:

小心,self.model._meta.fields 不一定是 CustomAdmin 拥有的相同字段!

“管理员的所有字段”看起来更像这样:

from django.contrib import admin
from django.contrib.admin.utils import flatten_fieldsets

class CustomAdmin(admin.ModelAdmin):
    def get_readonly_fields(self, request, obj=None):
        if request.user.is_superuser:
            return self.readonly_fields

        if self.declared_fieldsets:
            return flatten_fieldsets(self.declared_fieldsets)
        else:
            return list(set(
                [field.name for field in self.opts.local_fields] +
                [field.name for field in self.opts.local_many_to_many]
            ))

【讨论】:

  • 谢谢这很好用。只需要删除 many_to_many,因为它看起来很奇怪(无论如何它们都被注册为内联并拥有自己的权限。这感觉比获得 model._meta 更安全。
  • 不用担心。如果您有一个不是内联的 m2m,请小心。虽然它应该让自己知道:-)
  • 谢谢。这是我找到的最合理的答案。
  • declared_fieldsets 自 django 1.7 起已弃用,将在 django 1.9 中删除;用get_fieldsets(req,obj) 替换它达到递归限制。此外,admin.util 更名为admin.utils
  • 这对我有用 Django 1.10 ` def get_readonly_fields(self, request, obj=None): if request.user.is_superuser: return self.readonly_fields return list(set( [field.name for field在 self.opts.local_fields] + [self.opts.local_many_to_many 中字段的字段名称]))`
【解决方案3】:

好的,现在是这个:

class CustomAdmin(admin.ModelAdmin):
    def get_readonly_fields(self, request, obj=None):
        # ...

        return [f.name for f in self.model._meta.fields]

仍在寻找不那么丑陋的方法。

【讨论】:

  • 其实我在别处找到了这个。在对您的答案进行投票并将其调整为我在我自己的代码中使用和测试的版本之后。
  • if 子句不会为您的问题添加任何内容
  • 是的。它只是更清洁。此外,它不这样做:AttributeError: type object 'NoneType' has no attribute '_meta' 创建对象时。
  • 这不包括任何额外的字段(例如:使用自定义表单)。
【解决方案4】:

您可以遍历模型元字段:

def get_readonly_fields(self, request, obj=None):
    if obj:
        self.readonly_fields = [field.name for field in obj.__class__._meta.fields]
    return self.readonly_fields

【讨论】:

  • 当用户尝试创建新对象时,这里出现异常:obj.__class__._meta.fields
  • 好的,我删除我的答案,你真的是第一个:)
  • @hedde 您无权获得答案,仅仅因为您先写了它,并且仅仅因为有人采用相同的方法并不意味着他们在模仿您或应该投反对票
  • 非常感谢。很好的答案!
  • 这不包括任何额外的字段(例如:使用自定义表单)。
【解决方案5】:

对于内联(制表符或堆栈)

def get_readonly_fields(self, request, obj=None):
    fields = []
    for field in self.model._meta.get_all_field_names():
        if field != 'id':
            fields.append(field)
    return fields

def has_add_permission(self, request):
    return False

【讨论】:

【解决方案6】:

这对我来说适用于 Django 1.10

def get_readonly_fields(self, request, obj=None):
    if request.user.is_superuser:
        return self.readonly_fields

    return list(set(
        [field.name for field in self.opts.local_fields] +
        [field.name for field in self.opts.local_many_to_many]
    ))

【讨论】:

    【解决方案7】:

    如果有人还在寻找更好的方法,你可以这样使用它:

    @admin.register(ClassName)
    class ClassNameAdmin(admin.ModelAdmin):
        readonly_fields = [field.name for field in ClassName._meta.fields]
    

    ClassName 是您的模型类。

    【讨论】:

      【解决方案8】:

      我的要求是相似的。我需要 only one 字段显示为 read-only 。这很好用:

      class ChoiceInline(admin.TabularInline):
          model = Choice
          extra = 1
          fields = ['choice_text', 'votes']
          readonly_fields = ['votes']
      
      class QuestionAdmin(admin.ModelAdmin):
          #fields = ['pub_date', 'question_text']
          fieldsets = [
              (None, {'fields': ['question_text']}),
              ('Date Information', {'fields': ['pub_date']}),
          ]
          search_fields = ['question_text']
      
      
          inlines = [ChoiceInline]
      

      参考:C:\Python27\Lib\site-packages\django\contrib\admin\options.py

      【讨论】:

        【解决方案9】:

        假设您已将 user_mode 定义为;

        Admin, Customers and Staff
        

        如果您想拒绝员工(当然还有客户)删除客户、产品、订单等的权限...

        您的代码运行;

        def get_readonly_fields(self, request: HttpRequest, obj=None):
            if request.user.user_mode != "Admin":
                return self.readonly_fields + ['user_mode']
            return super().get_readonly_fields(request, obj)
        

        其中 user_mode = 保存用户类型的模型字段。

        注意:我的代码使用“Pylance”(如 JS 中的打字稿)

        【讨论】:

          【解决方案10】:

          使用 get_fieldsets 您可以从表单中获取所有字段

          def get_readonly_fields(self, request, obj=None):
              readonly = []
              for fs in self.get_fieldsets(request, obj):
                  if len(fs) > 1:
                      readonly += fs[1].get('fields', [])
              return readonly
          

          【讨论】:

          • get_readonly_fields() 内调用get_fieldsets() 达到递归限制:(
          【解决方案11】:
          @admin.register(Hero)
          class HeroAdmin(admin.ModelAdmin, ExportCsvMixin):
              ...
              readonly_fields = ["father", "mother", "spouse"]
          

          参考: https://books.agiliq.com/projects/django-admin-cookbook/en/latest/changeview_readonly.html

          【讨论】:

          • 问题是得到all fields readonly without listing them explicitly。您的回答建议明确列出它们。
          猜你喜欢
          • 1970-01-01
          • 2018-06-03
          • 2018-06-12
          • 2012-02-01
          • 2011-03-08
          • 1970-01-01
          • 2012-07-21
          • 2021-09-18
          • 2019-03-14
          相关资源
          最近更新 更多