Django

⌘K
  1. Home
  2. Django
  3. Django Rest Framework
  4. custom permission

custom permission

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 to has_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 AttendanceHasDynamicModelPermission is applied along with the default IsAuthenticated.
  • Throttling: We also use UserRateThrottle to 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 add permission):
    POST http://127.0.0.1:8000/api/attendance-log/
  • PUT request to update an attendance log (requires change permission):
    PUT http://127.0.0.1:8000/api/attendance-log/1/
  • DELETE request to delete an attendance log (requires delete permission):
    DELETE http://127.0.0.1:8000/api/attendance-log/1/

Conclusion

In this professional tutorial, we demonstrated how to:

  1. Create a custom permission class that dynamically checks permissions based on HTTP methods and model names.
  2. Apply this permission class in a Django REST Framework ViewSet.
  3. Test user permissions for actions like viewing, adding, updating, and deleting data in the AttendanceLog model.

This method is highly flexible and can be reused for any model in your Django app by simply applying the AttendanceHasDynamicModelPermission.

How can we help?