Authorization
While Authentication verifies who a user is, Authorization dictates what that user is permitted to do. Django handles authorization through a built-in Role-Based Access Control (RBAC) system using Permissions and Groups.
This system allows you to restrict access to specific views, database records, or UI components based on a user's assigned role.
1. Core Concepts: Permissions & Groups
Permissions (The Fine-Grained Rules)
Whenever you run a database migration on a new model, Django automatically generates four fine-grained, atomic permissions for that model. These permissions follow a strict naming convention: <app_label>.<action>_<modelname>.
For a model named ProjectOpening inside an app named positions_board, Django creates:
positions_board.add_projectopeningpositions_board.change_projectopeningpositions_board.delete_projectopeningpositions_board.view_projectopening
You can check if a user has a specific permission using the has_perm() method:
Python
# Returns True if the user has explicit or inherited authorization
if user.has_perm('positions_board.delete_projectopening'):
print("User is authorized to purge records.")
Groups (The Roles)
Assigning permissions to hundreds of users individually quickly becomes unmanageable. Instead, you can use Groups to bundle permissions into logical corporate roles (e.g., "Recruiters", "Hiring Managers", "Compliance Officers").
When you add a user to a Group, they automatically inherit every permission assigned to that Group.
2. Enforcing Access in Function-Based Views (Decorators)
For Function-Based Views (FBVs), Django provides a suite of Python decorators to intercept incoming requests and block unauthorized users before your view code executes.
A. Checking Authentication State (@login_required)
Redirects anonymous users to your login page instantly.
Python
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
@login_required(login_url='/login/')
def internal_dashboard_view(request):
return render(request, 'dashboard.html')B. Checking Specific Permissions (@permission_required)
Blocks users who lack a specific permission flag. By default, unauthorized users are redirected to the login page, but setting raise_exception=True triggers a standard 403 Forbidden error response instead.
Python
from django.contrib.auth.decorators import permission_required
@permission_required('positions_board.delete_projectopening', raise_exception=True)
def purge_listing_view(request, opening_id):
# This block executes only if the user has the explicit delete permission
return render(request, 'deleted_success.html')C. Checking Custom Criteria (@user_passes_test)
When you need to restrict a view based on custom logic—such as checking if a user belongs to a specific corporate group—use @user_passes_test. It accepts a helper function that returns True or False.
from django.contrib.auth.decorators import user_passes_test
def is_hr_staff(user):
# Checks if the user belongs to the 'HR Team' group array
return user.groups.filter(name='HR Team').exists()
@user_passes_test(is_hr_staff, raise_exception=True)
def hr_payroll_portal(request):
return render(request, 'payroll.html')
3. Enforcing Access in Class-Based Views (Mixins)
Because you cannot attach function decorators directly to entire class objects, Class-Based Views (CBVs) enforce authorization using Mixins. Mixins are parent classes that inject validation hooks into the view's lifecycle execution loop.
Critical Guardrail: Python evaluates base classes from left to right. To ensure authorization checks execute before anything else, always declare your access mixins as the very first arguments in your class definition.
Python
from django.views.generic import ListView
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from .models import ProjectOpening
# The view enforces login validation first, then checks permissions, and finally loads the ListView mechanics
class SecureOpeningListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
model = ProjectOpening
template_name = 'openings_list.html'
# 1. Configure the required permission rule
permission_required = 'positions_board.view_projectopening'
# 2. Dictate the failure behavior path
raise_exception = True4. UI-Level Authorization (Template Layer)
Restricting views protects your backend endpoints, but you should also update your front-end user interface to hide links or buttons that unauthorized users cannot use.
Django's template engine automatically injects a perms variable into the context, allowing you to check user permissions directly inside your HTML layouts using standard conditional tags.
HTML
<div class="control-panel">
<h3>Requisition Matrix</h3>
<a href="{% url 'view-openings' %}" class="btn">View Openings Ledger</a>
{% if perms.positions_board.add_projectopening %}
<a href="{% url 'create-opening' %}" class="btn btn-success">Add New Requisition Slot</a>
{% endif %}
{% if perms.positions_board.delete_projectopening %}
<button class="btn btn-danger">Purge System Table Row</button>
{% endif %}
</div>