JavaRush /Курсы /Модуль 3: Django /Практическое занятие по пагинации и фильтрации

Практическое занятие по пагинации и фильтрации

Модуль 3: Django
19 уровень , 9 лекция
Открыта

За последние несколько занятий мы погрузились в мир пагинации и фильтрации данных в Django REST Framework (DRF). Мы узнали, что пагинация спасает приложения от перегрузки огромными объемами данных, разрезая их на части. Мы рассмотрели два популярных метода пагинации — PageNumberPagination и LimitOffsetPagination — а также научились создавать кастомные классы пагинации. Затем плавно перешли к фильтрации данных: изучили возможности DjangoFilterBackend, реализацию поиска с помощью SearchFilter и сортировки через OrderingFilter. Мы даже заглянули в премудрости кастомных фильтров и сложной фильтрации с использованием Q-объектов. Сегодня мы закрепим всё это на практике.

Постановка задачи

Давайте представим, что у нас есть API для онлайн-магазина, в котором пользователи могут искать товары, фильтровать их по категории, цене и бренду, а также получать данные с постраничной разбивкой. Нам нужно:

  1. Реализовать пагинацию для списка товаров.
  2. Добавить фильтрацию по категориям, цене (с использованием диапазонов) и брендам.
  3. Настроить поиск по названию товара.
  4. Включить возможность сортировки по цене и дате добавления.

Итак, вооружаемся терпением, 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, которые покорят сердца пользователей! 🎉

1
Задача
Модуль 3: Django, 19 уровень, 9 лекция
Недоступна
Фильтрация заказов по клиенту и статусу
Фильтрация заказов по клиенту и статусу
1
Задача
Модуль 3: Django, 19 уровень, 9 лекция
Недоступна
Расширенная фильтрация и поиск событий
Расширенная фильтрация и поиск событий
3
Опрос
Кастомизация фильтрации и поиск, 19 уровень, 9 лекция
Недоступен
Кастомизация фильтрации и поиск
Кастомизация фильтрации и поиск
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ