За останні кілька занять ми занурилися у світ пагінації та фільтрації даних у 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, які підкорять серця користувачів! 🎉
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ