Django admin CRUD permissions
By default, the Django admin allows access to users with superuser and staff permissions -- in case you've never heard of the terms Django superuser, Django staff or Django permissions, see the previous chapter which describes Django user management.
A Django superuser, is its name implies, means it's a user with 'super' permissions. By extension, this means a superuser has access to any page in the Django admin, as well as permissions to Create, Read, Update and Delete any type of model record available in the Django admin. Because Django superusers represent an 'all or nothing' proposition, the Django admin is also designed to allow access to Django staff users.
Any Django user marked as staff is given access to the Django admin, but nothing else, unless explicitly given permissions, as illustrated in figure 11-47.
Figure 11-47. Django admin main page for staff user with no permissions
As you can see in figure 11-47,
the main Django admin page is empty. Although this scenario of an
empty Django admin main page can also present itself when you have
no registered models in a project's admin.py
files,
this case represents a scenario for a staff user with no explicit
model permissions.
In order for staff users to gain access to Django admin pages, they must be given explicit permissions by means of model permissions, given Django admin pages operate on the basis of CRUD model actions (e.g. a Django admin page to create model records, a Django admin page to delete model records).
By default, all Django models are
given add
, change
and delete
permissions, which you can assign to staff users. As a consequence,
each of these model permissions represents an access permission for
a Django admin page.
For example, if a staff user is
given the delete
permission on a model, it means he's
also given access to delete records of said model in the Django
admin. Similarly, if a staff user is given the add
permission on a model, it means he's given access to the create
record page of said model in the Django admin. Finally, if a staff
user is given the change
permission on a model, it
means he's given access to the edit record page of said model in
the Django admin.
Note Granting adelete
permission to a user on a given model, also requires granting thechange
permission to fulfill the delete action in the Django admin. This is because the Django admin delete action is available on the Django admin record change page.
As you can deduce from this behavior, by using staff users and model permissions, you can allow very fine grained access to different sections of the Django admin, instead of the 'all or nothing' Django admin superuser behavior.
Still, Django admin staff users
have one important missing behavior: the ability to allow read only
access for model records in the Django admin. Because Django models
default to having add
, change
and
delete
permissions (i.e. CUD [Create,Update, Delete]
behaviors), a read
permission is deemed implicit with
the presence of change
(i.e. if you're able to change
a record, then you're able to read it). Therefore, to achieve a
standalone read-only permission in the Django admin, you must add a
custom model read permission (i.e. the missing R in CRUD).
The previous chapter describes custom model permissions in greater detail, but I'll describe this process in listing 11-25 for the context of the Django admin by adding a read only permission.
Listing 11-25. Django model with custom read permission and Django admin class enforcing read permission.
# models.py from django.db import models class Menu(models.Model): name = models.CharField(max_length=30) creator = models.CharField(max_length=100,default='Coffeehouse Chef') class Item(models.Model): menu = models.ForeignKey(Menu, on_delete=models.CASCADE) name = models.CharField(max_length=30) description = models.CharField(max_length=100) class Meta: permissions = ( ('read_item','Can read item'), ) # admin.py from django.contrib import admin from coffeehouse.items.models import Item class ItemAdmin(admin.ModelAdmin): list_per_page = 5 list_display = ['menu','name','menu_creator'] def get_readonly_fields(self, request, obj=None): if not request.user.is_superuser and request.user.has_perm('items.read_item'): return [f.name for f in self.model._meta.fields] return super(ItemAdmin, self).get_readonly_fields( request, obj=obj ) admin.site.register(Item, ItemAdmin)
The first step highlighted in
listing 11-25 is the Item
model with a custom
permission named read_item
with the friendly name
'Can read item'
. After you run the Item
model in listing 11-25 through its corresponding migration, the
Item
model will get a custom read_item
permission. Next, create a staff user and assign it both the
read_item
and built-in change
permission
of Item
model. Once a staff user is given these
permissions, you must enforce the Django admin class for the
Item
model only allow read access to users with these
permissions.
When a user is given the
change
permission on a model, the Django admin grants
a user access to the Django admin form page, which is used to
update records of a given model and is shown from figures 11-23 to
11-44. But since you want to restrict the update functionality to
read only, you must set this page's form fields to read only, which
is the purpose of the get_readonly_fields()
method in
the second part of the listing 11-25.
By defining an admin class with a
custom get_readonly_fields()
method, you can tell the
Django admin under which circumstances you want to set a Django
admin page's form fields to read only. In this case, you can see
the logic of the get_readonly_fields()
method in
listing 11-25 uses the is_superuser()
and
has_perm()
methods the determine if the calling party
is not a superuser (i.e. staff) and if the user has the
read_item
permission on the Item
model.
If this last rule is true, then the
get_readonly_fields()
method sets all the model form
fields to read-only, which is the whole purpose the
get_readonly_fields()
method. If this last rule is
false, then the get_readonly_fields()
method returns
its default behavior calling the parent class's default
get_readonly_fields()
method.
As you can see with this brief exercise, by using custom Django admin class methods in conjunction with standard and custom Django model permissions, there are no limitations to restricting or allowing CRUD operations in the Django admin.