Difficulty: Advanced
Django REST Framework (DRF) is the most popular third-party package for building RESTful APIs with Django. It provides serializers, views, viewsets, routers, authentication, permissions, pagination, filtering, and throttling - everything you need for a production-quality API.
Install DRF with 'pip install djangorestframework' and add 'rest_framework' to INSTALLED_APPS. DRF configuration lives in settings.py under the REST_FRAMEWORK dictionary, where you can set default authentication, permissions, pagination, and other global settings.
Serializers are the core of DRF. They convert complex data types (like Django model instances and QuerySets) to Python native types that can be rendered into JSON, XML, or other content types. They also handle deserialization - converting parsed data back into complex types after validating the incoming data.
Serializer is the base class. You define fields explicitly, similar to Django forms. Each field type (CharField, IntegerField, DateTimeField, etc.) handles validation and conversion. ModelSerializer is a shortcut that automatically generates serializer fields from a model, similar to how ModelForm works with Django forms.
ModelSerializer introspects the model and creates fields automatically. You specify the model and fields in a Meta class. It generates validators from model constraints and provides default create() and update() methods. You can override fields, add extra fields, or customize validation just like with regular Serializers.
Serializer validation follows a similar pattern to Django forms: field-level validation with validate_fieldname() methods, and object-level validation with the validate() method. The validate() method receives the full dictionary of validated data and can check cross-field relationships.
Nested serializers allow you to represent related objects inline. A field defined as another serializer class will include the related object's data in the output. For write operations with nested data, you need to override the create() and update() methods to handle the nested creation/update logic.
# Install: pip install djangorestframework
# settings.py
INSTALLED_APPS = [
# ...
'rest_framework',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 20,
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
],
}
Add 'rest_framework' to INSTALLED_APPS and configure defaults in REST_FRAMEWORK. BrowsableAPIRenderer provides a web-based interface for testing your API. Default settings apply to all API views unless overridden.
from rest_framework import serializers
from .models import Article, Category
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['id', 'name', 'slug']
class ArticleSerializer(serializers.ModelSerializer):
# Read-only computed field
author_name = serializers.CharField(source='author.get_full_name', read_only=True)
# Nested serializer (read-only by default)
category = CategorySerializer(read_only=True)
# Write-only field for setting category by ID
category_id = serializers.PrimaryKeyRelatedField(
queryset=Category.objects.all(),
source='category',
write_only=True,
)
class Meta:
model = Article
fields = [
'id', 'title', 'slug', 'content', 'excerpt',
'author', 'author_name', 'category', 'category_id',
'is_published', 'created_at', 'updated_at',
]
read_only_fields = ['author', 'created_at', 'updated_at']
ModelSerializer auto-generates fields from the model. 'source' maps a serializer field to a model attribute. read_only_fields prevent those fields from being set via the API. Nested serializer shows full category data in responses.
from rest_framework import serializers
from .models import Article
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ['id', 'title', 'content', 'status']
def validate_title(self, value):
"""Field-level validation."""
if len(value) < 5:
raise serializers.ValidationError(
'Title must be at least 5 characters.'
)
return value
def validate(self, data):
"""Object-level validation."""
if data.get('status') == 'published' and not data.get('content'):
raise serializers.ValidationError(
'Published articles must have content.'
)
return data
def create(self, validated_data):
"""Custom create with request user."""
validated_data['author'] = self.context['request'].user
return super().create(validated_data)
def update(self, instance, validated_data):
"""Custom update logic."""
instance.title = validated_data.get('title', instance.title)
instance.content = validated_data.get('content', instance.content)
instance.save()
return instance
validate_fieldname() validates individual fields. validate() validates across fields. create() and update() handle object creation/modification. self.context['request'] gives access to the HTTP request.
from .models import Article
from .serializers import ArticleSerializer
# Serialization (object -> JSON data)
article = Article.objects.get(pk=1)
serializer = ArticleSerializer(article)
serializer.data
# {'id': 1, 'title': 'Hello', 'content': '...', ...}
# Serialization of multiple objects
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
serializer.data
# [{'id': 1, ...}, {'id': 2, ...}, ...]
# Deserialization (JSON data -> object)
data = {'title': 'New Article', 'content': 'Body text'}
serializer = ArticleSerializer(data=data)
serializer.is_valid(raise_exception=True)
article = serializer.save()
# Update existing object
serializer = ArticleSerializer(article, data=data, partial=True)
serializer.is_valid(raise_exception=True)
updated = serializer.save()
Pass an instance for serialization (reading). Pass data= for deserialization (writing). many=True handles lists. partial=True allows partial updates (PATCH). is_valid() runs validation and raise_exception=True auto-returns 400 errors.
DRF, Serializer, ModelSerializer, Validation, Nested Serializers