Django REST Framework Custom Permissions Tutorial
In this tutorial, we will create a custom permission class to dynamically manage permissions for different models within an Attendance app. We will allow access based on the HTTP methods and the user’s permissions for specific actions (e.g., view, add, change, delete). This will be a professional guide for handling complex permission requirements in Django Rest Framework (DRF).
Step 1: Set up the Attendance App
Create a Django project and app
If you don’t have a project yet, create one and an app named attendance.
django-admin startproject company_attendance
cd company_attendance
python manage.py startapp attendance
Add attendance and rest_framework to INSTALLED_APPS in settings.py:
# company_attendance/settings.py
INSTALLED_APPS = [
# other apps
'rest_framework',
'attendance',
]
Run migrations to set up your database:
python manage.py makemigrations
python manage.py migrate
Step 2: Create a Model for Attendance
Let’s create an AttendanceLog model for logging employee attendance. This model will store records like user, in-time, and out-time.
# attendance/models.py
from django.db import models
from django.contrib.auth.models import User
class AttendanceLog(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
in_time = models.DateTimeField()
out_time = models.DateTimeField(null=True, blank=True)
def __str__(self):
return f"Attendance for {self.user.username} at {self.in_time}"
After defining the model, run the migrations:
python manage.py makemigrations attendance
python manage.py migrate
Step 3: Create Serializers for AttendanceLog
We will now create serializers to convert our AttendanceLog data into JSON format for our API.
# attendance/serializers.py
from rest_framework import serializers
from .models import AttendanceLog
class AttendanceLogSerializer(serializers.ModelSerializer):
class Meta:
model = AttendanceLog
fields = '__all__'
Step 4: Create Custom Permissions
Now, let’s create a custom permission class that dynamically checks if a user has the required permissions to access certain models based on the HTTP method.
# attendance/permissions.py
from rest_framework import permissions
class AttendanceHasDynamicModelPermission(permissions.BasePermission):
"""
Custom permission to only allow users with the appropriate permissions based on the model.
"""
def has_permission(self, request, view):
# Determine the model name dynamically from the viewset.
model_name = view.queryset.model._meta.model_name
# Map HTTP methods to permission types.
if request.method in permissions.SAFE_METHODS:
perm_action = 'view'
elif request.method == 'POST':
perm_action = 'add'
elif request.method in ['PUT', 'PATCH']:
perm_action = 'change'
elif request.method == 'DELETE':
perm_action = 'delete'
else:
return False
# Check if the user has the required permission.
required_permission = f'attendance.{perm_action}_{model_name}'
return request.user.has_perm(required_permission)
def has_object_permission(self, request, view, obj):
# Similar to has_permission, but checks permissions on an object level.
model_name = obj._meta.model_name
if request.method in permissions.SAFE_METHODS:
perm_action = 'view'
elif request.method in ['PUT']:
perm_action = 'change'
elif request.method == 'DELETE':
perm_action = 'delete'
else:
return False
required_permission = f'attendance.{perm_action}_{model_name}'
return request.user.has_perm(required_permission, obj)
How it Works:
has_permission: Checks permissions globally based on the request type (GET,POST,PUT,DELETE). It dynamically determines the permission based on the model.has_object_permission: Similar tohas_permission, but checks permissions at the object level.
Step 5: Create the Attendance API View
Now that we have our permission class, let’s create a ViewSet for the AttendanceLog model and apply the custom permission.
# attendance/views.py
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from rest_framework_simplejwt.authentication import JWTAuthentication
from .models import AttendanceLog
from .serializers import AttendanceLogSerializer
from .permissions import AttendanceHasDynamicModelPermission
from rest_framework.throttling import UserRateThrottle
class AttendanceLogViewSet(viewsets.ModelViewSet):
queryset = AttendanceLog.objects.all()
serializer_class = AttendanceLogSerializer
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated, AttendanceHasDynamicModelPermission]
throttle_classes = [UserRateThrottle]
- Authentication: We use JWT authentication to secure the API.
- Permission Classes: Our custom permission class
AttendanceHasDynamicModelPermissionis applied along with the defaultIsAuthenticated. - Throttling: We also use
UserRateThrottleto limit the number of requests a user can make.
Step 6: Register the ViewSet in URLs
Let’s set up the URLs for the AttendanceLogViewSet.
# attendance/urls.py
from rest_framework.routers import DefaultRouter
from .views import AttendanceLogViewSet
router = DefaultRouter()
router.register(r'attendance-log', AttendanceLogViewSet)
urlpatterns = router.urls
Now, include the attendance app URLs in the main project URL configuration:
# company_attendance/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('attendance.urls')),
]
Step 7: Testing Permissions
To test the permissions, you need to create some user roles and assign them appropriate permissions in the Django admin panel or shell.
Example of Assigning Permissions:
from django.contrib.auth.models import User, Permission
from django.contrib.contenttypes.models import ContentType
from attendance.models import AttendanceLog
# Create a user
user = User.objects.create_user(username='john', password='password123')
# Get the ContentType for AttendanceLog
content_type = ContentType.objects.get_for_model(AttendanceLog)
# Assign 'view' permission
permission = Permission.objects.get(codename='view_attendancelog', content_type=content_type)
user.user_permissions.add(permission)
Now, the user john will be able to access GET requests for the AttendanceLog model, but won’t be able to add, change, or delete records without the corresponding permissions (add_attendancelog, change_attendancelog, delete_attendancelog).
Step 8: Run the Server and Test
Start the Django development server:
python manage.py runserver
Use tools like Postman or cURL to test the endpoints:
- GET request to list attendance logs:
GET http://127.0.0.1:8000/api/attendance-log/ - POST request to create an attendance log (requires
addpermission):POST http://127.0.0.1:8000/api/attendance-log/ - PUT request to update an attendance log (requires
changepermission):PUT http://127.0.0.1:8000/api/attendance-log/1/ - DELETE request to delete an attendance log (requires
deletepermission):DELETE http://127.0.0.1:8000/api/attendance-log/1/
Conclusion
In this professional tutorial, we demonstrated how to:
- Create a custom permission class that dynamically checks permissions based on HTTP methods and model names.
- Apply this permission class in a Django REST Framework
ViewSet. - Test user permissions for actions like viewing, adding, updating, and deleting data in the
AttendanceLogmodel.
This method is highly flexible and can be reused for any model in your Django app by simply applying the AttendanceHasDynamicModelPermission.