24👍
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 %}
77👍
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: This makes all fields read-only:
readonly_fields = [field.name for field in MyModel._meta.get_fields()]
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
- [Django]-Django admin default filter
- [Django]-Good ways to sort a queryset? – Django
- [Django]-How to change site title, site header and index title in Django Admin?
57👍
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
- [Django]-What are the limitations of Django's ORM?
- [Django]-Use Python standard logging in Celery
- [Django]-Django-DB-Migrations: cannot ALTER TABLE because it has pending trigger events
15👍
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')
- [Django]-How to limit the maximum value of a numeric field in a Django model?
- [Django]-How to change User representation in Django Admin when used as Foreign Key?
- [Django]-Default value for user ForeignKey with Django admin
13👍
If you want the user become aware that he/she cannot edit it, 2 pieces are missing on the first solution. You have remove the delete action!
class MyAdmin(ModelAdmin)
def has_add_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
def get_actions(self, request):
actions = super(MyAdmin, self).get_actions(request)
if 'delete_selected' in actions:
del actions['delete_selected']
return actions
Second: the readonly solution works fine on plain models. But it does NOT work if you have an inherited model with foreign keys. Unfortunately, I don’t know the solution for that yet. A good attempt is:
But it does not work for me either.
And a final note, if you want to think on a broad solution, you have to enforce that each inline has to be readonly too.
- [Django]-Invalid http_host header
- [Django]-Filter Queryset on empty ImageField
- [Django]-Change a form value before validation in Django form
11👍
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
- [Django]-Django get the static files URL in view
- [Django]-Using {% url ??? %} in django templates
- [Django]-Django: reverse accessors for foreign keys clashing
11👍
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.
- [Django]-Multiple ModelAdmins/views for same model in Django admin
- [Django]-How do I migrate a model out of one django app and into a new one?
- [Django]-Disable a method in a ViewSet, django-rest-framework
6👍
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
- [Django]-Python Socket.IO client for sending broadcast messages to TornadIO2 server
- [Django]-Negating a boolean in Django template
- [Django]-What's the best Django search app?
6👍
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.
- [Django]-Django: using more than one database with inspectdb?
- [Django]-Django: "projects" vs "apps"
- [Django]-You need to install postgresql-server-dev-X.Y for building a server-side extension or libpq-dev for building a client-side application
4👍
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
- [Django]-Django annotate count with a distinct field
- [Django]-How to solve "Page not found (404)" error in Django?
- [Django]-How can i test for an empty queryset in Django?
3👍
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
- [Django]-Django testing: Test the initial value of a form field
- [Django]-Django – limiting query results
- [Django]-Django: Fat models and skinny controllers?
1👍
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
- [Django]-How to change status of JsonResponse in Django
- [Django]-How to squash recent Django migrations?
- [Django]-Django Installed Apps Location
1👍
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, ... )
- [Django]-Django REST framework serializer without a model
- [Django]-Is it possible to pass query parameters via Django's {% url %} template tag?
- [Django]-Django Rest Framework model serializer with out unique together validation
0👍
read-only => views permission
pipenv install django-admin-view-permission
- add ‘admin_view_permission’ to INSTALLED_APPS in the settings.py.like this:
`INSTALLED_APPS = [
‘admin_view_permission’, - python manage.py migrate
- python manage.py runserver
6666
ok.have fun with the ‘views’ permission
- [Django]-How can I upgrade specific packages using pip and a requirements file?
- [Django]-How does django handle multiple memcached servers?
- [Django]-How to use subquery in django?