Django管理界面中的只读模型?

Readonly models in Django admin interface?

如何在管理界面中使模型完全只读?这是一种日志表,我在其中使用管理功能来搜索、排序、筛选等,但不需要修改日志。

如果这看起来像是复制品,我不想这样做:

  • 我不是在寻找只读字段(即使将每个字段都设为只读也会让您创建新的记录)
  • 我不想创建只读用户:每个用户都应该是只读的。


管理员负责编辑,而不仅仅是查看(您将无法找到"查看"权限)。为了实现您想要的,您必须禁止添加、删除和使所有字段只读:

1
2
3
4
5
6
7
class MyAdmin(ModelAdmin):

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

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

(如果你禁止改变,你甚至看不到物体)

对于一些试图以只读方式自动设置所有字段的未测试代码,请将我对整个模型的回答视为只读。

编辑:还未测试,但刚查看了我的logentryadmin,它已经

1
readonly_fields = MyModel._meta.get_all_field_names()

不知道在任何情况下这是否有效。

edit:queryset.delete()仍然可以批量删除对象。要解决这个问题,请提供您自己的"对象"管理器和相应的不删除的queryset子类-请参见Django中的overriding queryset.delete()。


下面是两个我用来创建模型的类和/或它的inline只读。

对于模型管理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from django.contrib import admin

class ReadOnlyAdmin(admin.ModelAdmin):
    readonly_fields = []

    def get_readonly_fields(self, request, obj=None):
        return list(self.readonly_fields) + \
               [field.name for field in obj._meta.fields] + \
               [field.name for field in obj._meta.many_to_many]


    def has_add_permission(self, request):
        return False

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

class MyModelAdmin(ReadOnlyAdmin):
    pass

对于入口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ReadOnlyTabularInline(admin.TabularInline):
    extra = 0
    can_delete = False
    editable_fields = []
    readonly_fields = []
    exclude = []

    def get_readonly_fields(self, request, obj=None):
        return list(self.readonly_fields) + \
               [field.name for field in self.model._meta.fields
                if field.name not in self.editable_fields and
                   field.name not in self.exclude]

    def has_add_permission(self, request):
        return False


class MyInline(ReadOnlyTabularInline):
    pass


参见https://djangosnippets.org/snippets/10539/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class ReadOnlyAdminMixin(object):
   """Disables all editing capabilities."""
    change_form_template ="admin/view.html"

    def __init__(self, *args, **kwargs):
        super(ReadOnlyAdminMixin, self).__init__(*args, **kwargs)
        self.readonly_fields = self.model._meta.get_all_field_names()

    def get_actions(self, request):
        actions = super(ReadOnlyAdminMixin, self).get_actions(request)
        del actions["delete_selected"]
        return actions

    def has_add_permission(self, request):
        return False

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

    def save_model(self, request, obj, form, change):
        pass

    def delete_model(self, request, obj):
        pass

    def save_related(self, request, form, formsets, change):
        pass

模板/admin/view.html

1
2
3
4
5
6
7
8
{% extends"admin/change_form.html" %}
{% load i18n %}

{% block submit_buttons_bottom %}
 
    {% blocktrans %}Back to list{% endblocktrans %}
 
{% endblock %}

templates/admin/view.html(用于grappelli)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{% extends"admin/change_form.html" %}
{% load i18n %}

{% block submit_buttons_bottom %}
  <footer class="grp-module grp-submit-row grp-fixed-footer">
    <header style="display:none">{% trans"submit options"|capfirst context"heading" %}</header>
   
<ul>

       
<li>
{% blocktrans %}Back to list{% endblocktrans %}
</li>

   
</ul>

  </footer>
{% endblock %}


如果您希望用户意识到他/她无法编辑它,则第一个解决方案缺少2个部分。您已删除删除操作!

1
2
3
4
5
6
7
8
9
10
11
class MyAdmin(ModelAdmin)
    def has_add_permission(self, request, obj=None):
        return False
    def has_delete_permission(self, request, obj=None):
        return False

    def get_actions(self, request):
        actions = super(MyAdmin, self).get_actions(request)
        if 'delete_selected' in actions:
            del actions['delete_selected']
        return actions

第二:只读解决方案在普通模型上运行良好。但是,如果您有一个带有外键的继承模型,那么它就不起作用。不幸的是,我还不知道解决方法。一个好的尝试是:

整个模型为只读

但也不适合我。

最后一点需要注意的是,如果您想考虑一个广泛的解决方案,您必须强制要求每个内联也必须是只读的。


实际上,您可以尝试这个简单的解决方案:

1
2
3
4
5
6
7
class ReadOnlyModelAdmin(admin.ModelAdmin):
    actions = None
    list_display_links = None
    # more stuff here

    def has_add_permission(self, request):
        return False

  • actions = None:避免使用"删除所选…"选项显示下拉列表。
  • list_display_links = None:避免点击列来编辑该对象。
  • 返回false的has_add_permission()避免为该模型创建新对象


这是添加到django 2.1,该版本于8/1/18发布!

ModelAdmin.has_view_permission()与现有的有删除权限、有更改权限、有添加权限一样。你可以在这里的文档里读到

从发行说明:

This allows giving users read-only access to models in the admin.
ModelAdmin.has_view_permission() is new. The implementation is
backwards compatible in that there isn’t a need to assign the"view"
permission to allow users who have the"change" permission to edit
objects.


编译@darklow和@josir的优秀答案,再加上添加一点以删除"save"和"save and continue"按钮(在python 3语法中):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class ReadOnlyAdmin(admin.ModelAdmin):
   """Provides a read-only view of a model in Django admin."""
    readonly_fields = []

    def change_view(self, request, object_id, extra_context=None):
       """ customize add/edit form to remove save / save and continue"""
        extra_context = extra_context or {}
        extra_context['show_save_and_continue'] = False
        extra_context['show_save'] = False
        return super().change_view(request, object_id, extra_context=extra_context)

    def get_actions(self, request):
        actions = super().get_actions(request)
        if 'delete_selected' in actions:
            del actions['delete_selected']
        return actions

    def get_readonly_fields(self, request, obj=None):
        return list(self.readonly_fields) + \
           [field.name for field in obj._meta.fields] + \
           [field.name for field in obj._meta.many_to_many]

    def has_add_permission(self, request):
        return False

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

然后你用像

1
2
class MyModelAdmin(ReadOnlyAdmin):
    pass

我只在Django1.11/python 3上尝试过。


如果接受的答案对您不起作用,请尝试以下操作:

1
2
3
4
5
6
def get_readonly_fields(self, request, obj=None):
    readonly_fields = []
    for field in self.model._meta.fields:
        readonly_fields.append(field.name)

    return readonly_fields


接受的答案应该有效,但这也将保留只读字段的显示顺序。您也不必用这个解决方案对模型进行硬编码。

1
2
3
4
5
6
7
8
9
class ReadonlyAdmin(admin.ModelAdmin):
   def __init__(self, model, admin_site):
      super(ReadonlyAdmin, self).__init__(model, admin_site)
      self.readonly_fields = [field.name for field in filter(lambda f: not f.auto_created, model._meta.fields)]

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


当Django Admin中的某些用户需要将所有字段设置为只读时,我遇到了同样的要求,最终利用Django模块"Django Admin View Permission"而不滚动自己的代码。如果需要更细粒度的控件来显式定义哪些字段,那么需要扩展模块。你可以在这里查看这个插件


我编写了一个通用类,根据用户权限(包括inlines;)处理只读视图。

在models.py中:

1
2
3
4
5
6
7
8
9
10
class User(AbstractUser):
    ...
    def is_readonly(self):
        if self.is_superuser:
            return False
        # make readonly all users not in"admins" group
        adminGroup = Group.objects.filter(name="admins")
        if adminGroup in self.groups.all():
            return False
        return True

在admin.py中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# read-only user filter class for ModelAdmin
class ReadOnlyAdmin(admin.ModelAdmin):
    def __init__(self, *args, **kwargs):
        # keep initial readonly_fields defined in subclass
        self._init_readonly_fields = self.readonly_fields
        # keep also inline readonly_fields
        for inline in self.inlines:
            inline._init_readonly_fields = inline.readonly_fields
        super().__init__(*args,**kwargs)
    # customize change_view to disable edition to readonly_users
    def change_view( self, request, object_id, form_url='', extra_context=None ):
        context = extra_context or {}
        # find whether it is readonly or not
        if request.user.is_readonly():
            # put all fields in readonly_field list
            self.readonly_fields = [ field.name for field in self.model._meta.get_fields() if not field.auto_created ]
            # readonly mode fer all inlines
            for inline in self.inlines:
                inline.readonly_fields = [field.name for field in inline.model._meta.get_fields() if not field.auto_created]
            # remove edition buttons
            self.save_on_top = False
            context['show_save'] = False
            context['show_save_and_continue'] = False
        else:
            # if not readonly user, reset initial readonly_fields
            self.readonly_fields = self._init_readonly_fields
            # same for inlines
            for inline in self.inlines:
                inline.readonly_fields = self._init_readonly_fields
        return super().change_view(
                    request, object_id, form_url, context )
    def save_model(self, request, obj, form, change):
        # disable saving model for readonly users
        # just in case we have a malicious user...
        if request.user.is_readonly():
            # si és usuari readonly no guardem canvis
            return False
        # if not readonly user, save model
        return super().save_model( request, obj, form, change )

然后,我们可以在admin.py中正常继承类:

1
2
3
4
class ContactAdmin(ReadOnlyAdmin):
    list_display = ("name","email","whatever")
    readonly_fields = ("updated","created")
    inlines = ( PhoneInline, ... )


只读=>查看权限

  • 埃多克斯1〔3〕
  • 在settings.py中向已安装的应用程序添加"管理员查看权限"。如下所示:`已安装的应用程序=['管理员查看权限',
  • python manage.py迁移
  • python manage.py运行服务器6666
  • 好的。在"视图"许可下玩吧。