⚖️ Старый 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 = [] явно.