За последние несколько занятий мы погрузились в мир пагинации и фильтрации данных в Django REST Framework (DRF). Мы узнали, что пагинация спасает приложения от перегрузки огромными объемами данных, разрезая их на части. Мы рассмотрели два популярных метода пагинации — PageNumberPagination и LimitOffsetPagination — а также научились создавать кастомные классы пагинации. Затем плавно перешли к фильтрации данных: изучили возможности DjangoFilterBackend, реализацию поиска с помощью SearchFilter и сортировки через OrderingFilter. Мы даже заглянули в премудрости кастомных фильтров и сложной фильтрации с использованием Q-объектов. Сегодня мы закрепим всё это на практике.
Постановка задачи
Давайте представим, что у нас есть API для онлайн-магазина, в котором пользователи могут искать товары, фильтровать их по категории, цене и бренду, а также получать данные с постраничной разбивкой. Нам нужно:
- Реализовать пагинацию для списка товаров.
- Добавить фильтрацию по категориям, цене (с использованием диапазонов) и брендам.
- Настроить поиск по названию товара.
- Включить возможность сортировки по цене и дате добавления.
Итак, вооружаемся терпением, Python и кофе, если он всё ещё не кончился.
Подготовка данных
Сначала убедимся, что у нас есть модель для работы. Если её нет, создадим её.
Модель Product
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=255)
brand = models.CharField(max_length=255)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="products")
price = models.DecimalField(max_digits=10, decimal_places=2)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
Эта модель позволит нам работать с товарами в магазине. Мы связали товары с категориями через ForeignKey и добавили необходимые атрибуты, такие как цена, бренд и дата добавления.
Настройка пагинации
Добавим глобальную пагинацию в файл settings.py. Мы будем использовать PageNumberPagination для простоты.
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10 # Показывать 10 товаров на странице
}
Теперь все наши представления, использующие пагинацию, будут по умолчанию выдавать по 10 объектов на страницу. Давайте проверим это.
Представление для списка товаров
Создадим представление для отображения списка товаров. Для начала создадим простой ViewSet и подключим его к роутеру.
from rest_framework.viewsets import ModelViewSet
from .models import Product
from .serializers import ProductSerializer
class ProductViewSet(ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
И подключим его в urls.py:
from rest_framework.routers import DefaultRouter
from .views import ProductViewSet
router = DefaultRouter()
router.register(r'products', ProductViewSet)
urlpatterns = router.urls
Проверяем API. Теперь оно должно возвращать данные с постраничной разбивкой, а параметры ?page=1, ?page=2 можно использовать для переключения страниц. Выглядит довольно стильно, не правда ли?
Реализация фильтрации
Установка DjangoFilterBackend
Убедимся, что мы установили и настроили django-filter. Если нет, выполняем:
pip install django-filter
Затем добавляем его в settings.py:
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
}
Создадим класс фильтров для нашей модели через библиотеку django-filter.
from django_filters import rest_framework as filters
from .models import Product
class ProductFilter(filters.FilterSet):
price_min = filters.NumberFilter(field_name="price", lookup_expr='gte')
price_max = filters.NumberFilter(field_name="price", lookup_expr='lte')
category = filters.CharFilter(field_name="category__name", lookup_expr='icontains')
brand = filters.CharFilter(lookup_expr='icontains')
class Meta:
model = Product
fields = ['price_min', 'price_max', 'category', 'brand']
Подключим наш фильтр к ProductViewSet.
from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend
from .models import Product
from .serializers import ProductSerializer
from .filters import ProductFilter
class ProductViewSet(ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
filter_backends = [DjangoFilterBackend]
filterset_class = ProductFilter
Теперь параметры фильтрации, такие как ?price_min=10&price_max=100&category=Electronics, начнут работать.
Настройка поиска
Добавим поиск по названию товара. Для этого используем встроенный SearchFilter.
from rest_framework.filters import SearchFilter
class ProductViewSet(ModelViewSet):
...
filter_backends = [DjangoFilterBackend, SearchFilter]
search_fields = ['name']
Теперь параметры ?search=iPhone будут выдавать только товары с соответствующими названиями. Приятный подарок для любителей Apple, да?
Сортировка данных
Последний штрих — поддержка сортировки. Используем OrderingFilter для этой задачи.
from rest_framework.filters import OrderingFilter
class ProductViewSet(ModelViewSet):
...
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
ordering_fields = ['price', 'created_at']
ordering = ['-created_at'] # По умолчанию сортируем по дате добавления (новые товары сверху)
Теперь можно сортировать данные с помощью параметров ?ordering=price или ?ordering=-price.
Кастомизация пагинации
Создадим кастомный класс пагинации, чтобы добавить дополнительную информацию в ответ API.
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
class CustomPagination(PageNumberPagination):
page_size = 10
def get_paginated_response(self, data):
return Response({
'links': {
'next': self.get_next_link(),
'previous': self.get_previous_link()
},
'total_items': self.page.paginator.count,
'total_pages': self.page.paginator.num_pages,
'current_page': self.page.number,
'results': data
})
И указать его в ProductViewSet.
class ProductViewSet(ModelViewSet):
...
pagination_class = CustomPagination
Теперь наш API отвечает с дополнительной информацией о текущей странице, общем количестве объектов и страниц. Клиенты, интегрирующие это API, будут вам благодарны.
Итоговый API
Теперь наш API поддерживает:
- Пагинацию с настройкой через параметры
settings.py. - Фильтрацию по цене, брендам и категориям.
- Поиск по названию товаров.
- Сортировку по цене и дате добавления.
- Кастомизацию пагинации для дополнительной информации.
Пример полного запроса:
/api/products/?page=2&price_min=100&price_max=1000&category=Electronics&search=phone&ordering=-price
В результате мы получаем товары на второй странице, отфильтрованные по цене, категории, поиску, и отсортированные по цене по убыванию.
Наша задача выполнена. Теперь вы знаете всё о пагинации и фильтрации данных в Django REST Framework. Используйте эти знания, чтобы создавать удобные и производительные API, которые покорят сердца пользователей! 🎉
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ