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, які підкорять серця користувачів! 🎉

3
Опитування
Кастомізація фільтрації та пошук, рівень 19, лекція 9
Недоступний
Кастомізація фільтрації та пошук
Кастомізація фільтрації та пошук
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ