Whole model as read-only Whole model as read-only django django

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 defs. Less DRY, but if you don't care...)

This will:

  1. remove the actions drop-down box (with "delete") in the list view
  2. disallow adding new model entries
  3. disallow deleting existing model entries
  4. avoid creating log entries in the model history
  5. avoid displaying "was changed successfully" messages after saving
  6. 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.