DjangoMiddleSystem design
Как реализовать версионирование в Django REST API?
Django REST Framework поддерживает версионирование через URLPath, Namespace, QueryParameter и Accept-заголовок. Версия извлекается из request.version и используется для маршрутизации логики.
Версионирование в Django REST Framework
DRF предоставляет четыре встроенных класса версионирования, настраиваемых глобально через DEFAULT_VERSIONING_CLASS или на уровне конкретного view через атрибут versioning_class.
Способы версионирования
- URLPathVersioning — версия в пути:
/api/v1/users/ - NamespaceVersioning — версия через namespace Django URL
- QueryParameterVersioning —
/api/users/?version=v1 - AcceptHeaderVersioning —
Accept: application/json; version=v1
Настройка URLPathVersioning
# settings.py
REST_FRAMEWORK = {
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
"DEFAULT_VERSION": "v1",
"ALLOWED_VERSIONS": ["v1", "v2"],
"VERSION_PARAM": "version",
}
# urls.py
from django.urls import path, include
urlpatterns = [
path("api/<version>/", include("myapp.urls")),
]
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
class UserView(APIView):
def get(self, request, *args, **kwargs):
if request.version == "v2":
data = {"id": 1, "full_name": "Alice Smith", "email": "alice@example.com"}
else:
data = {"id": 1, "name": "Alice"}
return Response(data)
Версионирование на уровне ViewSet с разными сериализаторами
# serializers.py
from rest_framework import serializers
from .models import User
class UserSerializerV1(serializers.ModelSerializer):
class Meta:
model = User
fields = ["id", "username"]
class UserSerializerV2(serializers.ModelSerializer):
class Meta:
model = User
fields = ["id", "username", "email", "date_joined"]
# views.py
from rest_framework import viewsets
from .serializers import UserSerializerV1, UserSerializerV2
from .models import User
class UserViewSet(viewsets.ReadOnlyModelViewSet):
queryset = User.objects.all()
def get_serializer_class(self):
if self.request.version == "v2":
return UserSerializerV2
return UserSerializerV1
Роутер с версионированием
# urls.py
from rest_framework.routers import DefaultRouter
from .views import UserViewSet
router = DefaultRouter()
router.register(r"users", UserViewSet, basename="user")
urlpatterns = [
path("api/<version>/", include(router.urls)),
]
Подводные камни
- Забытый ALLOWED_VERSIONS — если не задать список, DRF принимает любую строку как валидную версию, и опечатка
v11не вызовет 404. - Дублирование бизнес-логики — ветвление
if request.versionразбросано по всему коду вместо инкапсуляции в отдельные view-классы или сериализаторы. - Устаревшие версии не удаляются — v1 живёт вечно; отсутствует политика deprecation с заголовком
Sunsetи сроком отключения. - AcceptHeaderVersioning ломает кэширование — CDN и прокси кэшируют ответы без учёта заголовка Accept, клиент получает данные чужой версии.
- Несогласованность URL-схемы — часть эндпоинтов версионирована, часть нет; документация (drf-spectacular) генерирует смешанную схему.
- Тесты не покрывают все версии — тест для v2 написан, для v1 забыт; регрессия обнаруживается в проде.
- DEFAULT_VERSION не соответствует реальному поведению — если клиент не передаёт версию, он получает v1, хотя бизнес-логика уже перешла на v2.
Common mistakes
- Описывать api versioning только как термин и не показывать механизм на минимальном примере.
- Игнорировать ошибки, пустые данные, конкурентный доступ или границы транзакции.
- Не связывать поведение с официальным контрактом Django и реальной эксплуатацией.
What the interviewer is testing
- Объясняет api versioning через последовательность действий, а не через набор ключевых слов.
- Приводит короткий кодовый пример или production-сценарий с ожидаемым поведением.
- Называет хотя бы один риск: производительность, безопасность, транзакции, память или сопровождение.