Гайды
Django REST Framework: сериализаторы и viewsets
ModelSerializer и валидация, APIView, generic views, ModelViewSet и DefaultRouter, пагинация и версионирование API.
~11 мин чтения
Django REST Framework: сериализаторы и viewsets
Django REST Framework (DRF) добавляет слой сериализаторов, generic views, viewsets и роутеров поверх Django. Этот гайд — ядро API без углубления в permissions (их легко добавить по документации DRF). Основа Django — Django: первый проект; ORM — оптимизация запросов.
1. Установка
pip install djangorestframework
settings.py:
INSTALLED_APPS = [
...
"rest_framework",
]
2. Сериализатор
from rest_framework import serializers
from shop.models import Product
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ["id", "name", "price", "created_at"]
read_only_fields = ["id", "created_at"]
Serializer вручную — для нетипичных структур; ModelSerializer — быстрый старт.
Валидация:
def validate_price(self, value):
if value <= 0:
raise serializers.ValidationError("Price must be positive")
return value
3. APIView (явный контроль)
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class ProductListView(APIView):
def get(self, request):
qs = Product.objects.all()[:50]
return Response(ProductSerializer(qs, many=True).data)
def post(self, request):
ser = ProductSerializer(data=request.data)
ser.is_valid(raise_exception=True)
ser.save()
return Response(ser.data, status=status.HTTP_201_CREATED)
4. Generic views
from rest_framework import generics
class ProductListCreate(generics.ListCreateAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
class ProductDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
5. ViewSet и Router
from rest_framework import viewsets
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
urls.py:
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r"products", ProductViewSet, basename="product")
urlpatterns = router.urls
Получите /products/, /products/{pk}/, при DefaultRouter — и /products/{pk}/ с trailing slash по настройкам Django.
6. Пагинация
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 20,
}
Для больших списков рассмотрите cursor pagination.
7. Версионирование API
Заголовок Accept: application/vnd.myapi+json; version=1, поддомен или URL path /api/v1/ — закрепите в команде одну стратегию; см. REST: HATEOAS и версионирование.
8. Поля, фильтры, ограничения
SerializerMethodField — вычисляемое поле из get_<name>; не злоупотребляйте N+1: лучше annotate() в queryset и обычное поле сериализатора. django-filter + DjangoFilterBackend снимают с эндпоинта ручной разбор query string.
throttle_scope / ScopedRateThrottle` защищают публичные списки и поиск; для аутентифицированных пользователей комбинируйте с permissions и квотами на уровне API gateway — см. CORS и лимиты в FastAPI для параллелей.
Глобально в REST_FRAMEWORK:
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.AnonRateThrottle",
"rest_framework.throttling.UserRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {"anon": "100/hour", "user": "1000/hour"},
}
9. ViewSet: perform_* и владелец объекта
Не кладите бизнес-логику «кто может создать» только в сериализатор: в ModelViewSet переопределяйте perform_create (проставить user=request.user), perform_update (запрет смены owner чужим), get_queryset() — базовая фильтрация по тенанту/пользователю до любого retrieve.
class NoteViewSet(viewsets.ModelViewSet):
serializer_class = NoteSerializer
def get_queryset(self):
return Note.objects.filter(owner=self.request.user)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
10. Кастомные действия @action
Для подресурсов и не-CRUD операций используйте @action(detail=True|False, methods=["post"]) вместо «левых» URL вне router — так проще permissions и OpenAPI-схема (если подключён drf-spectacular и т.п.).
11. Чек-лист
- Не отдавать queryset с N+1 —
select_related/prefetch_relatedвget_queryset(). - Раздельные сериализаторы Read vs Write при разных полях.
-
perform_create/get_querysetсогласованы с моделью прав доступа. - Тесты API —
APITestCaseили pytest-django — см. тестирование FastAPI для идей по фикстурам.
Дальше: REST и дизайн API · тег DRF