Django’s authentication and authorization system is one of its strongest features. It provides a complete framework for managing users, authentication, and permissions, giving developers full control over what each user can do within an application. One of the key parts of this system is the permission-based access control, along with groups that allow you to organize and manage permissions more efficiently.
In this post, we’ll go deep into how Django’s permissions work, how you can define custom permissions, how to use groups to manage roles, and how to check permissions in both views and templates. We will also explore how you can extend this system to create role-based access control (RBAC) for real-world applications.
Table of Contents
- Introduction to Django’s Permission System
- The Default Permissions in Django
- Assigning Permissions to Users
- Creating and Managing Custom Permissions
- Using Groups to Bundle Permissions
- Assigning Groups to Users
- Checking Permissions in Views
- Checking Permissions in Templates
- Using the
User
andGroup
Models - Working with Django Admin Permissions
- Implementing Role-Based Access Control (RBAC)
- Practical Example: Building a Blog with Editors and Authors
- Managing Permissions Programmatically
- Extending Django’s Default Permission System
- Best Practices for Permissions and Groups
- Common Pitfalls and How to Avoid Them
- Conclusion
1. Introduction to Django’s Permission System
In most web applications, not every user should have the same level of access. For instance, in a blogging platform, you might have authors who can create and edit their own posts, editors who can review and approve articles, and administrators who can manage everything.
Django’s permission system helps implement this kind of access control easily. Every user can be assigned permissions that determine what actions they can perform. Permissions can be attached directly to users or through groups.
Django’s permission system works hand in hand with its authentication framework (django.contrib.auth
). This framework provides models and utilities for managing users, groups, and permissions.
2. The Default Permissions in Django
When you create a Django model, Django automatically creates three permissions for it:
add_<modelname>
– allows the user to add instances of the model.change_<modelname>
– allows the user to modify instances of the model.delete_<modelname>
– allows the user to delete instances of the model.
If you have a model named Article
inside an app called blog
, Django automatically creates:
blog.add_article
blog.change_article
blog.delete_article
You can check these permissions in the database under the auth_permission
table. This table stores all permission codes and their corresponding names.
You can also create custom permissions if the default three are not enough — for example, publish_article
.
3. Assigning Permissions to Users
Each user in Django has a field called user_permissions
, which is a many-to-many relationship with the Permission
model. You can assign permissions directly to users in several ways.
Example 1: Assign Permissions in the Admin
In the Django admin interface, open the user’s page and scroll to the “User permissions” section. From there, you can select which permissions to assign.
Example 2: Assign Permissions in Code
You can also assign permissions programmatically using Django’s ORM.
from django.contrib.auth.models import User, Permission
from django.contrib.contenttypes.models import ContentType
from blog.models import Article
# Get the permission
content_type = ContentType.objects.get_for_model(Article)
permission = Permission.objects.get(
codename='change_article',
content_type=content_type,
)
# Get the user
user = User.objects.get(username='john')
# Add permission to the user
user.user_permissions.add(permission)
Now, the user john
has permission to change articles.
4. Creating and Managing Custom Permissions
Default permissions cover basic CRUD operations, but many applications require more specific permissions.
You can define custom permissions in your model’s Meta
class.
class Article(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
permissions = [
("publish_article", "Can publish article"),
("view_unpublished_article", "Can view unpublished article"),
]
After defining custom permissions, run python manage.py makemigrations
and python manage.py migrate
. Django will create these new permissions in the database.
You can then assign them to users or groups in the same way as default permissions.
5. Using Groups to Bundle Permissions
Managing permissions for many users individually can quickly become complex. That’s where groups come in.
A group is a collection of permissions. When you assign a group to a user, the user automatically gains all permissions that belong to that group.
This is extremely useful for defining roles like Admin, Editor, or Author. Instead of assigning ten permissions to each editor, you just assign them to a group called “Editor” and then add users to that group.
6. Assigning Groups to Users
You can create and assign groups either in the Django admin or programmatically.
In the Admin
- Go to the “Groups” section in the admin panel.
- Create a new group (e.g., “Editors”).
- Add the desired permissions to this group.
- Then open a user’s page and assign the user to the “Editors” group.
In Code
from django.contrib.auth.models import Group, Permission
# Create a group
editors = Group.objects.create(name='Editors')
# Add permissions to the group
permission = Permission.objects.get(codename='change_article')
editors.permissions.add(permission)
# Add a user to the group
user = User.objects.get(username='alice')
user.groups.add(editors)
Now, the user alice
automatically has the change_article
permission because she belongs to the Editors group.
7. Checking Permissions in Views
Django provides several ways to check if a user has a certain permission.
Using user.has_perm()
You can check permissions directly using this method.
if request.user.has_perm('blog.change_article'):
# User can change articles
...
else:
# Permission denied
...
Using @permission_required
Decorator
You can also use the permission_required
decorator to protect views.
from django.contrib.auth.decorators import permission_required
@permission_required('blog.add_article')
def create_article(request):
# Only users with add_article permission can access this view
...
If a user without the required permission tries to access this view, they will be redirected to the login page by default.
You can also set raise_exception=True
to raise a PermissionDenied
error instead of redirecting.
8. Checking Permissions in Templates
You can check permissions directly in templates using the perms
template context variable.
{% if perms.blog.add_article %}
<a href="{% url 'article_add' %}">Add Article</a>
{% endif %}
This allows you to show or hide UI elements depending on the user’s permissions.
9. Using the User
and Group
Models
The main models for managing users and groups are:
User
– represents individual users.Group
– represents roles or permission bundles.Permission
– represents specific actions users can perform.
Django’s User
model automatically includes relationships to Group
and Permission
:
user.groups
– groups the user belongs to.user.user_permissions
– permissions assigned directly to the user.
You can access them like this:
user = User.objects.get(username='bob')
user.groups.all() # List of groups
user.user_permissions.all() # List of specific permissions
10. Working with Django Admin Permissions
Django admin respects the same permission system.
Users can access specific models or actions in the admin only if they have the correct permissions.
For example:
- To view a model in admin, the user must have
view_modelname
permission. - To add an object, they must have
add_modelname
. - To change or delete objects, they need
change_modelname
ordelete_modelname
.
You can control this at a granular level by assigning or revoking permissions in the admin interface.
11. Implementing Role-Based Access Control (RBAC)
Role-Based Access Control (RBAC) is a pattern where permissions are not assigned directly to users, but through roles (implemented as groups in Django).
For example:
Role | Permissions |
---|---|
Admin | add, change, delete any model |
Editor | change and publish articles |
Author | add and edit own articles |
This structure makes it easy to manage access because you only need to modify the group’s permissions when roles change.
12. Practical Example: Building a Blog with Editors and Authors
Let’s walk through a practical example to demonstrate how permissions and groups can be applied.
Step 1: Define Models and Custom Permissions
class Article(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
published = models.BooleanField(default=False)
class Meta:
permissions = [
('publish_article', 'Can publish article'),
]
Step 2: Create Groups
- Admins: All permissions.
- Editors:
change_article
,publish_article
. - Authors:
add_article
,change_article
.
Step 3: Assign Groups
admin_group = Group.objects.create(name='Admins')
editor_group = Group.objects.create(name='Editors')
author_group = Group.objects.create(name='Authors')
# Assign permissions
admin_group.permissions.set(Permission.objects.all())
editor_group.permissions.set(Permission.objects.filter(codename__in=['change_article', 'publish_article']))
author_group.permissions.set(Permission.objects.filter(codename__in=['add_article', 'change_article']))
Step 4: Restrict Views Based on Permissions
from django.contrib.auth.decorators import permission_required
@permission_required('blog.publish_article')
def publish_article(request, pk):
...
This ensures only editors or admins can publish articles.
13. Managing Permissions Programmatically
You can dynamically create, assign, and revoke permissions using Django’s ORM.
Create a Permission
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from blog.models import Article
content_type = ContentType.objects.get_for_model(Article)
permission = Permission.objects.create(
codename='archive_article',
name='Can archive article',
content_type=content_type,
)
Revoke a Permission
user.user_permissions.remove(permission)
Clear All Permissions from a User
user.user_permissions.clear()
14. Extending Django’s Default Permission System
You can extend Django’s permission system to suit your needs.
Object-Level Permissions
By default, Django permissions apply at the model level. For example, if a user has change_article
, they can change any article.
However, sometimes you want to restrict users to only modify their own objects.
For this, you can use third-party packages like django-guardian, which supports object-level permissions.
Example:
from guardian.shortcuts import assign_perm
assign_perm('change_article', user, obj)
Now the permission applies only to that specific object.
15. Best Practices for Permissions and Groups
- Use Groups for Roles – Always use groups to manage common permission sets.
- Keep Custom Permissions Descriptive – Use clear, consistent names.
- Avoid Hardcoding Permissions in Code – Store permission checks in decorators or centralized utilities.
- Document Role Responsibilities – Maintain documentation describing what each group can do.
- Use the Admin for Simple Permission Management – For small teams, the Django admin is sufficient.
- Use Object-Level Permissions When Necessary – For large systems, consider django-guardian or similar tools.
- Regularly Audit Permissions – Periodically verify that roles and permissions are aligned with your current requirements.
16. Common Pitfalls and How to Avoid Them
- Forgetting to Migrate After Adding Custom Permissions
Always run migrations after modifying yourpermissions
list in a model’sMeta
. - Assigning Permissions to the Wrong App
Double-check your permission code format:app_label.permission_codename
. - Not Checking Permissions in Templates
Even if you restrict backend access, also hide restricted UI elements using theperms
template variable. - Overusing Direct Permissions
Assigning permissions directly to users leads to clutter. Use groups instead. - Ignoring Object-Level Needs
If users should only edit their own data, implement object-level checks in views.
Leave a Reply