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:
- Automatic serialization of models to JSON and back.
- Supports API authentication and permissions out of the box.
- Browsable API interface for testing and debugging.
- Integrates seamlessly with Django ORM and models.
- Customizable and scalable for production use.
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
- Use ModelSerializer to reduce boilerplate.
- Prefer ViewSets + Routers for CRUD operations.
- Enable pagination for large datasets.
- Use filtering and ordering to improve query flexibility.
- Protect endpoints with authentication and permissions.
- Apply throttling for rate limiting.
- Write unit tests for API endpoints.
- Version your API to support future changes.