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发布!
从发行说明:
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, ... ) |
号
只读=>查看权限
好的。在"视图"许可下玩吧。