Readonly models in Django admin interface? Readonly models in Django admin interface? django django

Readonly models in Django admin interface?


The admin is for editing, not just viewing (you won't find a "view" permission). In order to achieve what you want you'll have to forbid adding, deleting, and make all fields readonly:

class MyAdmin(ModelAdmin):    def has_add_permission(self, request, obj=None):        return False    def has_delete_permission(self, request, obj=None):        return False

(if you forbid changing you won't even get to see the objects)

For some untested code that tries to automate setting all fields read-only see my answer to Whole model as read-only

EDIT: also untested but just had a look at my LogEntryAdmin and it has

readonly_fields = MyModel._meta.get_all_field_names()

Don't know if that will work in all cases.

EDIT: QuerySet.delete() may still bulk delete objects. To get around this, provide your own "objects" manager and corresponding QuerySet subclass which doesn't delete - see Overriding QuerySet.delete() in Django


Here are two classes I am using to make a model and/or it's inlines read only.

For model admin:

from django.contrib import adminclass 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 Falseclass MyModelAdmin(ReadOnlyAdmin):    pass

For inlines:

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 Falseclass MyInline(ReadOnlyTabularInline):    pass


See https://djangosnippets.org/snippets/10539/

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 = [f.name for f in self.model._meta.get_fields()]    def get_actions(self, request):        actions = super(ReadOnlyAdminMixin, self).get_actions(request)        del_action = "delete_selected"        if del_action in actions:            del actions[del_action]        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

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

templates/admin/view.html (for Grappelli)

{% 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"><h1>{% trans "submit options"|capfirst context "heading" %}</h1></header>    <ul>       <li><a href="../" class="grp-button grp-default">{% blocktrans %}Back to list{% endblocktrans %}</a></li>    </ul>  </footer>{% endblock %}