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

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

Устаревшие паттерны показаны только для понимания старого кода. В новых проектах используйте современный подход (DRF 3.15+, Django 5.x).

⚡ Главные изменения

  • Ручная фильтрация в get_queryset()filter_backends
  • Без super() в get_serializer_context() → всегда super().get_serializer_context()
  • get_object() без check_object_permissions → добавить явный вызов
  • Глобальная фильтрация через REST_FRAMEWORK['DEFAULT_FILTER_BACKENDS']

1. Фильтрация данных

Из лекции (старый способ) — ручная фильтрация в get_queryset()

# views.py — ручная фильтрация (старый подход)
class BookListView(ListAPIView):
    serializer_class = BookSerializer

    def get_queryset(self):
        queryset = Book.objects.all()
        author = self.request.query_params.get('author')
        if author:
            queryset = queryset.filter(author=author)
        search = self.request.query_params.get('search')
        if search:
            queryset = queryset.filter(
                Q(title__icontains=search) | Q(author__icontains=search)
            )
        return queryset

Современный подход (DRF 3.15+) — filter_backends

# views.py — автоматическая фильтрация через filter_backends
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters

class BookListView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [DjangoFilterBackend, filters.SearchFilter,
                       filters.OrderingFilter]
    filterset_fields = ['author', 'is_bestseller']
    search_fields = ['title', 'author']
    ordering_fields = ['price', 'published_date']

Почему лучше: меньше кода, автоматическая документация в DRF Browsable API, поддержка OpenAPI/Swagger, стандартизированные параметры запроса.

2. Контекст сериализатора без super()

Старый (неправильный) способ — без super()

# НЕПРАВИЛЬНО — теряются request, format, view
def get_serializer_context(self):
    return {'include_related': True}  # контекст пустой от базового класса

Современный подход — всегда вызывать super()

# ПРАВИЛЬНО
def get_serializer_context(self):
    context = super().get_serializer_context()  # {'request', 'format', 'view'}
    context['include_related'] = (
        self.request.query_params.get('include_related', 'false').lower() == 'true'
    )
    return context

Почему важно: request в контексте используется для генерации абсолютных URL в гиперссылочных полях (HyperlinkedRelatedField). Без него они сломаются.

3. get_object() без проверки разрешений

Старый способ — без check_object_permissions

# НЕБЕЗОПАСНО при использовании object-level permissions
def get_object(self):
    pk = self.kwargs.get('pk')
    try:
        return Book.objects.get(pk=pk, is_banned=False)
    except Book.DoesNotExist:
        raise NotFound("Not found.")

Современный подход — с явной проверкой разрешений

# БЕЗОПАСНО
def get_object(self):
    pk = self.kwargs.get('pk')
    try:
        obj = self.queryset.get(pk=pk, is_banned=False)
    except Book.DoesNotExist:
        raise NotFound(f"Book with id '{pk}' not found or is banned.")
    self.check_object_permissions(self.request, obj)  # явный вызов
    return obj

Почему важно: если в будущем к представлению добавятся object-level permissions (например, IsOwnerOrReadOnly), они не сработают без явного вызова check_object_permissions.

4. Глобальная настройка фильтров

Локальная настройка (на уровне представления)

# views.py — только для конкретного представления
class BookListView(ListAPIView):
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    ...

Глобальная настройка (современный подход)

# settings.py — применяется ко всем представлениям
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend',
        'rest_framework.filters.SearchFilter',
        'rest_framework.filters.OrderingFilter',
    ],
}
⚠️ Проверить по документации: при глобальной настройке фильтры применяются ко всем представлениям, включая те, где фильтрация не нужна. Для таких представлений задавайте filter_backends = [] явно.