Python

Django REST Framework (DRF) – Detailed Guide A great basic article on Django REST Framework

Django REST Framework (DRF) is a powerful toolkit to build Web APIs using Django. Unlike vanilla Django, DRF provides structured tools for serialization, validation, authentication, permissions, versioning, throttling, and testing APIs efficiently.


1. Why DRF?

DRF simplifies API development by abstracting repetitive tasks. Key reasons to use DRF:


2. Installation and Setup

Install DRF via pip:

pip install djangorestframework

Add to INSTALLED_APPS in settings.py:

INSTALLED_APPS = [
            'django.contrib.admin',
            'django.contrib.auth',
            # ...
            'rest_framework',
        ]

3. Serializers – Converting Models to JSON

Serializers handle data transformation between complex Django objects and native Python types (JSON, XML).

ModelSerializer Example

from rest_framework import serializers
        from .models import Article

        class ArticleSerializer(serializers.ModelSerializer):
            class Meta:
                model = Article
                fields = ['id', 'title', 'content', 'author', 'created_at']

Custom Serializer with Validation

class CustomSerializer(serializers.Serializer):
            name = serializers.CharField(max_length=100)
            age = serializers.IntegerField()

            def validate_age(self, value):
                if value < 0:
                    raise serializers.ValidationError("Age cannot be negative")
                return value

Explanation: You can validate each field individually using validate_fieldname or override validate() for object-level validation.


4. Views and ViewSets

DRF provides multiple ways to handle requests: APIView, Generic Views, and ViewSets.

APIView Example (Manual Handling)

from rest_framework.views import APIView
        from rest_framework.response import Response
        from rest_framework import status

        class HelloView(APIView):
            def get(self, request):
                return Response({"message": "Hello, World!"}, status=status.HTTP_200_OK)

            def post(self, request):
                data = request.data
                return Response(data, status=status.HTTP_201_CREATED)

Generic Views + Mixins

from rest_framework import generics
        from .models import Article
        from .serializers import ArticleSerializer

        class ListCreateArticle(generics.ListCreateAPIView):
            queryset = Article.objects.all()
            serializer_class = ArticleSerializer

ViewSets + Routers (Recommended)

ViewSets combine all CRUD operations. Routers automatically generate URLs.

from rest_framework import viewsets
        from .models import Article
        from .serializers import ArticleSerializer

        class ArticleViewSet(viewsets.ModelViewSet):
            queryset = Article.objects.all()
            serializer_class = ArticleSerializer
        
from rest_framework.routers import DefaultRouter
        from django.urls import path, include
        from .views import ArticleViewSet

        router = DefaultRouter()
        router.register(r'articles', ArticleViewSet)

        urlpatterns = [
            path('', include(router.urls)),
        ]

Explanation: No need to define separate GET, POST, PUT, DELETE URLs — the router handles it automatically.


5. Authentication & Permissions

DRF provides authentication methods (Session, Token, JWT) and permissions to restrict access.

Default Authentication Setup

REST_FRAMEWORK = {
            'DEFAULT_AUTHENTICATION_CLASSES': [
                'rest_framework.authentication.SessionAuthentication',
                'rest_framework.authentication.TokenAuthentication',
            ],
            'DEFAULT_PERMISSION_CLASSES': [
                'rest_framework.permissions.IsAuthenticatedOrReadOnly',
            ],
        }

Custom Permission Example

from rest_framework.permissions import BasePermission, SAFE_METHODS

        class IsOwnerOrReadOnly(BasePermission):
            def has_object_permission(self, request, view, obj):
                if request.method in SAFE_METHODS:
                    return True
                return obj.owner == request.user

6. Pagination, Filtering, and Ordering

Pagination Example

REST_FRAMEWORK = {
            'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
            'PAGE_SIZE': 10
        }

Filtering and Searching Example

from rest_framework import filters
        from rest_framework.generics import ListAPIView

        class ArticleList(ListAPIView):
            queryset = Article.objects.all()
            serializer_class = ArticleSerializer
            filter_backends = [filters.SearchFilter, filters.OrderingFilter]
            search_fields = ['title', 'content']
            ordering_fields = ['created_at']

7. Throttling / Rate Limiting

Throttle classes control the number of requests a user can make.

REST_FRAMEWORK = {
            'DEFAULT_THROTTLE_CLASSES': [
                'rest_framework.throttling.UserRateThrottle',
                'rest_framework.throttling.AnonRateThrottle',
            ],
            'DEFAULT_THROTTLE_RATES': {
                'user': '1000/day',
                'anon': '100/day',
            },
        }

Custom Throttle Example

from rest_framework.throttling import UserRateThrottle
        from rest_framework.views import APIView
        from rest_framework.response import Response

        class MyView(APIView):
            throttle_classes = [UserRateThrottle]

            def get(self, request):
                return Response({"status": "ok"})

8. Nested Serialization & Relationships

class CommentSerializer(serializers.ModelSerializer):
            class Meta:
                model = Comment
                fields = ['id', 'content', 'author']

        class PostSerializer(serializers.ModelSerializer):
            comments = CommentSerializer(many=True, read_only=True)

            class Meta:
                model = Post
                fields = ['id', 'title', 'body', 'comments']

Hyperlinked relationships example:

class UserSerializer(serializers.HyperlinkedModelSerializer):
            posts = serializers.HyperlinkedRelatedField(
                many=True,
                view_name='post-detail',
                read_only=True
            )

            class Meta:
                model = User
                fields = ['url', 'username', 'posts']

9. Versioning

REST_FRAMEWORK = {
            'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning'
        }

URL examples:

/api/v1/articles/
        /api/v2/articles/

10. Testing DRF APIs

from rest_framework.test import APITestCase
        from rest_framework import status

        class ArticleTests(APITestCase):
            def test_create_article(self):
                url = '/articles/'
                data = {'title': 'Test', 'content': 'Some content'}
                response = self.client.post(url, data, format='json')
                self.assertEqual(response.status_code, status.HTTP_201_CREATED)

11. Error Handling & Validation

DRF automatically returns a 400 Bad Request with error details when validation fails.

class ArticleSerializer(serializers.ModelSerializer):
            title = serializers.CharField(max_length=100)

            def validate_title(self, value):
                if "forbidden" in value.lower():
                    raise serializers.ValidationError("Forbidden word used in title.")
                return value
{"title": ["Forbidden word used in title."]}

12. Best Practices