Loading...

Advanced Django REST API Development

Web Development January 15, 2026


# Advanced Django REST API Development

Building robust REST APIs is crucial for modern web applications. Django REST Framework (DRF) provides powerful tools to create scalable, maintainable APIs that can handle complex business logic and high traffic loads.

## Setting Up Advanced Serializers

### Custom Field Serialization
```python
from rest_framework import serializers
from django.contrib.auth.models import User

class UserProfileSerializer(serializers.ModelSerializer):
full_name = serializers.SerializerMethodField()
avatar_url = serializers.SerializerMethodField()

class Meta:
model = User
fields = ['id', 'username', 'email', 'full_name', 'avatar_url']

def get_full_name(self, obj):
return f"{obj.first_name} {obj.last_name}".strip()

def get_avatar_url(self, obj):
if hasattr(obj, 'profile') and obj.profile.avatar:
return self.context['request'].build_absolute_uri(obj.profile.avatar.url)
return None
```

### Nested Serializers for Complex Relationships
```python
class PostSerializer(serializers.ModelSerializer):
author = UserProfileSerializer(read_only=True)
comments = CommentSerializer(many=True, read_only=True)
tags = TagSerializer(many=True)

class Meta:
model = Post
fields = '__all__'

def create(self, validated_data):
tags_data = validated_data.pop('tags', [])
post = Post.objects.create(**validated_data)

for tag_data in tags_data:
tag, created = Tag.objects.get_or_create(**tag_data)
post.tags.add(tag)

return post
```

## Advanced ViewSets and Mixins

### Custom ViewSet with Multiple Actions
```python
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response

class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer

@action(detail=True, methods=['post'])
def like(self, request, pk=None):
post = self.get_object()
user = request.user

if user in post.likes.all():
post.likes.remove(user)
liked = False
else:
post.likes.add(user)
liked = True

return Response({
'liked': liked,
'total_likes': post.likes.count()
})

@action(detail=False, methods=['get'])
def trending(self, request):
trending_posts = Post.objects.filter(
created_at__gte=timezone.now() - timedelta(days=7)
).annotate(
like_count=Count('likes')
).order_by('-like_count')[:10]

serializer = self.get_serializer(trending_posts, many=True)
return Response(serializer.data)
```

## Authentication and Permissions

### JWT Authentication Setup
```python
# settings.py
from datetime import timedelta

SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
'ROTATE_REFRESH_TOKENS': True,
'BLACKLIST_AFTER_ROTATION': True,
}

# Custom JWT Claims
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
token = super().get_token(user)
token['username'] = user.username
token['email'] = user.email
return token
```

### Custom Permissions
```python
from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
# Read permissions for any request
if request.method in permissions.SAFE_METHODS:
return True
# Write permissions only to the owner
return obj.owner == request.user

class IsAuthorOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.author == request.user or request.user.is_staff
```

## Advanced Filtering and Search

### Custom Filter Backend
```python
from django_filters import rest_framework as filters
from django.db.models import Q

class PostFilter(filters.FilterSet):
title = filters.CharFilter(lookup_expr='icontains')
author = filters.CharFilter(field_name='author__username', lookup_expr='icontains')
date_from = filters.DateFilter(field_name='created_at', lookup_expr='gte')
date_to = filters.DateFilter(field_name='created_at', lookup_expr='lte')
tags = filters.CharFilter(method='filter_tags')

class Meta:
model = Post
fields = ['category', 'status']

def filter_tags(self, queryset, name, value):
return queryset.filter(tags__name__icontains=value).distinct()
```

### Full-Text Search with PostgreSQL
```python
from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank

class PostViewSet(viewsets.ModelViewSet):
@action(detail=False, methods=['get'])
def search(self, request):
query = request.query_params.get('q', '')
if not query:
return Response({'results': []})

search_vector = SearchVector('title', weight='A') + SearchVector('content', weight='B')
search_query = SearchQuery(query)

posts = Post.objects.annotate(
search=search_vector,
rank=SearchRank(search_vector, search_query)
).filter(search=search_query).order_by('-rank')

serializer = self.get_serializer(posts, many=True)
return Response({'results': serializer.data})
```

## Performance Optimization

### Query Optimization
```python
class PostViewSet(viewsets.ModelViewSet):
def get_queryset(self):
return Post.objects.select_related('author').prefetch_related(
'tags', 'comments__author'
).annotate(
comment_count=Count('comments'),
like_count=Count('likes')
)
```

### Caching Strategies
```python
from django.core.cache import cache
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page

class PostViewSet(viewsets.ModelViewSet):
@method_decorator(cache_page(60 * 15)) # Cache for 15 minutes
@action(detail=False, methods=['get'])
def popular(self, request):
cache_key = 'popular_posts'
popular_posts = cache.get(cache_key)

if not popular_posts:
popular_posts = Post.objects.annotate(
score=Count('likes') + Count('comments')
).order_by('-score')[:10]
cache.set(cache_key, popular_posts, 60 * 30) # Cache for 30 minutes

serializer = self.get_serializer(popular_posts, many=True)
return Response(serializer.data)
```

## API Versioning

### URL Path Versioning
```python
# urls.py
urlpatterns = [
path('api/v1/', include('api.v1.urls')),
path('api/v2/', include('api.v2.urls')),
]

# settings.py
REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
'DEFAULT_VERSION': 'v1',
'ALLOWED_VERSIONS': ['v1', 'v2'],
}
```

## Testing Your API

### Comprehensive Test Suite
```python
from rest_framework.test import APITestCase
from django.contrib.auth.models import User
from rest_framework_simplejwt.tokens import RefreshToken

class PostAPITestCase(APITestCase):
def setUp(self):
self.user = User.objects.create_user(
username='testuser',
email='test@example.com',
password='testpass123'
)
refresh = RefreshToken.for_user(self.user)
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {refresh.access_token}')

def test_create_post(self):
data = {
'title': 'Test Post',
'content': 'This is a test post content.',
'category': 'tech'
}
response = self.client.post('/api/v1/posts/', data)
self.assertEqual(response.status_code, 201)
self.assertEqual(Post.objects.count(), 1)

def test_unauthorized_access(self):
self.client.credentials() # Remove authentication
response = self.client.post('/api/v1/posts/', {})
self.assertEqual(response.status_code, 401)
```

## Conclusion

Building advanced Django REST APIs requires understanding of serialization, authentication, permissions, filtering, and performance optimization. By implementing these patterns, you can create robust, scalable APIs that handle complex business requirements while maintaining excellent performance.

Remember to always test your APIs thoroughly and monitor their performance in production environments.