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 ran into the same requirement when needing to make all fields readonly for certain users in django admin ended up leveraging on django module "django-admin-view-permission" without rolling my own code. If you need more fine grained control to explicitly define which fields then you would need to extend the module. You can check out the plugin in action here
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 = self.model._meta.get_all_field_names()
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 %}
With Django 2.2 I do it like this:
@admin.register(MyModel)
class MyAdmin(admin.ModelAdmin):
readonly_fields = ('all', 'the', 'necessary', 'fields')
actions = None # Removes the default delete action in list view
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
The accepted answer should work, but this will also preserve the display order of the readonly fields. You also don't have to hardcode the model with this solution.
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
If the accepted answer doesn't work for you, try this:
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
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 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
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 False
class MyInline(ReadOnlyTabularInline):
pass