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


86

如何在管理界面中将模型完全设为只读?它用于一种日志表,我在其中使用管理功能来搜索,排序,过滤等,但无需修改日志。

万一这看起来像是重复的,这不是我想要做的:

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

2
此功能应很快推出:github.com/django/django/pull/5297
Bosco

2
has_view_permission最终在Django 2.1中实现。另请参阅下面的stackoverflow.com/a/51641149
djvg

Answers:


21

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

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_action = "delete_selected"
        if del_action in actions:
            del actions[del_action]
        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

templates / admin / view.html

{% extends "admin/change_form.html" %}
{% load i18n %}

{% block submit_buttons_bottom %}
  <div class="submit-row">
    <a href="../">{% blocktrans %}Back to list{% endblocktrans %}</a>
  </div>
{% endblock %}

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

{% 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"><h1>{% trans "submit options"|capfirst context "heading" %}</h1></header>
    <ul>
       <li><a href="../" class="grp-button grp-default">{% blocktrans %}Back to list{% endblocktrans %}</a></li>
    </ul>
  </footer>
{% endblock %}

似乎是合法的。自从我使用Django以来已经很长时间了,可能要等一下其他评论者要说些什么。
史蒂夫·本内特

这是对一个mixin Model,或为ModelAdmin
OrangeDog

是给的ModelAdmin
Pascal Polleunus

对于Django 1.8及更高版本,不建议使用get_all_field_names。向后兼容的方式来获取它们获得它们的捷径
fzzylogic

您可以使用has_add_permission
rluts

70

管理员用于编辑,而不仅仅是查看(您不会找到“查看”权限)。为了实现您想要的功能,您必须禁止添加,删除所有字段并将其设置为只读:

class MyAdmin(ModelAdmin):

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

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

(如果您禁止更改,则什至看不到对象)

对于一些试图自动将所有字段设置为只读的未经测试的代码,请参阅我对Whole model的回答为只读

编辑:也未经测试,但只是看看我的LogEntryAdmin,它具有

readonly_fields = MyModel._meta.get_all_field_names()

不知道这是否在所有情况下都有效。

编辑:QuerySet.delete()可能仍会批量删除对象。要解决此问题,请提供您自己的“对象”管理器以及不会删除的相应QuerySet子类-请参见在Django中覆盖QuerySet.delete()


2
PS:是的,和其他答案一样,方法可能是在ReadOnlyAdmin类中定义这三件事,然后在需要该行为的地方从其子类化。甚至可以得到看中,并允许该组/权限的定义允许编辑,然后返回True相应(和使用get_readonly_fields(),它可以访问请求,因此当前用户)。
Danny W. Adair

几乎完美。我可以贪婪地问是否有一种方法可以使行不链接到编辑页面?(同样,无需放大任何行,也无需编辑任何内容)
Steve Bennett

1
如果将ModelAdmin的list_display_links设置为评估为False的值(例如空列表/元组),则ModelAdmin .__ init __()会将list_display_links设置为所有列(操作复选框除外)-请参阅options.py。我想这样做是为了确保有链接。因此,我将在ReadOnlyAdmin中重写__init __(),调用父级,然后将list_display_links设置为空列表或元组。鉴于您现在将没有指向只读更改形式的链接,可能最好为此创建一个参数/类属性-我不认为这是通常需要的行为。Hth
Danny W. Adair

关于从模型设置的readonly_fields,如果您覆盖表单并添加其他字段,则此方法可能行不通...基于实际的表单字段可能更好。
Danny W. Adair

这不起作用:def __init __(self,* args):super(RegistrationStatusAdmin,self).__ init __(* args)self.display_links = []
Steve Bennett

50

这是我用来制作模型的两个类,并且/或者它是内联的,只读的。

对于模型管理员:

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

对于内联:

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

如何将两个类都应用于一个子类。例如,如果我在班级中有普通的字段和内联线?我可以同时延长两者吗?
Timo 2015年

@timo使用这些类作为mixins
MartinM '17

1
has_add_permissioninReadOnlyAdmin仅接受请求作为参数
MartinM '17

has_change_permission()也需要被覆盖。def has_change_permission(self,request,obj = None):
大卫·欧拉

13

如果您希望用户知道他/她无法对其进行编辑,则第一个解决方案将缺少2个部分。您已经删除了删除操作!

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

第二:只读解决方案在普通模型上运行良好。但它不是,如果你有外键继承的模型工作。不幸的是,我还不知道解决方案。一个很好的尝试是:

整个模型为只读

但这对我也不起作用。

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


11

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

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:避免单击列来编辑该对象
  • has_add_permission() 返回False避免为该模型创建新对象

1
这禁止打开任何实例来查看字段,但是,如果只列出列表就可以了,那么它就可以工作。
塞巴斯蒂安Vansteenkiste

8

它已添加到18/1/18发布的Django 2.1中!

ModelAdmin.has_view_permission()就像现有的has_delete_permission,has_change_permission和has_add_permission一样。您可以在这里的文档中阅读

从发行说明中:

这允许授予用户对管理员中模型的只读访问权限。ModelAdmin.has_view_permission()是新的。该实现是向后兼容的,因为无需分配“查看”权限即可允许具有“更改”权限的用户编辑对象。


超级用户仍然可以在管理界面中修改对象,对吗?
Flimm

这是正确的,除非您重写这些方法之一来更改行为以禁止超级用户访问。
grrrrrr

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

5

编译@darklow和@josir的出色答案,再加上一些内容以删除“ Save”和“ Save and Continue”按钮,将导致(使用Python 3语法):

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

然后你用像

class MyModelAdmin(ReadOnlyAdmin):
    pass

我只在Django 1.11 / Python 3中尝试过此操作。


自从我使用Django以来已经很长时间了。其他人可以担保吗?
史蒂夫·本内特

@SteveBennettㄹ此要求满足的要求有很多不同...这个答案不是水密的...在这里建议解释:stackoverflow.com/a/36019597/2586761和您在stackoverflow.com上评论的答案/ a / 33543817/2586761比接受的答案更完整
ptim

3

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

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

3

在Django 2.2中,我这样做是这样的:

@admin.register(MyModel)
class MyAdmin(admin.ModelAdmin):
    readonly_fields = ('all', 'the', 'necessary', 'fields')
    actions = None # Removes the default delete action in list view

    def has_add_permission(self, request):
        return False

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

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

使用django 2.2,则不需要readonly_fieldsactions
cheng10

3

使用django 2.2,只读管理员可以很简单:

class ReadOnlyAdminMixin():
    def has_add_permission(self, request):
        return False

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

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


class LogEntryAdmin(ReadOnlyAdminMixin, admin.ModelAdmin):
    list_display = ('id', 'user', 'action_flag', 'content_type', 'object_repr')

1

当需要使django admin中的某些用户的所有字段均为只读时,我遇到了相同的要求,最终利用了django模块“ django-admin-view-permission”而又没有滚动自己的代码。如果需要更精细的控制来显式定义哪些字段,则需要扩展模块。您可以在此处查看实际使用的插件


0

只读=>查看权限

  1. pipenv install django-admin-view-permission
  2. 在settings.py中将“ admin_view_permission”添加到INSTALLED_APPS中,如下所示:ʻINSTALLED_APPS = ['admin_view_permission',
  3. python manage.py迁移
  4. python manage.py运行服务器 6666

ok。拥有“视图”权限的乐趣


0

我已经编写了一个通用类来根据用户权限(包括内联;)处理ReadOnly视图。

在models.py中:

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中:

# 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中的类:

class ContactAdmin(ReadOnlyAdmin):
    list_display = ("name","email","whatever")
    readonly_fields = ("updated","created")
    inlines = ( PhoneInline, ... )
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.