Мы научились использовать базовые классы пагинации в DRF, такие как PageNumberPagination и LimitOffsetPagination, а также кастомизировали их для более гибкой настройки. Отсюда и плавно переходим к нашей новой теме: фильтрация данных. Потому что, знаете ли, мало данных порционно отдавать—хорошо бы ещё давать пользователю только то, что ему нужно, а не "всё и сразу".
Фильтрация в DRF
Django REST Framework (DRF) поддерживает фильтрацию данных прямо "из коробки". В DRF для фильтрации уже подготовлены механизмы, такие как DjangoFilterBackend, SearchFilter и OrderingFilter. Эти инструменты можно комбинировать, чтобы не просто фильтровать данные, но и сортировать их по полям API, а также искать по конкретным значениям. Мы начнем с основ, а затем будем двигаться к более сложным и кастомным сценариям.
Простейшая ручная фильтрация
Начнем с базового примера. Если вдруг вам не нужно использовать готовые инструменты фильтрации или вы хотите реализовать что-то простое, можно добавить фильтрацию прямо в метод вашего представления.
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Product
class ProductListView(APIView):
def get(self, request):
# Получаем необработанные данные из модели
products = Product.objects.all()
# Проверяем, есть ли параметр фильтрации "category"
category = request.query_params.get('category')
if category:
products = products.filter(category=category)
# Преобразуем данные в Python, чтобы отдать их клиенту
data = [{"id": p.id, "name": p.name, "category": p.category} for p in products]
return Response(data)
Здесь происходит следующее:
- Мы берем все записи из модели
Product. - Проверяем, передал ли пользователь параметр
categoryв строке запроса (?category=some-category). - Если параметр есть, фильтруем данные по категории.
Довольно просто, не так ли? Однако если вы хотите добавить больше возможностей фильтрации, например, фильтр по цене, названию, производителю, то ваш код быстро превратится в кашу из условий. Поэтому для масштабируемости лучше обратиться за помощью к инструментам DRF.
Поддержка фильтрации в DRF
DjangoFilterBackend—это встроенный инструмент DRF для работы с фильтрацией. Он позволяет подключить фильтрацию на уровне представлений и использовать готовые фильтры, определяемые через специальные классы. Это делает код чище и позволяет сосредоточиться на логике.
Прежде чем мы начнем с настройкой, убедитесь, что у вас установлена библиотека django-filter. Если не установлен, то вам на помощь приходит команда:
pip install django-filter
Затем добавьте django_filters в список INSTALLED_APPS вашего settings.py:
INSTALLED_APPS = [
# Другие приложения
'django_filters',
]
Вуаля, теперь мы готовы к фильтрации!
Настройка фильтрации с DjangoFilterBackend
Для начала подключим DjangoFilterBackend к нашему проекту. В DRF бэкенды фильтрации определяются в настройке DEFAULT_FILTER_BACKENDS в settings.py:
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
],
}
Это позволяет задавать фильтры в любом из ваших представлений.
Простая фильтрация
Используем DjangoFilterBackend в нашем представлении. Допустим, у нас есть модель Product:
# models.py
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=100)
category = models.CharField(max_length=50)
price = models.DecimalField(max_digits=10, decimal_places=2)
Теперь добавим фильтрацию в представление:
# views.py
from rest_framework.generics import ListAPIView
from django_filters.rest_framework import DjangoFilterBackend
from .models import Product
from .serializers import ProductSerializer
class ProductListView(ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
filter_backends = [DjangoFilterBackend] # Подключаем бэкенд фильтрации
filterset_fields = ['category', 'price'] # Указываем доступные поля фильтрации
Теперь вы можете обращаться к API с помощью таких запросов:
GET /api/products?category=electronics—получить все продукты в категории "electronics".GET /api/products?price=99.99—получить все продукты с ценой 99.99.
Как видите, все уже работает! А еще код стал чище, чем в нашем базовом примере.
Кастомизация фильтрации с FilterSet
Если нужно больше контроля над логикой фильтрации, можно создавать кастомные наборы фильтров (filter sets), используя библиотеку django-filter.
# filters.py
import django_filters
from .models import Product
class ProductFilter(django_filters.FilterSet):
min_price = django_filters.NumberFilter(field_name="price", lookup_expr='gte')
max_price = django_filters.NumberFilter(field_name="price", lookup_expr='lte')
class Meta:
model = Product
fields = ['category', 'min_price', 'max_price']
Мы добавили два кастомных фильтра: min_price (цена больше или равна) и max_price (цена меньше или равна). Теперь подключим эти фильтры в нашем представлении:
# views.py
from .filters import ProductFilter
class ProductListView(ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
filter_backends = [DjangoFilterBackend]
filterset_class = ProductFilter # Указываем наш кастомный класс фильтров
Теперь доступны фильтры вроде:
GET /api/products?min_price=50&max_price=200—продукты с ценой между 50 и 200.GET /api/products?category=books&min_price=10—книги с ценой от 10.
Подсказка про отладку фильтров
Одна из типичных проблем при работе с фильтрацией — это отсутствие данных, потому что фильтр не сработал. Если видите пустой список результатов, первым делом проверьте:
- Что параметр фильтрации передается корректно
?category=something. - Что данные в базе соответствуют значению фильтра.
Если что-то не сходится, попробуйте отладить запрос на стороне Django ORM: возьмите получившийся QuerySet и выполните его в Django shell.
Преимущества подхода
Фильтрация с помощью DjangoFilterBackend сильно упрощает жизнь:
- Чистый и понятный код.
- Мощные возможности кастомизации.
- Быстрая интеграция фильтров в API.
Двигаясь дальше, мы углубимся в дополнительные возможности, такие как поиск, сортировка и кастомные фильтры. Мы только начали раскрывать потенциал фильтрации!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ