Currently, Django provides decorators like @login_required and @permission_required to restrict view access. However, there is no built-in support for role-based access control, which is a common requirement for many applications.
I want to propose adding the new role_required decorator that allows developers to restrict access to views based on user roles. The decorator will:
Check if the user has one or more specified roles.
Support both “any role” (test_all=False) and “all roles” (test_all=True) modes.
Redirect unauthorized users to the appropriate page or a custom URL.
This feature will make it easier for developers to implement role-based access control without writing custom decorators.
Example Use Case
A marketplace application might have roles like is_seller, is_buyer, and is_employee. The role_required decorator can be used to restrict access to specific views:
from django.contrib.auth.decorators import role_required
@role_required(['is_seller'])
def seller_dashboard(request):
# Only users with the 'is_seller' role can access this view.
pass
@role_required(['is_admin', 'is_moderator'], test_all=True)
def admin_dashboard(request):
# Only users with both 'is_admin' and 'is_moderator' roles can access this view.
pass
One of the Django main developers told me that they want to keep Django a core framework, not providing every utility that might be useful, that’s good, however, I see some built-in features and some CBVs their main role is to write fewer code and simplify the development process so that I see the role_required decorator, he advised me to use the permission_required instead, however, the role_required is mostly like the login_required, not just restricting users from some model permissions (this is my POV).
Since Django deals with different User attrs such as is_staff (and some projects need more than the is_staff role) then I could say that we’re already working with roles. (please correct me if I’m wrong)
Yes, this decorator looks for that attribute on the passed user object to evaluate if it passes or not, it’s based on the user_passes_test function, here is my implementation:
def role_required(roles: list[str], test_all=False, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):
"""
Decorator for views that checks that the user has a specific role,
redirecting to the log-in page if necessary.
role: must be a list of valid string user attributes as they were declared in their models
test_all: bool value that determines if all roles are required or just one.
"""
def _test_role(user):
if test_all:
return user.is_authenticated and all(getattr(user, role, False) for role in roles)
return user.is_authenticated and any(getattr(user, role, False) for role in roles)
actual_decorator = user_passes_test(
lambda u: _test_role(u),
login_url,
redirect_field_name
)
return actual_decorator
Yes I know there are some 3rd party libraries that provide RBAC, however, I’m just looking for the simplest way to achieve the desired results by making a built-in function with only 1 line of code to work with rather than dealing with 3rd party library.
Yes, I agree with you, and I believe there is more than one way to achieve the same result, however (based on my knowledge) I need to write the same block of code for every target view to ensure that the user belongs to that group with those permissions (unless there is another simple way that I don’t know yet), but this role_required deco will save me some time and simplify the process with only 1 line of code.
I would like to hear from you again sir ^^
A role is not a permission - a role is a set of permissions granting ability to perform functions associated with that role.
You don’t check for “role membership” at the view level - you check for specific permissions needed for that view.
The permission is assigned to one or more roles, allowing for access to a view to be granted to an indeterminate number of roles without requiring any changes to the code.