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

时间:2011-11-25 06:07:08

标签: django django-admin readonly

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

如果这看起来像重复,这里的不是我正在尝试做的事情:

  • 我不是在寻找只读字段(即使只读取每个字段仍然可以让你创建新记录)
  • 我不打算创建一个只读用户:每个用户都应该只读。

14 个答案:

答案 0 :(得分:65)

管理员用于编辑,而不仅仅是查看(您将找不到“查看”权限)。为了达到你想要的效果,你必须禁止添加,删除和使所有字段只读:

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 as read-only的回答

编辑:还没有经过测试,但只看了我的LogEntryAdmin,它有

readonly_fields = MyModel._meta.get_all_field_names()

不知道这是否适用于所有情况。

编辑:QuerySet.delete()仍然可以批量删除对象。要解决这个问题,请提供您自己的“对象”管理器和不删除的相应QuerySet子类 - 请参阅Overriding QuerySet.delete() in Django

答案 1 :(得分:41)

以下是我用来制作模型和/或内联只读的两个类。

对于模特管理员:

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

答案 2 :(得分:15)

请参阅https://djangosnippets.org/snippets/10539/

df = df.rename(columns={col: col.split('_')[0] for col in df.columns})

<强>模板/管理/ view.html

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

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

{% 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 %}

答案 3 :(得分:12)

如果您希望用户意识到他/她无法编辑它,则第一个解决方案中缺少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

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

Whole model as read-only

但它对我也不起作用。

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

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

答案 5 :(得分:4)

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

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

答案 6 :(得分:4)

编译@darklow和@josir的优秀答案,再添加一些内容以删除&#34; Save&#34;和&#34;保存并继续&#34;按钮导致(在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尝试过这个。

答案 7 :(得分:4)

此功能已添加到Django 2.1中,该版本于8/1/18发布!

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

中对其进行阅读

摘自发行说明:

  

这允许用户以只读方式访问管理员中的模型。   ModelAdmin.has_view_permission()是新的。实现是   向后兼容,因为不需要分配“视图”   允许具有“更改”权限的用户进行编辑的权限   对象。

答案 8 :(得分: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

答案 9 :(得分:2)

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

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

答案 10 :(得分:1)

当需要为django管理员中的某些用户准备所有字段时,我遇到了相同的要求,最终利用django模块“django-admin-view-permission”而没有滚动我自己的代码。如果您需要更精细的控制来明确定义哪些字段,那么您需要扩展模块。您可以查看操作中的插件here

答案 11 :(得分:1)

使用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')

答案 12 :(得分:0)

只读=&gt;查看权限

  1. pipenv install django-admin-view-permission
  2. 添加&#39; admin_view_permission&#39;在settings.py.like中的INSTALLED_APPS: `INSTALLED_APPS = [ &#39; admin_view_permission&#39;,
  3. python manage.py migrate
  4. python manage.py runserver 6666
  5. 好的。观看&#39;视图&#39;许可

答案 13 :(得分: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, ... )