Whole model as read-only
ModelAdmin provides the hook get_readonly_fields() - the following is untested, my idea being to determine all fields the way ModelAdmin does it, without running into a recursion with the readonly fields themselves:
from django.contrib.admin.util import flatten_fieldsetsclass ReadOnlyAdmin(ModelAdmin): def get_readonly_fields(self, request, obj=None): if self.declared_fieldsets: fields = flatten_fieldsets(self.declared_fieldsets) else: form = self.get_formset(request, obj).form fields = form.base_fields.keys() return fields
then subclass/mixin this admin whereever it should be a read-only admin.
For add/delete, and to make their buttons disappear, you'll probably also want to add
def has_add_permission(self, request): # Nobody is allowed to add return False def has_delete_permission(self, request, obj=None): # Nobody is allowed to delete return False
P.S.: In ModelAdmin, if has_change_permission (lookup or your override) returns False, you don't get to the change view of an object - and the link to it won't even be shown. It would actually be cool if it did, and the default get_readonly_fields() checked the change permission and set all fields to readonly in that case, like above. That way non-changers could at least browse the data... given that the current admin structure assumes view=edit, as jathanism points out, this would probably require the introduction of a "view" permission on top of add/change/delete...
EDIT: regarding setting all fields readonly, also untested but looking promising:
readonly_fields = MyModel._meta.get_all_field_names()
EDIT: Here's another one
if self.declared_fieldsets: return flatten_fieldsets(self.declared_fieldsets)else: return list(set( [field.name for field in self.opts.local_fields] + [field.name for field in self.opts.local_many_to_many] ))
As "view permissions" will not make it into Django 1.11, unfortunately, here's a solution that makes your ModelAdmin read-only by making both saving model changes and adding model history log entries a no-op.
def false(*args, **kwargs): """A simple no-op function to make our changes below readable.""" return Falseclass MyModelReadOnlyAdmin(admin.ModelAdmin): list_display = [ # list your admin listview entries here (as usual) ] readonly_fields = [ # list your read-only fields here (as usual) ] actions = None has_add_permission = false has_delete_permission = false log_change = false message_user = false save_model = false
(NOTE: Don't mistake the false
no-op helper with the False
builtin. If you don't sympathize with the helper function outside the class move it into the class, call it no_op
or something else, or override the affected attributes by usual def
s. Less DRY, but if you don't care...)
This will:
- remove the actions drop-down box (with "delete") in the list view
- disallow adding new model entries
- disallow deleting existing model entries
- avoid creating log entries in the model history
- avoid displaying "was changed successfully" messages after saving
- avoid saving changeform changes to the database
It will not:
- remove or replace the two buttons "Save and continue editing" and "SAVE" (which would be nice to improve the user experience)
Note that get_all_field_names
(as mentioned in the accepted answer) was removed in Django 1.10. Tested with Django 1.10.5.
You may customize your ModelAdmin
classes with the readonly_fields
attribute. See this answer for more.