Django REST Framework - API Development
Downloaded from PrepIQ (https://prepiq.online)
Q1. What is Django REST Framework and why should you use it for API development?
Django REST Framework (DRF) is a powerful, flexible toolkit for building REST APIs with Django. It provides serializers for data validation, class-based views, authentication, permissions, and built-in browsable API interface. Reduces boilerplate code dramatically.
| Feature | Benefit | Example |
|---|---|---|
| Serializers | Automatic data validation and conversion | ModelSerializer auto-generates from model |
| ViewSets | Reusable view logic for CRUD | One ViewSet replaces 5+ view functions |
| Authentication | Multiple auth methods built-in | Token, JWT, Session auth |
| Permissions | Fine-grained access control | Object-level, global-level permissions |
| Browsable API | Interactive API documentation | Test endpoints directly in browser |
// Install DRF
pip install djangorestframework
// Add to INSTALLED_APPS (settings.py)
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'rest_framework', # Add this
'myapp',
]
// Configure DRF (settings.py)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 20,
}
// models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
published_date = models.DateField()
created_at = models.DateTimeField(auto_now_add=True)
// serializers.py
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['id', 'title', 'author', 'price', 'published_date', 'created_at']
read_only_fields = ['id', 'created_at']
// views.py - APIView (Traditional)
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Book
from .serializers import BookSerializer
class BookListAPIView(APIView):
def get(self, request):
books = Book.objects.all()
serializer = BookSerializer(books, many=True)
return Response(serializer.data)
def post(self, request):
serializer = BookSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
// views.py - ViewSets (Recommended)
from rest_framework import viewsets
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
// urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import BookViewSet
router = DefaultRouter()
router.register(r'books', BookViewSet)
urlpatterns = [
path('api/', include(router.urls)),
]
// API Endpoints Generated Automatically:
// GET /api/books/ - List all books
// POST /api/books/ - Create new book
// GET /api/books/{id}/ - Retrieve specific book
// PUT /api/books/{id}/ - Update book
// PATCH /api/books/{id}/ - Partial update
// DELETE /api/books/{id}/ - Delete book
// Why DRF is Better than Raw Django:
// 1. Less boilerplate - ViewSets handle CRUD automatically
// 2. Automatic serialization - JSON/XML conversion handled
// 3. Validation - Serializers validate data automatically
// 4. Authentication - Multiple methods out of box
// 5. Permissions - Fine-grained access control
// 6. Documentation - Auto-generated API docs
// 7. Pagination - Built-in pagination support
// 8. Filtering/Search - Easy to add filtering
// 9. Throttling - Rate limiting built-in
// 10. Testing - Easy to test API endpoints
Q2. What is the difference between Serializer and ModelSerializer in DRF?
Serializer manually defines fields for any data structure. ModelSerializer automatically generates fields from a Django model, saving boilerplate code. ModelSerializer includes automatic validation and save() method implementation.
// Regular Serializer - Manual field definition
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=200)
author = serializers.CharField(max_length=100)
price = serializers.DecimalField(max_digits=10, decimal_places=2)
published_date = serializers.DateField()
def create(self, validated_data):
# Must manually implement create method
return Book.objects.create(**validated_data)
def update(self, instance, validated_data):
# Must manually implement update method
instance.title = validated_data.get('title', instance.title)
instance.author = validated_data.get('author', instance.author)
instance.save()
return instance
// ModelSerializer - Auto-generated from model
from rest_framework import serializers
from .models import Book
class BookModelSerializer(serializers.ModelSerializer):
# Automatically generates all fields from model
# Automatic create() and update() implementation
# Automatic validation based on model fields
class Meta:
model = Book
fields = ['id', 'title', 'author', 'price', 'published_date']
read_only_fields = ['id']
extra_kwargs = {
'title': {'required': True},
'price': {'min_value': 0},
}
// Custom validation in ModelSerializer
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['id', 'title', 'author', 'price', 'published_date']
def validate_title(self, value):
if len(value) < 3:
raise serializers.ValidationError('Title must be at least 3 characters')
return value
def validate(self, data):
# Cross-field validation
if data['price'] < 0:
raise serializers.ValidationError('Price cannot be negative')
return data
// When to use each:
// Serializer - Non-model data, custom structures, API contracts
// ModelSerializer - Model-backed resources, CRUD operations, quick prototyping
// Nested Serializers
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ['id', 'name']
class BookDetailSerializer(serializers.ModelSerializer):
author = AuthorSerializer() # Nested serializer
class Meta:
model = Book
fields = ['id', 'title', 'author', 'price']
// Using custom methods (SerializerMethodField)
class BookWithDetailSerializer(serializers.ModelSerializer):
author_name = serializers.SerializerMethodField()
def get_author_name(self, obj):
return f"{obj.author.first_name} {obj.author.last_name}"
class Meta:
model = Book
fields = ['id', 'title', 'author_name', 'price']
Q3. Explain APIView vs ViewSets in Django REST Framework. When do you use each?
APIView is a low-level class-based view for complete control over request/response handling. ViewSet is a high-level abstraction that automatically generates multiple views (list, create, retrieve, update, delete) from a single class. ViewSets reduce code duplication significantly.
// APIView - Full control, more boilerplate
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class BookListAPIView(APIView):
def get(self, request):
books = Book.objects.all()
serializer = BookSerializer(books, many=True)
return Response(serializer.data)
def post(self, request):
serializer = BookSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class BookDetailAPIView(APIView):
def get(self, request, pk):
book = Book.objects.get(pk=pk)
serializer = BookSerializer(book)
return Response(serializer.data)
def put(self, request, pk):
book = Book.objects.get(pk=pk)
serializer = BookSerializer(book, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
book = Book.objects.get(pk=pk)
book.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
// urls.py for APIView
urlpatterns = [
path('books/', BookListAPIView.as_view(), name='book-list'),
path('books/<int:pk>/', BookDetailAPIView.as_view(), name='book-detail'),
]
// ViewSet - Less code, automatic CRUD handling
from rest_framework import viewsets
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
# Automatically provides:
# - list (GET /books/)
# - create (POST /books/)
# - retrieve (GET /books/{id}/)
# - update (PUT /books/{id}/)
# - partial_update (PATCH /books/{id}/)
# - destroy (DELETE /books/{id}/)
def get_queryset(self):
# Filter based on query params
queryset = Book.objects.all()
author = self.request.query_params.get('author')
if author:
queryset = queryset.filter(author=author)
return queryset
// urls.py for ViewSet - Router auto-generates URLs
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'books', BookViewSet)
urlpatterns = [
path('', include(router.urls)),
]
// Custom Actions in ViewSet
from rest_framework.decorators import action
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
@action(detail=False, methods=['get'])
def recent(self, request):
# GET /books/recent/
recent_books = Book.objects.order_by('-created_at')[:10]
serializer = self.get_serializer(recent_books, many=True)
return Response(serializer.data)
@action(detail=True, methods=['post'])
def mark_featured(self, request, pk=None):
# POST /books/{id}/mark_featured/
book = self.get_object()
book.is_featured = True
book.save()
return Response({'status': 'book marked as featured'})
// When to use each:
// APIView - Complex logic, multiple serializers, custom response formats
// ViewSet - Standard CRUD operations, quick prototyping, REST compliance
// ViewSet Types:
// ModelViewSet - Full CRUD (create, read, update, delete)
// ReadOnlyModelViewSet - List and Retrieve only
// ViewSet - No automatic actions, manual implementation
Q4. How do you implement authentication and permissions in Django REST Framework?
Authentication identifies who the user is (TokenAuthentication, JWTAuthentication, SessionAuthentication). Permissions determine what authenticated users can do (IsAuthenticated, IsAdminUser, IsAuthenticatedOrReadOnly). Apply at view, viewset, or global level.
// settings.py - Global configuration
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
// Token Authentication Setup
# Add to INSTALLED_APPS
INSTALLED_APPS = [
'rest_framework.authtoken',
'myapp',
]
# Create token for user
python manage.py drf_create_token username
# Client sends token in header
# curl -H 'Authorization: Token abc123def456' http://localhost:8000/api/books/
// Built-in Permission Classes
from rest_framework.permissions import (
IsAuthenticated,
IsAdminUser,
IsAuthenticatedOrReadOnly,
AllowAny,
)
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
# Only authenticated users can access
permission_classes = [IsAuthenticated]
// Different permissions for different actions
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get_permissions(self):
if self.action == 'list' or self.action == 'retrieve':
# Anyone can view
permission_classes = [AllowAny]
elif self.action == 'create':
# Only authenticated users can create
permission_classes = [IsAuthenticated]
else:
# Only admins can update/delete
permission_classes = [IsAdminUser]
return [permission() for permission in permission_classes]
// Custom Permissions
from rest_framework.permissions import BasePermission
class IsOwnerOrReadOnly(BasePermission):
def has_object_permission(self, request, view, obj):
# Allow GET requests for anyone
if request.method in ['GET', 'HEAD', 'OPTIONS']:
return True
# Only allow edits by the book's author
return obj.author == request.user
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = [IsOwnerOrReadOnly]
// JWT Authentication (Token with expiry)
pip install djangorestframework-simplejwt
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from rest_framework_simplejwt.authentication import JWTAuthentication
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
# urls.py
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
urlpatterns = [
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
# Client usage:
# 1. POST /api/token/ with username/password -> get access + refresh tokens
# 2. Use access token in Authorization header
# 3. When access expires, POST /api/token/refresh/ with refresh token
// APIView-level permissions
from rest_framework.decorators import api_view, permission_classes
@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
def book_list(request):
# Only authenticated users can access
pass
Q5. How do you implement pagination, filtering, and search in DRF?
Pagination breaks large result sets into pages. Filtering narrows results based on specific criteria. Search provides full-text searching. DRF provides built-in pagination classes and django-filter for advanced filtering capabilities.
// Pagination - settings.py
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 20,
}
// Pagination Types
from rest_framework.pagination import (
PageNumberPagination,
LimitOffsetPagination,
CursorPagination,
)
class StandardPagination(PageNumberPagination):
page_size = 10
page_size_query_param = 'page_size'
max_page_size = 100
// Usage in ViewSet
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
pagination_class = StandardPagination
// Client requests
# /api/books/
# /api/books/?page=2
# /api/books/?page=1&page_size=50
// Filtering - Install django-filter
pip install django-filter
# settings.py
INSTALLED_APPS = ['django_filters']
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
'rest_framework.filters.SearchFilter',
'rest_framework.filters.OrderingFilter',
],
}
// Filtering in ViewSet
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
# Exact field filtering
filterset_fields = ['author', 'published_date']
# Full-text search
search_fields = ['title', 'author', 'description']
# Sorting/Ordering
ordering_fields = ['created_at', 'price']
ordering = ['-created_at'] # Default ordering
// Client requests
# Filter: /api/books/?author=John
# Search: /api/books/?search=django
# Order: /api/books/?ordering=-price
# Combined: /api/books/?author=John&search=django&ordering=-created_at
// Advanced Filtering
from django_filters import FilterSet, CharFilter, NumberFilter, DateFromToRangeFilter
class BookFilterSet(FilterSet):
title__icontains = CharFilter(field_name='title', lookup_expr='icontains')
min_price = NumberFilter(field_name='price', lookup_expr='gte')
max_price = NumberFilter(field_name='price', lookup_expr='lte')
date_range = DateFromToRangeFilter(field_name='published_date')
class Meta:
model = Book
fields = ['author', 'title__icontains', 'min_price', 'max_price']
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
filterset_class = BookFilterSet
// Search with custom logic
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
search_fields = ['=title', '@author', '$description'] # = exact, @ partial, $ full-text
// Manual Pagination without default
from rest_framework.pagination import PageNumberPagination
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
pagination_class = None # Disable pagination for this view
Q6. How do you handle errors and use HTTP status codes correctly in DRF APIs?
DRF provides automatic error handling with ValidationError for invalid data and status codes (200, 201, 204, 400, 401, 403, 404, 500) for different scenarios. Custom exception handlers allow consistent error responses across your API.
// Standard HTTP Status Codes
// 200 OK - Successful GET
// 201 Created - Successful POST creating resource
// 204 No Content - Successful DELETE
// 400 Bad Request - Validation error, invalid input
// 401 Unauthorized - Authentication required
// 403 Forbidden - Authenticated but no permission
// 404 Not Found - Resource doesn't exist
// 500 Server Error - Unexpected server error
from rest_framework.response import Response
from rest_framework import status
from rest_framework.views import APIView
class BookAPIView(APIView):
def get(self, request, pk):
try:
book = Book.objects.get(pk=pk)
serializer = BookSerializer(book)
return Response(serializer.data, status=status.HTTP_200_OK)
except Book.DoesNotExist:
return Response(
{'detail': 'Book not found'},
status=status.HTTP_404_NOT_FOUND
)
def post(self, request):
serializer = BookSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
try:
book = Book.objects.get(pk=pk)
book.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
except Book.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
// Validation Error Handling
from rest_framework.exceptions import ValidationError
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
def create(self, request, *args, **kwargs):
if 'title' not in request.data:
raise ValidationError({'title': 'This field is required'})
return super().create(request, *args, **kwargs)
// Custom Exception Handler
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context):
response = exception_handler(exc, context)
if response is not None:
response.data = {
'error': True,
'message': str(exc.detail),
'status_code': response.status_code,
}
return response
# settings.py
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'myapp.exception_handlers.custom_exception_handler',
}
// Consistent Error Response Format
from rest_framework.response import Response
from rest_framework import status
def error_response(message, status_code, errors=None):
data = {
'success': False,
'message': message,
'status_code': status_code,
}
if errors:
data['errors'] = errors
return Response(data, status=status_code)
// Usage
return error_response(
'Validation failed',
status.HTTP_400_BAD_REQUEST,
{'title': ['This field is required']}
)
// Custom Exception Class
from rest_framework.exceptions import APIException
class BookNotFound(APIException):
status_code = status.HTTP_404_NOT_FOUND
default_detail = 'Book not found'
class BookViewSet(viewsets.ModelViewSet):
def get_object(self):
try:
return Book.objects.get(pk=self.kwargs['pk'])
except Book.DoesNotExist:
raise BookNotFound()
// Throttling (Rate Limiting) Errors
from rest_framework.throttling import UserRateThrottle
class BookRateThrottle(UserRateThrottle):
scope = 'books'
# settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.UserRateThrottle',
],
'DEFAULT_THROTTLE_RATES': {
'user': '1000/hour',
'books': '100/hour',
},
}
// 429 Too Many Requests - Auto-generated when rate limit exceeded
Q7. How do you test Django REST Framework APIs effectively?
Use APITestCase and APIClient for testing DRF views. Mock external services, test authentication/permissions, validate response data and status codes. Write comprehensive tests for all HTTP methods and edge cases.
// Testing Setup
from django.test import TestCase
from rest_framework.test import APITestCase, APIClient
from rest_framework import status
from .models import Book
from .serializers import BookSerializer
class BookAPITests(APITestCase):
def setUp(self):
# Run before each test
self.client = APIClient()
self.book = Book.objects.create(
title='Test Book',
author='Test Author',
price=9.99
)
def test_list_books(self):
response = self.client.get('/api/books/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data['results']), 1)
def test_create_book(self):
data = {
'title': 'New Book',
'author': 'New Author',
'price': 19.99
}
response = self.client.post('/api/books/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['title'], 'New Book')
self.assertEqual(Book.objects.count(), 2)
def test_retrieve_book(self):
response = self.client.get(f'/api/books/{self.book.id}/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['id'], self.book.id)
def test_update_book(self):
data = {'title': 'Updated Title'}
response = self.client.patch(f'/api/books/{self.book.id}/', data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.book.refresh_from_db()
self.assertEqual(self.book.title, 'Updated Title')
def test_delete_book(self):
response = self.client.delete(f'/api/books/{self.book.id}/')
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual(Book.objects.count(), 0)
// Testing Authentication
from rest_framework.test import APITestCase
from django.contrib.auth.models import User
class AuthenticatedAPITests(APITestCase):
def setUp(self):
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.client = APIClient()
def test_unauthenticated_access(self):
response = self.client.get('/api/books/')
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_authenticated_access(self):
self.client.force_authenticate(user=self.user)
response = self.client.get('/api/books/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_token_authentication(self):
# Create token
from rest_framework.authtoken.models import Token
token = Token.objects.create(user=self.user)
# Use token in header
self.client.credentials(HTTP_AUTHORIZATION=f'Token {token.key}')
response = self.client.get('/api/books/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
// Testing Permissions
class PermissionTests(APITestCase):
def setUp(self):
self.admin_user = User.objects.create_superuser(
username='admin',
password='admin123',
email='admin@test.com'
)
self.normal_user = User.objects.create_user(
username='user',
password='user123'
)
def test_admin_can_delete(self):
book = Book.objects.create(title='Test', author='Test', price=9.99)
self.client.force_authenticate(user=self.admin_user)
response = self.client.delete(f'/api/books/{book.id}/')
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
def test_normal_user_cannot_delete(self):
book = Book.objects.create(title='Test', author='Test', price=9.99)
self.client.force_authenticate(user=self.normal_user)
response = self.client.delete(f'/api/books/{book.id}/')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
// Testing with Factory (factory_boy)
from factory import DjangoModelFactory
class BookFactory(DjangoModelFactory):
class Meta:
model = Book
title = 'Test Book'
author = 'Test Author'
price = 9.99
class BookFactoryTests(APITestCase):
def test_create_multiple_books(self):
books = BookFactory.create_batch(5)
response = self.client.get('/api/books/')
self.assertEqual(len(response.data['results']), 5)
Q8. How do you generate and provide API documentation for a Django REST Framework API?
Use drf-spectacular or drf-yasg to auto-generate OpenAPI/Swagger documentation. Add docstrings and descriptions to serializers and viewsets. Configure documentation URL in settings. Clients can explore and test endpoints interactively.
// Using drf-spectacular (Recommended)
pip install drf-spectacular
# settings.py
INSTALLED_APPS = [
'drf_spectacular',
'rest_framework',
]
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}
# urls.py
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView, SpectacularRedocView
urlpatterns = [
# Schema endpoints
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
path('api/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
# API endpoints
path('api/', include('myapp.urls')),
]
// Adding Documentation to ViewSet
from drf_spectacular.utils import extend_schema, OpenApiParameter
class BookViewSet(viewsets.ModelViewSet):
'''A viewset for viewing and editing books.'''
queryset = Book.objects.all()
serializer_class = BookSerializer
@extend_schema(
summary='List all books',
description='Returns a paginated list of all books in the system.',
parameters=[
OpenApiParameter(
name='author',
description='Filter by author name',
required=False,
),
],
)
def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)
@extend_schema(
summary='Create a new book',
description='Creates and returns a new book object.',
)
def create(self, request, *args, **kwargs):
return super().create(request, *args, **kwargs)
// Documenting Serializers
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
class BookDetailedSerializer(serializers.ModelSerializer):
'''Detailed book serializer with all information.'''
author = serializers.CharField(
help_text='Full name of the author',
max_length=100
)
price = serializers.DecimalField(
max_digits=10,
decimal_places=2,
help_text='Price in USD'
)
class Meta:
model = Book
fields = ['id', 'title', 'author', 'price', 'created_at']
// Using drf-yasg (Alternative)
pip install drf-yasg
# settings.py
INSTALLED_APPS = [
'drf_yasg',
'rest_framework',
]
# urls.py
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
schema_view = get_schema_view(
openapi.Info(
title='Book API',
default_version='v1',
description='API for managing books',
contact=openapi.Contact(email='contact@example.com'),
),
public=True,
)
urlpatterns = [
path('api/docs/', schema_view.with_ui('swagger', cache_timeout=0)),
path('api/redoc/', schema_view.with_ui('redoc', cache_timeout=0)),
]
// Accessing Documentation
// Swagger UI: http://localhost:8000/api/docs/
// ReDoc: http://localhost:8000/api/redoc/
// OpenAPI Schema: http://localhost:8000/api/schema/
// Custom Schema Descriptions
from drf_spectacular.utils import extend_schema, extend_schema_serializer
@extend_schema_serializer(
examples=[
{
'value': {
'id': 1,
'title': 'Django for Beginners',
'author': 'John Smith',
'price': '29.99',
'created_at': '2024-01-15T10:30:00Z'
}
}
]
)
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
Q9. How do you handle CORS (Cross-Origin Resource Sharing) in Django REST Framework?
CORS allows APIs to be accessed from different domains. Use django-cors-headers middleware to enable CORS. Configure allowed origins, methods, and headers in settings to allow cross-origin requests securely.
// Install django-cors-headers
pip install django-cors-headers
// settings.py
INSTALLED_APPS = [
'corsheaders',
'django.contrib.admin',
'rest_framework',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # Must be early in the list
'django.middleware.common.CommonMiddleware',
# ... other middleware
]
// Allow all origins (Development only)
CORS_ALLOW_ALL_ORIGINS = True
// Allow specific origins (Production)
CORS_ALLOWED_ORIGINS = [
'http://localhost:3000',
'http://localhost:8080',
'https://example.com',
'https://www.example.com',
]
// Allow credentials (cookies, auth headers)
CORS_ALLOW_CREDENTIALS = True
// Allowed methods
CORS_ALLOW_METHODS = [
'GET',
'POST',
'PUT',
'PATCH',
'DELETE',
'OPTIONS',
]
// Allowed headers
CORS_ALLOW_HEADERS = [
'accept',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
]
// Advanced CORS Configuration
CORS_ALLOWED_ORIGIN_REGEXES = [
r'^https://\w+\.example\.com$', # Allow any subdomain
]
CORS_EXPOSE_HEADERS = ['X-Total-Count'] # Expose custom headers
CORS_MAX_AGE = 3600 # Preflight cache duration
// Per-View CORS Configuration
from corsheaders.decorators import ensure_csrf_cookie
@ensure_csrf_cookie
def get_csrf_token(request):
return JsonResponse({'csrfToken': get_token(request)})
// Test CORS
# Frontend JavaScript
fetch('http://localhost:8000/api/books/', {
method: 'GET',
headers: {
'Authorization': 'Token abc123',
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.catch(error => console.error('CORS Error:', error));
// CORS Errors to Handle
// Access-Control-Allow-Origin header missing
// Credentials mode is 'include' but... not allowed
// Method not allowed by CORS policy
// Headers not allowed by CORS policy
Django REST Framework - API Development
What is Django REST Framework and why should you use it for API development?
Django REST Framework (DRF) is a powerful, flexible toolkit for building REST APIs with Django. It provides se...
What is the difference between Serializer and ModelSerializer in DRF?
Serializer manually defines fields for any data structure. ModelSerializer automatically generates fields from...
Explain APIView vs ViewSets in Django REST Framework. When do you use each?
APIView is a low-level class-based view for complete control over request/response handling. ViewSet is a high...
How do you implement authentication and permissions in Django REST Framework?
Authentication identifies who the user is (TokenAuthentication, JWTAuthentication, SessionAuthentication). Per...
How do you implement pagination, filtering, and search in DRF?
Pagination breaks large result sets into pages. Filtering narrows results based on specific criteria. Search p...
How do you handle errors and use HTTP status codes correctly in DRF APIs?
DRF provides automatic error handling with ValidationError for invalid data and status codes (200, 201, 204, 4...
How do you test Django REST Framework APIs effectively?
Use APITestCase and APIClient for testing DRF views. Mock external services, test authentication/permissions,...
How do you generate and provide API documentation for a Django REST Framework API?
Use drf-spectacular or drf-yasg to auto-generate OpenAPI/Swagger documentation. Add docstrings and description...
How do you handle CORS (Cross-Origin Resource Sharing) in Django REST Framework?
CORS allows APIs to be accessed from different domains. Use django-cors-headers middleware to enable CORS. Con...