⚖️ Старый vs Новый — Урок 29

← К оглавлению урока

Устаревшие паттерны из лекции (до DRF 3.x, Django 3.x) → современный подход (Django 5.x + DRF 3.15+)

⚡ Ключевые изменения

  • Обработка 404: вместо голого try/except — get_object_or_404() или get_object() в GenericAPIView
  • Дублирование кода в APIView — переиспользуй get_object() как вспомогательный метод
  • Нет partial=True при PATCH — используй serializer(instance, data, partial=True)
  • Ручная маршрутизация ViewSet — используй Router
  • FBV для стандартного CRUD — используй Generic Views

1. Повторяющийся try/except в каждом методе

Из лекции (старое)Современный подход
class BookDetailView(APIView):
    def get(self, request, pk):
        try:
            book = Book.objects.get(pk=pk)
        except Book.DoesNotExist:
            return Response({'error': 'Not found'},
                            status=404)
        ...

    def put(self, request, pk):
        try:
            book = Book.objects.get(pk=pk)
        except Book.DoesNotExist:
            return Response({'error': 'Not found'},
                            status=404)
        ...
class BookDetailView(APIView):
    def get_object(self, pk):
        """Вспомогательный метод — DRY"""
        try:
            return Book.objects.get(pk=pk)
        except Book.DoesNotExist:
            return None

    def get(self, request, pk):
        book = self.get_object(pk)
        if book is None:
            return Response({'error': 'Not found'},
                status=status.HTTP_404_NOT_FOUND)
        ...

    def put(self, request, pk):
        book = self.get_object(pk)
        if book is None:
            return Response({'error': 'Not found'},
                status=status.HTTP_404_NOT_FOUND)
        ...

2. Магические числа вместо статус-констант

Из лекции (старое)Современный подход
return Response(data, status=201)
return Response(error, status=400)
return Response(status=204)
from rest_framework import status

return Response(data, status=status.HTTP_201_CREATED)
return Response(error, status=status.HTTP_400_BAD_REQUEST)
return Response(status=status.HTTP_204_NO_CONTENT)
# Явно, читаемо, без «магических чисел»

3. PATCH без partial=True

Из лекции (старое)Современный подход
# Лекция показывает только PUT
# PATCH не рассматривается отдельно
def put(self, request, pk):
    serializer = BookSerializer(book, data=request.data)
    ...
# PUT — полное обновление (все поля обязательны)
def put(self, request, pk):
    serializer = BookSerializer(book, data=request.data)
    ...

# PATCH — частичное обновление (только изменяемые поля)
def patch(self, request, pk):
    serializer = BookSerializer(
        book,
        data=request.data,
        partial=True    # разрешает неполные данные
    )
    ...

4. Ручное дублирование CRUD вместо Generic Views

Из лекции (старое — APIView)Современный подход (Generic Views)
# Много boilerplate кода
class BookListCreateView(APIView):
    def get(self, request):
        books = Book.objects.all()
        s = BookSerializer(books, many=True)
        return Response(s.data)

    def post(self, request):
        s = BookSerializer(data=request.data)
        if s.is_valid():
            s.save()
            return Response(s.data, status=201)
        return Response(s.errors, status=400)
# Минимум кода — то же поведение
from rest_framework import generics

class BookListCreateView(generics.ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
# Готово. DRF реализует get() и post() автоматически.

Используйте APIView, когда нужна нестандартная логика. Для стандартного CRUD — Generic Views или ViewSet.

5. Ручные URL вместо Router для ViewSet

Старый подходСовременный подход (Router)
# Много ручной работы
urlpatterns = [
    path('books/', BookViewSet.as_view({'get': 'list', 'post': 'create'})),
    path('books/<int:pk>/', BookViewSet.as_view({
        'get': 'retrieve',
        'put': 'update',
        'patch': 'partial_update',
        'delete': 'destroy'
    })),
]
# Router генерирует всё автоматически
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'books', BookViewSet)

urlpatterns = [
    path('api/', include(router.urls)),
]
# Результат: все 6 URL-паттернов создаются автоматически

6. select_related / prefetch_related для оптимизации запросов

Из лекции (без оптимизации)Современный подход (с оптимизацией)
class BookListCreateView(APIView):
    def get(self, request):
        books = Book.objects.all()  # N+1 проблема!
        serializer = BookSerializer(books, many=True)
        return Response(serializer.data)
class BookListCreateView(APIView):
    def get(self, request):
        # select_related — для ForeignKey (publisher)
        # prefetch_related — для ManyToMany (genres)
        books = Book.objects.select_related('publisher')\
                            .prefetch_related('genres')\
                            .all()
        serializer = BookSerializer(books, many=True)
        return Response(serializer.data)
⚠️ Проверить по документации: DRF 3.15+ добавил улучшения в работе с nested serializers и throttling. Для актуальных примеров pagination_class, filter_backends и permission_classes см. официальную документацию DRF 3.15+.