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

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

⚡ Главные отличия старого и нового

  • Пагинация в APIView: из лекции — множественное наследование APIView, PageNumberPagination; современный способ — pagination_class на Generic View
  • request.GET vs request.query_params: в DRF request.query_params предпочтительнее request.GET
  • Валидация sort_by: из лекции — без валидации; современный способ — белый список полей или OrderingFilter
  • Фильтрация: из лекции — ручная через словарь; современный способ — django-filter или DRF SearchFilter

1. Пагинация в APIView

Из лекции (работает, но не идиоматично)

# Множественное наследование
class BookListView(APIView, PageNumberPagination):
    page_size = 5

    def get(self, request):
        books = Book.objects.all()
        page_size = self.get_page_size(request)
        self.page_size = page_size
        results = self.paginate_queryset(
            books, request, view=self
        )
        serializer = BookDetailSerializer(results, many=True)
        return self.get_paginated_response(
            serializer.data
        )

    def get_page_size(self, request):
        page_size = request.query_params.get('page_size')
        if page_size and page_size.isdigit():
            return int(page_size)
        return self.page_size

Современный способ (DRF 3.15+)

# Через pagination_class на Generic View
from rest_framework.generics import ListAPIView
from rest_framework.pagination import PageNumberPagination

class BookPagination(PageNumberPagination):
    page_size = 5
    page_size_query_param = 'page_size'
    max_page_size = 100

class BookListView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookDetailSerializer
    pagination_class = BookPagination

Или глобально в settings.py:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS':
        'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10
}
Итог: паттерн из лекции с множественным наследованием — учебный пример, он работает. В продакшн-коде предпочтительно использовать pagination_class на Generic View или глобальную настройку.

2. request.GET vs request.query_params

Устаревший (Django-стиль)

# В чистом Django (FBV или CBV без DRF)
def book_list(request):
    author = request.GET.get('author')
    ...

В DRF request — это обёрнутый объект, и request.GET работает, но это Django-стиль, не DRF-стиль.

Современный (DRF-стиль)

# В DRF APIView
class BookListView(APIView):
    def get(self, request):
        author = request.query_params.get('author')
        ...

request.query_params — предпочтительное имя в DRF. Семантически более явное.

3. Сортировка: без валидации vs с валидацией

Из лекции (без валидации)

class BookListView(APIView):
    def get(self, request):
        sort_by = request.query_params.get(
            'sort_by', 'title'
        )
        sort_order = request.query_params.get(
            'sort_order', 'asc'
        )
        books = Book.objects.all()
        if sort_order == 'desc':
            sort_by = f'-{sort_by}'
        books = books.order_by(sort_by)
        ...

Проблема: клиент может передать произвольное поле, в т.ч. несуществующее — вызовет ошибку БД.

С белым списком (безопасно)

ALLOWED_SORT_FIELDS = {
    'title', 'price', 'published_date', 'author'
}

class BookListView(APIView):
    def get(self, request):
        sort_by = request.query_params.get(
            'sort_by', 'title'
        )
        sort_order = request.query_params.get(
            'sort_order', 'asc'
        )
        # Валидация
        if sort_by not in ALLOWED_SORT_FIELDS:
            sort_by = 'title'
        if sort_order == 'desc':
            sort_by = f'-{sort_by}'
        books = Book.objects.all().order_by(sort_by)
        ...

Или используйте OrderingFilter из DRF — декларативный и безопасный.

4. Ручная фильтрация vs django-filter

Из лекции (ручная)

class BookListView(APIView):
    def get(self, request):
        filters = {}
        author = request.query_params.get('author')
        if author:
            filters['author'] = author
        books = Book.objects.filter(**filters)
        ...

Подходит для простых случаев. При росте числа параметров код разрастается.

Декларативный (django-filter)

# pip install django-filter
import django_filters
from rest_framework import filters
from rest_framework.generics import ListAPIView

class BookFilter(django_filters.FilterSet):
    author = django_filters.CharFilter(
        field_name='author',
        lookup_expr='icontains'
    )
    pub_year = django_filters.NumberFilter(
        field_name='published_date',
        lookup_expr='year'
    )
    class Meta:
        model = Book
        fields = ['author', 'pub_year']

class BookListView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [
        django_filters.rest_framework.DjangoFilterBackend,
        filters.OrderingFilter,
    ]
    filterset_class = BookFilter
    ordering_fields = ['title', 'price']
⚠️ Проверить по документации: django-filter требует отдельной установки и настройки в INSTALLED_APPS.