⚖️ Старый vs Новый: Эволюция представлений DRF
⚡ Ключевые изменения
- APIView (ручной CRUD) → ModelViewSet (автоматический)
- Ручные URL для каждого метода → DefaultRouter генерирует автоматически
- Отдельные view-классы для list/detail → ListCreateAPIView + RetrieveUpdateDestroyAPIView
- Ручная фильтрация через queryset → DjangoFilterBackend + filterset_fields
1. Ручные CRUD-представления → ModelViewSet
Старый подход (из лекции)
# Много кода: отдельный класс для каждого действия
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class CategoryListView(APIView):
def get(self, request):
categories = Category.objects.all()
serializer = CategorySerializer(categories, many=True)
return Response(serializer.data)
def post(self, request):
serializer = CategorySerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=201)
return Response(serializer.errors, status=400)
class CategoryDetailView(APIView):
def get_object(self, pk):
try:
return Category.objects.get(pk=pk)
except Category.DoesNotExist:
raise Http404
def get(self, request, pk):
category = self.get_object(pk)
serializer = CategorySerializer(category)
return Response(serializer.data)
def put(self, request, pk):
category = self.get_object(pk)
serializer = CategorySerializer(category, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=400)
def delete(self, request, pk):
category = self.get_object(pk)
category.delete()
return Response(status=204)
Современный подход (DRF 3.x)
# Минимальный код: всё в одном классе
from rest_framework import viewsets
from .models import Category
from .serializers import CategorySerializer
class CategoryViewSet(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
# 3 строки кода вместо 40+
# DRF автоматически реализует:
# - list (GET /categories/)
# - create (POST /categories/)
# - retrieve (GET /categories/{pk}/)
# - update (PUT /categories/{pk}/)
# - partial_update (PATCH /categories/{pk}/)
# - destroy (DELETE /categories/{pk}/)
2. Ручные URL → DefaultRouter
Старый подход
# urls.py — каждый URL прописывается вручную
from django.urls import path
from .views import CategoryListView, CategoryDetailView
urlpatterns = [
path('categories/', CategoryListView.as_view()),
path('categories/<int:pk>/', CategoryDetailView.as_view()),
# Плюс PATCH нужно добавлять отдельно...
]
Современный подход
# urls.py — Router генерирует все URL
from rest_framework.routers import DefaultRouter
from .views import CategoryViewSet
router = DefaultRouter()
router.register(r'categories', CategoryViewSet)
urlpatterns = [
path('', include(router.urls)),
]
# Все 6 URL созданы автоматически
3. Ручная фильтрация → DjangoFilterBackend
Старый подход
# Ручная фильтрация в get_queryset()
class ProductListView(ListAPIView):
serializer_class = ProductSerializer
def get_queryset(self):
queryset = Product.objects.all()
category = self.request.query_params.get('category')
price = self.request.query_params.get('price')
if category:
queryset = queryset.filter(category=category)
if price:
queryset = queryset.filter(price=price)
return queryset
# Много boilerplate, нет документации в Browsable API
Современный подход
# DjangoFilterBackend — декларативно
from django_filters.rest_framework import DjangoFilterBackend
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['category', 'price']
# Автоматически:
# - валидация query-параметров
# - документация в Browsable API
# - фильтрация работает через ?field=value
Итог: Лекция показывает оба подхода для понимания основ. В реальных проектах на Django 5.x + DRF 3.15+ используются
ModelViewSet, DefaultRouter и DjangoFilterBackend. Ручные APIView остаются нужными только для нестандартных эндпоинтов, которые не вписываются в CRUD-паттерн.