整个模型只读

18

在Django管理界面中,有没有一种方法可以使整个模型只读?我是指整个模型。 因此,不允许添加、删除或更改任何内容,只能查看对象和字段,所有操作都只读?


1
似乎这是一个正在进行中的工作:https://github.com/django/django/pull/5297 - Bosco
1
注意,覆盖 has_change_permission() 方法只允许添加/删除记录,但不能编辑现有记录(在 Django 管理界面中)。 - Nishi
6个回答

13

ModelAdmin 提供了 get_readonly_fields() 方法 - 下面的代码未经测试,我的想法是像 ModelAdmin 一样确定所有字段,而不会遇到只读字段本身的递归问题:

from django.contrib.admin.util import flatten_fieldsets

class ReadOnlyAdmin(ModelAdmin):
    def get_readonly_fields(self, request, obj=None):
        if self.declared_fieldsets:
            fields = flatten_fieldsets(self.declared_fieldsets)
        else:
            form = self.get_formset(request, obj).form
            fields = form.base_fields.keys()
        return fields

然后在需要只读管理员的地方,创建该管理员的子类或Mixin。

对于添加/删除操作,为了使它们的按钮消失,您可能还需要添加:

    def has_add_permission(self, request):
        # Nobody is allowed to add
        return False
    def has_delete_permission(self, request, obj=None):
        # Nobody is allowed to delete
        return False

顺便提一下,在 ModelAdmin 中,如果 has_change_permission(查询或您的覆盖)返回 False,则无法访问对象的更改视图 - 连接也不会显示。如果实际上可以这样做,并且默认的 get_readonly_fields()检查更改权限并在这种情况下将所有字段设置为只读,那将非常棒,这样非更改者即使只能浏览数据...鉴于当前的管理结构假定视图=编辑,正如 jathanism 指出的那样,这可能需要引入一个“查看”权限来覆盖添加/更改/删除...

编辑:关于设置所有字段为只读,虽然未经测试,但看起来很有希望:

readonly_fields = MyModel._meta.get_all_field_names()

编辑:这里还有另一个

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

1
我已经创建了一个功能请求,以此影响 https://code.djangoproject.com/ticket/17295 - Danny W. Adair
1
请注意:get_formset方法仅适用于InlineModelAdmin。 - Vajk Hermecz
嗨,感谢您提供的这些选项。不过,也许礼貌一点的做法是提供您得到这些建议的答案链接,因为我看到它们并不全是您自己的。另一个好处是读者可以看到其他人对每个建议所做的评论。 - steps
3
无法在Django 1.11中运行 - AttributeError: 'ReadOnlyAdmin'对象没有'declared_fieldsets'属性。 - turbotux
_meta.get_all_field_names()在Django 1.10中已被移除。请参考https://docs.djangoproject.com/en/1.10/ref/models/meta/#migrating-from-the-old-api 进行迁移。 - hashlash
令人烦恼的是,编辑需要6个或更多字符,所以我无法建议编辑,但是flatten_fieldsets似乎在django.contrib.admin.utils(复数utils)中。 - Oded

4

所选答案不适用于Django 1.11,我找到了一种更简单的方法来解决这个问题,所以我想分享一下:

class MyModelAdmin(admin.ModelAdmin):

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

    def has_delete_permission(self, request, obj=None):
        return False

    def has_add_permission(self, request):
        return False

4
由于“查看权限” 将不会进入Django 1.11,很遗憾,这里提供了一种解决方案,通过使模型管理器的保存模型更改和添加模型历史记录条目成为无操作来使其只读。
def false(*args, **kwargs):
    """A simple no-op function to make our changes below readable."""
    return False

class MyModelReadOnlyAdmin(admin.ModelAdmin):
    list_display = [
        # list your admin listview entries here (as usual) 
    ]
    readonly_fields = [
        # list your read-only fields here (as usual)
    ]

    actions = None
    has_add_permission = false
    has_delete_permission = false
    log_change = false
    message_user = false
    save_model = false

(注意: 不要将false no-op helper和False builtin混淆。如果您不喜欢类外的helper函数,请将其移入类中,将其命名为no_op或其他名称,或通过通常的def覆盖受影响的属性。DRY原则会降低一些,但如果您不在意也可以这样做...)

这将:

  1. 在列表视图中删除操作下拉框(包括“删除”)
  2. 禁止添加新的模型条目
  3. 禁止删除现有的模型条目
  4. 避免在模型历史记录中创建日志条目
  5. 在保存后避免显示“已成功更改”的消息
  6. 避免将changeform更改保存到数据库中

它不会:

  • 删除或替换“保存并继续编辑”和“保存”两个按钮(这将有助于提高用户体验)
请注意,正如接受的答案中所提到的那样,get_all_field_names在Django 1.10中已经被删除。使用Django 1.10.5进行测试。

3

但是有没有一种方法可以将整个模型设置为只读,而不必将其所有属性添加到readonly_fields列表中? - juliomalegria
1
有,但不容易。您可以在Django中创建自定义权限,但是在管理站点方面,这并不容易。 Django假设如果允许人们在管理界面中查看内容,则也允许他们编辑它。 如果您感到大胆,可以尝试来自此问题的建议。 在我看来,更容易的方法是明确设置所有字段为只读并继续前进。 - jathanism

0

我曾经遇到过类似的情况:

  1. 用户应该能够创建模型对象
  2. 用户应该能够查看模型对象的列表
  3. 一旦对象被创建,用户不应该能够编辑它

1. 覆盖Change View

因为在ModelAdmin中可以覆盖change_view(),我们可以利用这一点来防止编辑已创建的模型实例。以下是我使用的示例:

def change_view(self, request, object_id, form_url='', extra_context=None):
    messages.error(request, 'Sorry, but editing is NOT ALLOWED')
    return redirect(request.META['HTTP_REFERER'])

2. 有条件地更改编辑权限

我还意识到文档对 ModelAdmin.has_change_permission() 的结果有不同的解释:

如果允许编辑 obj,则应返回 True,否则返回 False。如果 obj 为 None,则应返回 True 或 False,以指示是否一般允许编辑此类型的对象(例如,False 将被解释为当前用户无权编辑此类型的任何对象)。

这意味着我可以检查 obj 是否为 None,在这种情况下返回 True,否则返回 False,这实际上允许用户查看更改列表,但不能在保存模型实例后编辑或查看更改表单。

def has_change_permission(self, request, obj = None, **kwargs):
    if obj is None:
        return True
    else:
        return False

我认为这可能也会覆盖任何MODEL_can_change权限,允许不需要的人查看更改列表?


-1

根据我在Django 1.8上的测试,我们不能像答案#3中所述那样使用以下内容,但它在Django 1.4上可以工作:

##     self.get_formset(request, obj)      ##
answer 3 needs fix. Generally, alternative codes for this issue about below section 
##          form = self.get_formset(request, obj).form    ##
##          fields = form.base_fields.keys()              ##

可以是类似于这样的东西:

#~ (A) or
[f.name for f in self.model._meta.fields]

#~ (B) or
MyModel._meta.get_all_field_names()

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接