Django REST Framework Integration
Django i18n Fields provides first-class Django REST Framework (DRF) support with automatic serialization in the active language.
Quick Setup
Basic Serializer
Use LocalizedModelSerializer for automatic handling of localized fields:
# serializers.py
from i18n_fields.drf import LocalizedModelSerializer
from .models import Article
class ArticleSerializer(LocalizedModelSerializer):
class Meta:
model = Article
fields = ['id', 'title', 'content', 'created_at']
Output (with English active):
{
"id": 1,
"title": "Hello World",
"content": "This is the content.",
"created_at": "2025-01-28T10:00:00Z"
}
How It Works
Automatic Translation
LocalizedModelSerializer automatically:
Serializes localized fields to simple values (not dicts)
Uses the active language from Django’s translation system
Handles all localized field types correctly
Supports DRF Spectacular for API documentation
# With active language set to Spanish
from django.utils import translation
with translation.override('es'):
serializer = ArticleSerializer(article)
print(serializer.data['title']) # "Hola Mundo"
Field Mapping
The serializer maps localized fields to appropriate DRF fields:
LocalizedCharField→CharFieldLocalizedTextField→CharFieldLocalizedIntegerField→IntegerFieldLocalizedFloatField→FloatFieldLocalizedBooleanField→BooleanFieldLocalizedFileField→FileFieldLocalizedUniqueSlugField→SlugField
ViewSets
Basic ViewSet
Use with any DRF viewset:
# views.py
from rest_framework import viewsets
from .models import Article
from .serializers import ArticleSerializer
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
Language Negotiation
Use Django’s language negotiation with the Accept-Language header:
# views.py
from rest_framework import viewsets
from django.utils import translation
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
def get_serializer_context(self):
context = super().get_serializer_context()
# Language is already set from middleware
context['language'] = translation.get_language()
return context
API Request Example:
curl -H "Accept-Language: es" http://api.example.com/articles/1/
Response:
{
"id": 1,
"title": "Hola Mundo",
"content": "Este es el contenido."
}
Creating Objects
Simple Input
Send translations as a dictionary:
# POST /api/articles/
{
"title": {
"en": "Hello World",
"es": "Hola Mundo"
},
"content": {
"en": "Content in English",
"es": "Contenido en español"
}
}
The serializer automatically converts dictionaries to LocalizedValue objects.
Single Language Input
Send a string to set only the active language:
# POST /api/articles/ (with Accept-Language: en)
{
"title": "Hello World",
"content": "Content in English"
}
This sets values only for English.
Partial Updates
Update specific languages:
# PATCH /api/articles/1/
{
"title": {
"es": "Nuevo Título" # Only update Spanish
}
}
Custom Serializers
Field-Level Customization
Override specific fields:
from rest_framework import serializers
from i18n_fields.drf import LocalizedModelSerializer
class ArticleSerializer(LocalizedModelSerializer):
# Custom read-only field
summary = serializers.SerializerMethodField()
class Meta:
model = Article
fields = ['id', 'title', 'content', 'summary']
def get_summary(self, obj):
# Returns translated content
return obj.content.translate()[:100]
Full Translation Output
Return all translations in the response:
from rest_framework import serializers
from i18n_fields.drf import LocalizedModelSerializer
class ArticleDetailSerializer(LocalizedModelSerializer):
all_translations = serializers.SerializerMethodField()
class Meta:
model = Article
fields = ['id', 'title', 'content', 'all_translations']
def get_all_translations(self, obj):
return {
'title': dict(obj.title),
'content': dict(obj.content)
}
Output:
{
"id": 1,
"title": "Hello World",
"content": "Content in English",
"all_translations": {
"title": {
"en": "Hello World",
"es": "Hola Mundo",
"fr": "Bonjour le Monde"
},
"content": {
"en": "Content in English",
"es": "Contenido en español"
}
}
}
Nested Serializers
Localized fields work in nested serializers:
class AuthorSerializer(LocalizedModelSerializer):
class Meta:
model = Author
fields = ['id', 'name', 'bio']
class ArticleSerializer(LocalizedModelSerializer):
author = AuthorSerializer(read_only=True)
class Meta:
model = Article
fields = ['id', 'title', 'content', 'author']
Output:
{
"id": 1,
"title": "Hello World",
"content": "Content",
"author": {
"id": 1,
"name": "John Doe",
"bio": "Author bio in English"
}
}
Filtering
Filter by Language
Use query parameters to filter by specific languages:
from rest_framework import viewsets
from django_filters import rest_framework as filters
class ArticleFilter(filters.FilterSet):
title_en = filters.CharFilter(field_name='title__en', lookup_expr='icontains')
title_es = filters.CharFilter(field_name='title__es', lookup_expr='icontains')
class Meta:
model = Article
fields = ['title_en', 'title_es']
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
filterset_class = ArticleFilter
Usage:
# Filter by English title
GET /api/articles/?title_en=hello
# Filter by Spanish title
GET /api/articles/?title_es=hola
Ordering
Order by Localized Fields
from rest_framework import viewsets
from i18n_fields import L
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
def get_queryset(self):
queryset = super().get_queryset()
# Order by title in current language
return queryset.order_by(L('title'))
DRF Spectacular Integration
Automatic Schema Generation
Django i18n Fields works seamlessly with DRF Spectacular:
# settings.py
INSTALLED_APPS = [
# ...
'drf_spectacular',
'i18n_fields',
]
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}
SPECTACULAR_SETTINGS = {
'TITLE': 'Your API',
'VERSION': '1.0.0',
}
The schema will show localized fields as simple types (string, integer, etc.) in the active language.
Custom Schema
Customize the schema for localized fields:
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
class ArticleSerializer(LocalizedModelSerializer):
@extend_schema_field(serializers.CharField)
title = serializers.CharField()
class Meta:
model = Article
fields = ['id', 'title', 'content']
Pagination
Localized fields work with pagination:
from rest_framework.pagination import PageNumberPagination
class ArticlePagination(PageNumberPagination):
page_size = 10
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
pagination_class = ArticlePagination
Search
Search Across Languages
from rest_framework import filters
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
filter_backends = [filters.SearchFilter]
search_fields = ['title__en', 'title__es', 'content__en']
Usage:
GET /api/articles/?search=hello
Permissions
Permissions work normally with localized fields:
from rest_framework import permissions
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
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
permission_classes = [IsAuthorOrReadOnly]
Complete Example
Here’s a full example with all features:
# serializers.py
from rest_framework import serializers
from i18n_fields.drf import LocalizedModelSerializer
from .models import Article, Category
class CategorySerializer(LocalizedModelSerializer):
class Meta:
model = Category
fields = ['id', 'name']
class ArticleListSerializer(LocalizedModelSerializer):
category = CategorySerializer(read_only=True)
author_name = serializers.CharField(source='author.name', read_only=True)
class Meta:
model = Article
fields = ['id', 'title', 'category', 'author_name', 'created_at']
class ArticleDetailSerializer(LocalizedModelSerializer):
category = CategorySerializer(read_only=True)
all_translations = serializers.SerializerMethodField()
class Meta:
model = Article
fields = [
'id', 'title', 'content', 'category',
'author', 'created_at', 'all_translations'
]
def get_all_translations(self, obj):
return {
'title': dict(obj.title),
'content': dict(obj.content),
}
# views.py
from rest_framework import viewsets, filters
from django_filters import rest_framework as django_filters
from i18n_fields import L
from .models import Article
from .serializers import ArticleListSerializer, ArticleDetailSerializer
class ArticleFilter(django_filters.FilterSet):
title = django_filters.CharFilter(field_name='title__en', lookup_expr='icontains')
class Meta:
model = Article
fields = ['category', 'author']
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.select_related('author', 'category')
filter_backends = [django_filters.DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_class = ArticleFilter
search_fields = ['title__en', 'title__es', 'content__en']
ordering_fields = ['created_at']
def get_serializer_class(self):
if self.action == 'list':
return ArticleListSerializer
return ArticleDetailSerializer
def get_queryset(self):
queryset = super().get_queryset()
return queryset.order_by(L('title'))
# urls.py
from rest_framework.routers import DefaultRouter
from .views import ArticleViewSet
router = DefaultRouter()
router.register(r'articles', ArticleViewSet)
urlpatterns = router.urls
Best Practices
Use LocalizedModelSerializer
Always use the specialized serializer for models with localized fields:
# Good class ArticleSerializer(LocalizedModelSerializer): pass # Missing automatic translation class ArticleSerializer(serializers.ModelSerializer): pass
Language Negotiation
Use Django’s middleware for automatic language detection:
MIDDLEWARE = [ # ... 'django.middleware.locale.LocaleMiddleware', ]
Return All Translations for Admin
Provide endpoints that return all translations for admin interfaces:
class ArticleAdminSerializer(LocalizedModelSerializer): translations = serializers.SerializerMethodField() def get_translations(self, obj): return {'title': dict(obj.title)}
Validate Required Languages
Add custom validation for required languages:
def validate_title(self, value): if not value.get('en'): raise serializers.ValidationError('English title required') return value
Use Filtering and Search
Enable filtering by specific languages for better search:
search_fields = ['title__en', 'title__es', 'content__en']
Troubleshooting
Serializer Not Translating
Ensure you’re using LocalizedModelSerializer:
from i18n_fields.drf import LocalizedModelSerializer
Wrong Language Returned
Check the active language:
from django.utils import translation
print(translation.get_language())
DRF Not Installed
Install Django REST Framework:
pip install djangorestframework
Next Steps
Advanced Queries - Advanced querying techniques
Fields Reference - Complete field reference