How To Get Readonly Mode In Django

23 Feb 2017

Last week, I came across an interesting problem at work. The problem was to get read only users in a Django based application.

But doing so was not very simple because there is no read only mode for users
in Django. In order to solve this, I first started reading answers on stack
overflow. Some of those links did pointed me to a correct route. Here, I will
document the whole process so that it could help others and serve as a reminder
for me as well.

First of all, the whole system of authentication in any system originates from
permissions. The basis permissions are. Read, Write, Execute. in Unix (chmod is
used to set permissions).

Django has a cool way of adding the permissions in the meta class. Let’s say we
have a model class named Cars.

Just like this, Any permission could be added to the Model. Now in order to get
these permissions in the database. You need to run migrate management command.

python manage.py migrate

So this just sets the background in place. The real job is getting this
permissions to work.

Now, I began to wonder what all things were required to develop this complete
functionality ?

The first thing that came to my mind naturally was to override templates and
hack ground admin.py.

So, I created a new class in Admin.py that was inherited from the admin class
I was earlier using:

frommodelimportCarsclassCarAdmin(admin.ModelAdmin):date_hierarchy='date'list_filter=('status','event_instance',)actions=['accept','reject','pending']classReadonlyCarAdmin(CarAdmin):def__init__(self,model,admin_site):super(ReadonlyCarAdmin,self).__init__(model,admin_site)self.model=modeldefhas_delete_permission(self,request,obj=None):ifrequest.user.has_perm('car.readonly')andnotrequest.user.is_superuser:returnFalseelse:returnTruedefhas_add_permission(self,request,obj=None):ifrequest.user.has_perm('car.readonly')andnotrequest.user.is_superuser:returnFalseelse:returnTruedefhas_change_permission(self,request,obj=None):ifrequest.user.is_superuser:self.readonly_fields=()# make sure to remove caching.
returnTrueelifrequest.user.has_perm('car.readonly'):# make the fields readonly for only users with readonly permissions.
self.readonly_fields=[field.nameforfieldinfilter(lambdaf:notf.auto_created,self.model._meta.fields)]returnTrueelse:returnFalsedefget_actions(self,request):actions=super(ReadOnlyCarAdmin,self).get_actions(request)ifrequest.user.has_perm('car.readonly')andnotrequest.user.is_superuser:# This ensures that that user doesn't not have any actions
if'delete_selected'inactions:delactions['delete_selected']delactions['accept']delactions['reject']delactions['pending']returnactionselse:returnactionsadmin_site.register(Car,ReadonlyCarAdmin)

Okay, so now we have a robust system in place to ensure whichever user has
readonly permission on Cars Model would only be able to see the model data in
Readonly mode.

But this is not it. Here is the part where templates are overridden.

First of all save and cancel button on buttom needs to go as we don’t need
them.

For that, create a new template in templates folder.The templates name is ` change_form.html.
Hence the full pathname is carapp/templates/admin/change_form.html`.

Copy the content from the default django template (/admin/change_form.html) in django project
add replace this content with this gist:

This will ensure that the selected user with this readonly only permissions won’t be able
get the submit button on his admin page.

Now, you have a fully functioning Django Admin with readonly mode. The main effort was to make it
very easy to use and ensure that this feature could run across future versions.

Hope this post help anyone else who has to implement something similar.