Django Roles, Groups, and Permissions Introduction

Hemanth S P
DjangoTube:
Published in
6 min readDec 22, 2019
Photo by Paweł Czerwiński on Unsplash

This article aims to provide a clean and clear introduction to Django roles vs permissions vs groups. To accomplish this, I am going to explain the things you need to know and understand all about permissions, groups, and roles, and then we are going to see code examples.

This is the list of topics that we are going to discuss in this article:

  1. Users Roles
  2. Permissions
  3. Groups

Before going ahead, first, we must understand the difference between authentication and authorization to know where these permissions are applicable.

Authentication vs authorization

Authentication is just checking user credentials like email and the password is correct. Authorization (permission) is authenticated to what the user can do inside the application.

1. What are User roles

Suppose if you are creating a Django project in that project you may have multiple user types, for example, consider a Hospital project in the hospital multiple roles will be there like a doctor, nurse, compounder, etc… then you need a different role for all the persons to categorize.

How to use user roles

To use roles you need to extend AbstractUser(for simple case) in your models.py

models.py Custom user model with the role
>>> user = User.objects.first()
>>> user.role = User.NURSE
>>> user.save()
>>> user.role == User.DOCTOR
False
>>> user.role == User.NURSE
True

you can check the user role like above.

2. What are permissions

Permissions are a rule(or restrictions) to view, add, change, delete(Django defaults), or custom rules to objects for a specific user or a group of users.

Django comes with a built-in permissions system. It provides a way to assign permissions to specific users and groups of users.

Django provides some default permissions using django.contrib.auth

When django.contrib.auth is listed in your INSTALLED_APPS setting, it will ensure that four default permissions – add, change, delete, and view – are created for each Django model defined in one of your installed applications.

Assuming you have an application with an app_label or app name poll and a model named Vote, To test which users have basic permissions or not you can use the below code.

python manage.py shell
user = User.objects.first()
  • add: user.has_perm('poll.add_vote')
  • change: user.has_perm('poll.change_vote')
  • delete: user.has_perm('poll.delete_vote')
  • view: user.has_perm('poll.view_vote')

Or use the decorator to check the user has permission to execute the function or not.

from django.contrib.auth.decorators import permission_required@permission_required('poll.add_vote') 
def your_func(request):
"""or you can rise permission denied exception"""

Or Use PermissionRequiredMixin class if you use a class-based view.

from django.contrib.auth.mixins import PermissionRequiredMixin
from django.views.generic import ListView
class VoteListView(PermissionRequiredMixin, ListView):
permission_required = 'polls.add_vote'
# Or multiple of permissions
permission_required = ('poll.add_vote', 'poll.change_vote')

To check the user has permission in the template, the syntax is

{% if perms.app_label.can_do_something %}

{% if perms.poll.add_vote %}

Or you can use if condition inside the function to check the user has required permission.

user.has_perm('vote.change_vote')

And this User(AbstractUser) model has ManyToMany field called user_permissions from the abstract Permission model PermissionsMixin, below are the methods you can assign and remove permission from or to the user.

user.user_permissions.set([permission_list])
user.user_permissions.add(permission, permission, ...)
user.user_permissions.remove(permission, permission, ...)
user.user_permissions.clear()

To get users all permissions syntax is.

user.user_permissions.all()

Custom permissions on the model level

You can define your own custom permissions if you did not satisfied with the Django 4 default permissions. for example, I added two more custom permission on the Vote model.

from django.db import modelsclass Vote(models.Model):

user = models.ForeignKey(User)
class Meta:
permissions = (
("view_vote_office", "can view vote office"),
("find_vote", "can find vote"),
)

Do not forget to run makemigrations and migrate the model changes.

fig: Added two more custom permission to the Vote model

2) a. Create custom permissions using Programmatically.

from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType

content_type = ContentType.objects.get_for_model(Vote)
permission = Permission.objects.create(
codename='can_see_vote_count',
name='Can See Vote Count',
content_type=content_type,
)

Important to notice is Permission caching. this means look at the below example to understand it better.

# be sure every permission must be unique(set,duplicate not allowed)user.has_perm('poll.find_owner') # False,bcz no perm available

# create permission
content_type = ContentType.objects.get_for_model(Vote)
permission = Permission.objects.get(
codename='find_owner',
content_type=content_type,
)

# assign new permission to user user.user_permissions.add(permission)

# Checking the cached permission set
user.has_perm('poll.find_owner') # False, bcz cache problem.

# get new instance of User
# Be aware that user.refresh_from_db() won't clear the cache.
user = get_object_or_404(User, pk=user_id)

# Permission cache is repopulated from the database
user.has_perm('myapp.find_owner') # True

Note: superuser inherent all the permissions (has_perm will return True) but you can modify based on your project requirements

# source code verification
# Active superusers have all permissions.
if self.is_active and self.is_superuser:
return True

2) b. Object, instance, a record, or DB table row Permission

So overall when you create a model Django generates 4 permissions, this permission is not available to the user unless someone assigns it. If permissions are assigned to the user, for example, admin give change_vote to one user that user can change all vote that belongs to other users so we do not want this feature most of the time, for that we need to restrict user like do not modify other user records for this purpose Django guardian(the editor can edit given(assigned object-level permissions) book provided by the admin) library created to give object or instance level permission. for more info read Django guardian with joey's example.

3. Django Groups

As name suggest Groups means can contain list of permissions or just group of anything.

or

A group describes a group of users. One user can be part of many groups and one group can have many users. Groups can use of labeling users.

django.contrib.auth.models.Group models are a generic way of categorizing users so you can apply permissions, or some other label, to those users. A user can belong to any number of groups.

A user in a group automatically has the permissions granted to that group. For example, if the group Site editors has the permission can_edit_home_page, any user in that group will have that permission.

Instead of maintaining permission for every user(not scalable), it's better to create Groups like Doctor, Nurse, NurseTwo, etc… and assign users to respective groups or multiple groups and check these user permissions using the above syntax code(has_perm). If the user belongs to any group then that user will inherit all the permissions from that group or groups.

You can create groups like Doctor, Nurse1, Nurse2, etc… to restrict permissions.

Note: do not check the user in the group to verify whether the users have permission or not, so must use has_perm because at the bottom line user will inherit the permissions from the assigned groups and you can use groups to categorize users if your intention was just to use a User Role model alternative.

Create Groups

from django.contrib.auth.models import Groupdoctor_group, created = Group.objects.get_or_create(name='Doctor')

Assign the set of permissions to that group

doctor_group.permissions.set([permission_list])
doctor_group.permissions.add(permission, permission, ...)
doctor_group.permissions.remove(permission, permission, ...)
doctor_group.permissions.clear()

Assign a user to groups

doctor_group.user_set.add(user)
OR
user.groups.add(doctor_group)

Check user in the group

def is_doctor(user):
return user.groups.filter(name='Doctor').exists()
from django.contrib.auth.decorators import user_passes_test@user_passes_test(is_doctor)
def my_view(request):
pass

Check user in the group in the template

# you can you custom template tag to check user in group or groups

Roles vs Groups

Most of the time in ACL you can call roles and groups both are the same, but their purpose is different and most of the time, users have multiple roles and multiple groups. the best thing is to create the same group name whenever you create a role.

My thought is to use roles for showing different HTML pages to users like based on the user category and use group for permission purposes.

Conclusion

In this article, I tried to deliver an introduction to multiple users or roles, permissions, and groups with all basics you need to know. Please note that Role(like doctor) and Groups (like nurse_group_1 and nurse_group_2, nurse1 group only has limited permission to view first-floor patient documents but not the second floor) are different.

I started explaining concepts and how it works with a basic example.

I recommend you to check the complete document on the Django official website.

I really hope this article helps you out in understanding the permissions and groups. feel free to ask doubts if any, thank you

******If you found this article helpful please follow me on medium.*******

--

--