How can I make a model completely read-only in the admin interface? It\'s for a kind of log table, where I\'m using the admin features to search, sort, filter etc, but there
I have written a generic class to handle ReadOnly view depending on User permissions, including inlines ;)
In 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
In 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 )
Then, we can just inherit normally our classes in admin.py:
class ContactAdmin(ReadOnlyAdmin):
list_display = ("name","email","whatever")
readonly_fields = ("updated","created")
inlines = ( PhoneInline, ... )
with django 2.2, readonly admin can be as simple as:
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')
pipenv install django-admin-view-permission
6666
This was added in to Django 2.1 which was released on 8/1/18!
ModelAdmin.has_view_permission()
is just like the existing has_delete_permission, has_change_permission and has_add_permission. You can read about it in the docs here
From the release notes:
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.
Compiling @darklow and @josir 's excellent answers, plus adding a bit more to remove "Save" and "Save and Continue" buttons leads to (in Python 3 syntax):
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
and then you use like
class MyModelAdmin(ReadOnlyAdmin):
pass
I've only tried this with Django 1.11 / Python 3.
Actually you can try this simple solution:
class ReadOnlyModelAdmin(admin.ModelAdmin):
actions = None
list_display_links = None
# more stuff here
def has_add_permission(self, request):
return False
actions = None
: avoids showing the dropdown with the "Delete selected ..." optionlist_display_links = None
: avoids clicking in columns to edit that objecthas_add_permission()
returning False avoids creating new objects for that model