Built-In Permission Classes in Django REST Framework
Built-In Permission Classes in Django REST Framework
This article looks at how the built-in permission classes work in Django REST Framework (DRF).
Objectives
By the end of this article, you should be able to:
1. Explain the differences between the seven built-in permission classes in DRF
2. Set permissions on a specific model and object
3. Use the built-in permission classes to set a global permission policy
Built-in Classes
Although you can create your own permission classes, DRF comes with seven built-in classes intended
to make your life easier:
Using them is as simple as including the class in the permission_classes list of a specific API View.
They stretch from entirely open (AllowAny) to access granted only to admin users (IsAdminUser).
With very little additional work, you can use them to implement fine-grained access control -- either on a
model or at the object level. You can also set permissions globally, for all API endpoints.
All of those classes, except the last one, DjangoObjectPermissions, override just the
has_permission method and inherits the has_object_permission from the BasePermission
class. has_object_permission in the BasePermission class always returns True, so it has no
impact on object-level access restriction:
AllowAny
1/14
The most open permission of all is AllowAny. The has_permission and has_object_permission
methods on AllowAny always return True without checking anything. Using it isn't necessary (by not
setting the permission class, you implicitly set this one), but you still should since it makes the intent
explicit and helps to maintain consistency throughout the app.
class MessageViewSet(viewsets.ModelViewSet):
queryset = Message.objects.all()
serializer_class = MessageSerializer
Anyone, even unauthenticated users, can access the API endpoint using any HTTP request method:
IsAuthenticated
IsAuthenticated checks if the request has a user and if that user is authenticated. Setting
permission_classes to IsAuthenticated means that only authenticated users will be able to
2/14
access the API endpoint with any of the request methods.
class MessageViewSet(viewsets.ModelViewSet):
queryset = Message.objects.all()
serializer_class = MessageSerializer
IsAuthenticatedOrReadOnly
When permissions are set to IsAuthenticatedOrReadOnly, the request must either have an
authenticated user or use one of the safe/read-only HTTP request methods (GET, HEAD, OPTIONS).
This means that every user will be able to see all the messages, but only logged-in users will be able to
add, change, or delete objects.
class MessageViewSet(viewsets.ModelViewSet):
3/14
permission_classes = [IsAuthenticatedOrReadOnly] # ReadOnly added
queryset = Message.objects.all()
serializer_class = MessageSerializer
An unauthenticated user can view the message that was posted by an authenticated user, but they can't
do anything with it or add their own:
IsAdminUser
Permissions set to IsAdminUser means that the request needs to have a user and that user must have
is_staff set to True. This means that only admin users can see, add, change, or delete objects.
class MessageViewSet(viewsets.ModelViewSet):
queryset = Message.objects.all()
serializer_class = MessageSerializer
The interesting part here is that unauthenticated users and authenticated users without admin access will
get different errors.
4/14
Meanwhile, for an authenticated user without admin access, a PermissionDenied exception is raised:
DjangoModelPermissions
DjangoModelPermissions allows us to set any combination of permissions to each of the users
separately. The permission then checks if the user is authenticated and if they have add, change, or
delete user permissions on the model.
class MessageViewSet(viewsets.ModelViewSet):
permission_classes = [DjangoModelPermissions]
queryset = Message.objects.all()
serializer_class = MessageSerializer
5/14
Unlike with other permissions, this is not the end of setting the permission. You need to set the
permissions for the specific user:
If you check the single view for the post, you can see that this specific user can edit the post, but can't
delete it:
DjangoModelPermissions doesn't necessarily need to be assigned to a single user. You can use it for
group permissions also. Here's a group that's allowed to delete the message:
6/14
And you can see here, that the member of that group can delete the message:
For example, the CreateAPIView generic view doesn't require a queryset, so if you set
DjangoModelPermissions on it you'll get an assertion error. However, if you perform the
querying, even though you're not using the queryset, DjangoModelPermissions will work:
class NewMessage(generics.CreateAPIView):
queryset = Message.objects.all()
permission_classes = [DjangoModelPermissions]
serializer_class = MessageSerializer
DjangoModelPermissionsOrAnonReadOnly
DjangoModelPermissionsOrAnonReadOnly extends the DjangoModelPermissions and only
changes one thing: It sets authenticated_users_only to False.
7/14
from rest_framework import viewsets
from rest_framework.permissions import DjangoModelPermissionsOrAnonReadOnly
class MessageViewSet(viewsets.ModelViewSet):
permission_classes = [DjangoModelPermissionsOrAnonReadOnly]
queryset = Message.objects.all()
serializer_class = MessageSerializer
Anonymous users can see the objects but can't interact with them:
DjangoObjectPermissions
While DjangoModelPermissions limits the user's permission for interacting with a model (all the
instances), DjangoObjectPermissions limits the interaction to a single instance of the model (an
object). To use DjangoObjectPermissions you'll need a permission backend that supports object-
level permissions. We'll look at django-guardian.
While there are quite a few packages that cover Django permissions, DRF explicitly mentions
django-guardian, which is why we're working with it in this article.
1. drf-extensions
2. django-oso
3. django-rules
4. django-role-permissions
8/14
Installing django-guardian
# settings.py
INSTALLED_APPS = [
# ...
'rest_framework',
'guardian',
]
# ...
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'guardian.backends.ObjectPermissionBackend',
)
If you want to see the permissions for a single object, you need to use GuardedModelAdmin instead of
ModelAdmin. You can do that in an admin.py file like so:
# admin.py
class MessageAdmin(GuardedModelAdmin):
pass
admin.site.register(Message, MessageAdmin)
Now, if you open a single object in the admin panel, on the top right there's a new button called "OBJECT
PERMISSIONS". Upon clicking on it, the object permissions panel opens:
9/14
Using DjangoObjectPermissions
Now knowing the difference between has_permission and has_object_permission will come in
handy. In short, DRF first checks if the request has permission to access the model. If it doesn't, DRF
doesn't care about the object-level permissions.
10/14
That means that the user must have model permission if you want the object-level permission to be
checked. A good use case for object-level permissions is for only allowing the owner of an object to
change or delete it. Here's a view that allows only the creator of the object to delete it:
# views.py
class MessageViewSet(viewsets.ModelViewSet):
queryset = Message.objects.all()
serializer_class = MessageSerializer
assign_perm is a django-guardian function used for assigning permissions to certain users or groups. It
takes three arguments:
1. permission: delete_message
2. user_or_group: self.request.user
3. object (defaults to None): instance
Again, for the object permission to work, the user must have model-level permission for the
corresponding model. Let's say you have two users with the same permissions on the model:
11/14
And you use the code above to assign permission only to the creator. user_1 -- the creator of the object --
can delete it, but user_2 can't delete it even though they have model-level permissions:
If we remove the permission to delete any object on the model for user_1:
12/14
Even though they have the permission to delete this particular object:
Global Permissions
You can easily set global permission in your settings.py file, using built-in permission classes. For
example:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
]
}
DEFAULT_PERMISSION_CLASSES will only work for the views or objects that don't have permissions
explicitly set.
You don't necessarily need to use built-in classes here. You can use your own custom
classes as well.
Conclusion
That's it. You should now know how to use Django REST Framework's seven built-in permission classes.
They vary from totally open (AllowAny) to mostly closed (IsAdminUser).
13/14
You can set permissions globally, on the model (DjangoModelPermissions), or on a single object
(DjangoObjectPermissions). There are also classes that allow you to limit "unsafe" HTTP methods,
but allow anyone the safe ones (IsAuthenticatedOrReadOnly,
DjangoModelPermissionsOrAnonReadOnly).
If you don't have any specific requirements, the built-in classes should suffice for most cases. On the
other hand, if you have some special requirements, you should build your own permission class.
14/14