Permissions in Django
Permissions in Django
io /blog/django-permissions/
Permissions in Django
Django comes with a powerful permission system out-of-the-box.
In this article, we'll look at how to assign permissions to users and groups in order to authorize them to
perform specific actions.
Objectives
By the end of this article, you will be able to:
Authentication vs Authorization
This article is concerned with authorization.
Put another way, authentication answers the question 'who are you?' while authorization answers 'what can
you do?'.
User-level Permissions
When django.contrib.auth is added to the INSTALLED_APPS setting in the settings.py file, Django
automatically creates add, change, delete and view permissions for each Django model that's created.
{app}.{action}_{model_name}
Notes:
app is the name of the Django app the associated model resides in
action: is add, change, delete, or view
model_name: is name of the model in lowercase
class Post(models.Model):
title = models.CharField(max_length=400)
body = models.TextField()
1. blog.add_post
2. blog.change_post
3. blog.delete_post
4. blog.view_post
You can then check if a user (via a Django user object) has permissions with the has_perm() method:
content_type = ContentType.objects.get_for_model(Post)
post_permission = Permission.objects.filter(content_type=content_type)
print([perm.codename for perm in post_permission])
# => ['add_post', 'change_post', 'delete_post', 'view_post']
# To add permissions
for perm in post_permission:
user.user_permissions.add(perm)
print(user.has_perm("blog.view_post"))
# => False
# Why? This is because Django's permissions do not take
# effect until you allocate a new instance of the user.
user = get_user_model().objects.get(email="[email protected]")
print(user.has_perm("blog.view_post"))
# => True
Superusers will always have permission set to True even if the permission does not exist:
superuser = User.objects.create_superuser(
username="super", password="test", email="[email protected]"
)
A superuser is a user type in Django that has every permission in the system. Whether custom
permissions or Django-created permissions, superusers have access to every permission.
A staff user is just like any other user in your system BUT with the added advantage of being
able to access the Django Admin interface. The Django Admin interface is only accessible to
superusers and staff users.
Group-level Permissions
Having to assign permissions each time to users is tedious and not scalable. There might be instances
where you would want to add new permissions to a set of users. Here's where Django groups come into
play.
What's a group?
With Django, you can create groups to class users and assign permissions to each group so when creating
users, you can just assign the user to a group and, in turn, the user has all the permissions from that group.
Code:
content_type = ContentType.objects.get_for_model(Post)
post_permission = Permission.objects.filter(content_type=content_type)
print([perm.codename for perm in post_permission])
# => ['add_post', 'change_post', 'delete_post', 'view_post']
user = User.objects.get(username="test")
user.groups.add(author_group) # Add the user to the Author group
To enforce permissions in class-based views, you can use the PermissionRequiredMixin from
django.contrib.auth.mixins like so:
@permission_required("blog.view_post")
def post_list_view(request):
return HttpResponse()
You can also check for permissions in your Django templates. With Django's auth context processors, a
perms variable is available by default when you render your template. The perms variable actually contains
all permissions in your Django application.
For example:
{% if perms.blog.view_post %}
{# Your content here #}
{% endif %}
Model-level Permissions
You can also add custom permissions to a Django model via the model Meta options.
class Post(models.Model):
title = models.CharField(max_length=400)
body = models.TextField()
is_published = models.Boolean(default=False)
class Post(models.Model):
title = models.CharField(max_length=400)
body = models.TextField()
is_published = models.Boolean(default=False)
class Meta:
permissions = [
(
"set_published_status",
"Can set the status of the post to either publish or not"
)
]
In order to enforce this permission, we can use the UserPassesTestMixin Django provided mixin in our
view, giving us the flexibility to explicitly check whether a user has the required permission or not.
Here's what a class-based view might look like that checks whether a user has permission to set the
published status of a post:
from django.contrib.auth.mixins import UserPassesTestMixin
from django.shortcuts import render
from django.views.generic import View
def test_func(self):
return self.request.user.has_perm("blog.set_published_status")
if post_id:
post = Post.objects.get(pk=post_id)
post.is_published = bool(published_status)
post.save()
So, with UserPassesTestMixin, you need to override the test_func method of the class and add your own
test. Do note that the return value of this method must always be a boolean.
Object-level Permissions
If you're using Django REST Framework, it already has object-level permissions built into the base
permission class. BasePermission has has_permission, which is basically for list views, and
has_object_permission, which checks if the user has permission to access a single model instance.
For more on permissions in Django REST Framework, review Permissions in Django REST
Framework.
If you're not using Django REST Framework, to implement object-level permissions, you can use a third-
party, like:
Django Guardian
Rules