Role-Based Access Control in Django: The right features to the right users

Subham Sarangi
3 min readAug 10, 2023

--

Role-based permissions are like the bouncers at a fancy party — they decide who gets access to the VIP lounge and who’s stuck in the coat-check area.

In this light-hearted tutorial, we’ll explore how to implement this in a Django application, enabling us to create modules, assign roles, and manage permissions effectively. Let’s begin.

Prerequisites:

  • Basic understanding of Python, Django and web development concepts.
  • A Django project set up and running.

1. Creating Models for Roles and Permissions

  • Defining the Module model.
class Module(models.Model):
name = models.CharField(max_length=50)
  • Creating the Permission model.
class Permission(models.Model):
name = models.CharField(max_length=50)
module = models.ForeignKey(Module, on_delete=models.CASCADE)
  • Designing the Role model.
class Role(models.Model):
name = models.CharField(max_length=50)
permissions = models.ManyToManyField(Permission)
  • Linking roles and permissions using the UserRole model.
class UserRole(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
role = models.ForeignKey(Role, on_delete=models.SET_NULL, null=True)

2. Defining Custom Decorators for Permissions

  • Understanding the role of decorators.

In Python, decorators are a powerful feature that allow us to modify the behavior of functions or methods. Apart from using for permission checking for role-based access control, they are commonly used in Django to add functionality to view functions, such as authentication, caching.

  • Creating the has_permission decorator.
from functools import wraps
from django.http import HttpResponseForbidden
from .models import *

def get_user_role(user):
user_role= UserRole.objects.get(user__id = self.id)
return user_role.role

def has_permission(perm_name):
def decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
if request.user.is_authenticated:
user_role = get_user_role(request.user)
if user_role and user_role.permissions.filter(name=perm_name).exists():
return view_func(request, *args, **kwargs)
return HttpResponseForbidden("You don't have permission to access this page.")
return _wrapped_view
return decorator
  • Time to bring in the bouncer!

Lets apply the decorator to our view functions.

# other imports
from .decorators import has_permission

@has_permission('retrive_job')
def job_list(request):
# ...
pass

@has_permission('update_project')
def project_edit(request):
# ...
pass

3. Managing User Roles and Assigning Roles

Using the django shell, lets create a user, role and a user role.

user = User.objects.get(username='username')
role = Role.objects.get(name='role_name')
user_role = UserRole.objects.create(user=user, role=role)

4. Creating Modules, Roles, and Permissions

We can use the django shell to create objects and link them together just to see if it works or not. Adding new modules and defining custom roles with specific permissions should be easy. Remember that each module, permission, and role adds a layer of fun and control.

# Create new modules
job_module= Module.objects.create(name="Job Management")
project_module= Module.objects.create(name="Project Management")

# Create permissions and associate them with modules like this
Permission.objects.create(
name="create_job",
module=job_module,
)
Permission.objects.create(
name="retrive_job",
module=job_module,
)
Permission.objects.create(
name="update_job",
module=job_module,
)
Permission.objects.create(
name="delete_job",
module=job_module,
)
Permission.objects.create(
name="create_project",
module=project_module,
)
Permission.objects.create(
name="retrive_project",
module=project_module,
)
...

# Define custom roles
role_admin = Role.objects.create(name="Admin")
role_emp = Role.objects.create(name="Employee")

# Get permissions (Note: replace these IDs with actual permission IDs)
perm_admin = Permission.objects.filter(id__in=[1, 2, 3, 4, 5, 6]) # all permissions
perm_emp = Permission.objects.filter(id__in=[2, 6]) # only read permissions

# Assign permissions to roles
role_admin.permissions.set(perm_admin)
role_emp.permissions.set(perm_emp)
  • Creating HTML Templates:

You would also need to create appropriate HTML templates for the forms, success messages, and selection of permissions for defining custom roles.

creating a new role
listing page for roles
editing a role

5. Conclusion

Implementing role-based permissions in our Django application is sometimes essential for maintaining data security and offering a tailored user experience. By following this guide, I think you can confidently build robust web applications that provide the right features to the right users while ensuring data integrity and user satisfaction.

--

--

Subham Sarangi

Nihilist, Occasional reader, Realist, Dabbler in Linguistics, Skeptic, Computer Engineer; Atheist.