Уявіть, що у вас є додаток для управління бібліотекою (скільки книг ви почнете читати одночасно?). Користувач хоче побачити книги конкретного автора, відсортовані у зростаючому порядку за датою публікації. Якщо ваш API не вміє фільтрувати дані та сортувати результати, користувачам доведеться витягувати ВСІ дані і обробляти їх на клієнтській стороні. Незручно? Та ще й ресурсоємно.
Фільтрація та сортування дозволяють:
- Зменшити навантаження на мережу і прискорити завантаження сторінок.
- Спростити обробку даних на клієнті.
- Зробити API більш зручним та інтуїтивно зрозумілим для розробників.
Замість того, щоб надсилати тисячу рядків JSON і змушувати клієнта розбиратися, які з них йому потрібні, ви повернете лише ту інформацію, яка відповідає умовам запиту. Ми економимо ресурси сервера і нерви користувачів.
Основи фільтрації у GraphQL
Фільтрація у GraphQL досягається за допомогою додавання аргументів до запиту. Коли ви запитуєте ресурси, ви можете передати параметри, які описують умови відбору. Наприклад, ми можемо запитувати всіх користувачів старше 18 років або тільки книги конкретного жанру.
Приклад фільтрації: книги за жанром
Почнемо зі створення моделі Book у вашому додатку Django. Переконайтеся, що вона вже існує у вашому проєкті. Якщо її немає, створіть наступну модель:
# models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.CharField(max_length=255)
genre = models.CharField(max_length=100)
publication_date = models.DateField()
def __str__(self):
return self.title
Тепер напишемо GraphQL схему для фільтрації книг за жанром. Ми додамо аргумент genre до нашого запиту.
# schema.py
from graphene import ObjectType, List, String, Field
from graphene_django.types import DjangoObjectType
from .models import Book
class BookType(DjangoObjectType):
class Meta:
model = Book
class Query(ObjectType):
books = List(BookType, genre=String())
def resolve_books(self, info, genre=None):
if genre:
return Book.objects.filter(genre=genre)
return Book.objects.all()
Тепер запит у GraphQL може виглядати так:
query {
books(genre: "Science Fiction") {
title
author
}
}
Результат:
{
"data": {
"books": [
{
"title": "Dune",
"author": "Frank Herbert"
},
{
"title": "Foundation",
"author": "Isaac Asimov"
}
]
}
}
Складні фільтри: комбінуємо умови
Що, якщо потрібно фільтрувати книги не тільки за жанром, але й за автором? Доповнимо наш резолвер, щоб він підтримував фільтрацію за декількома параметрами.
class Query(ObjectType):
books = List(BookType, genre=String(), author=String())
def resolve_books(self, info, genre=None, author=None):
queryset = Book.objects.all()
if genre:
queryset = queryset.filter(genre=genre)
if author:
queryset = queryset.filter(author=author)
return queryset
Приклад запиту:
query {
books(genre: "Science Fiction", author: "Isaac Asimov") {
title
publicationDate
}
}
Основи сортування даних у GraphQL
Сортування керує порядком, у якому повертаються результати. Ви можете використовувати його для впорядкування даних за датою, ім'ям або будь-яким іншим полем. У GraphQL це зазвичай досягається за допомогою аргументу order_by (або подібного, залежно від ваших уподобань).
Для реалізації сортування за одним полем додамо аргумент order_by у наш запит. Оновимо схему так:
class Query(ObjectType):
books = List(BookType, genre=String(), order_by=String())
def resolve_books(self, info, genre=None, order_by=None):
queryset = Book.objects.all()
if genre:
queryset = queryset.filter(genre=genre)
if order_by:
queryset = queryset.order_by(order_by)
return queryset
Тепер можна запитати книги, відсортувавши їх за назвою:
query {
books(orderBy: "title") {
title
author
}
}
Або у зворотному порядку:
query {
books(orderBy: "-publication_date") {
title
publicationDate
}
}
Зверніть увагу, що знак - перед назвою поля означає зворотний порядок сортування.
Сортування за кількома полями
У деяких випадках може знадобитися сортування одразу за кількома полями. Наприклад, спочатку відсортувати за датою публікації, а потім — за назвою.
Ми додамо підтримку списку аргументів для order_by:
class Query(ObjectType):
books = List(BookType, genre=String(), order_by=List(String))
def resolve_books(self, info, genre=None, order_by=None):
queryset = Book.objects.all()
if genre:
queryset = queryset.filter(genre=genre)
if order_by:
queryset = queryset.order_by(*order_by)
return queryset
Приклад запиту:
query {
books(orderBy: ["publication_date", "title"]) {
title
publicationDate
}
}
Комбінуємо фільтрацію та сортування
Тепер давайте об'єднаємо фільтрацію та сортування в одному запиті. Це дозволить користувачу, наприклад, отримати всі книги певного жанру, відсортовані за датою публікації.
query {
books(genre: "Fantasy", orderBy: "publication_date") {
title
author
publicationDate
}
}
Цей запит поверне всі книги жанру "Fantasy" у порядку їх публікації.
Інтеграція з DjangoFilterBackend для складної фільтрації
Хоча ми вже навчилися додавати фільтрацію вручну, у реальних проєктах простіше підключити DjangoFilterBackend. Це дасть нам можливість швидко створювати складні фільтри, такі як діапазони дат, пошук за підрядком тощо.
Встановіть бібліотеку django-filter:
pip install django-filter
Налаштуємо фільтр у схемі:
from django_filters import FilterSet, CharFilter, DateFilter
from graphene_django.filter import DjangoFilterConnectionField
class BookFilter(FilterSet):
title_contains = CharFilter(field_name="title", lookup_expr="icontains")
publication_date_after = DateFilter(field_name="publication_date", lookup_expr="gte")
class Meta:
model = Book
fields = ["genre", "author"]
class Query(ObjectType):
books = DjangoFilterConnectionField(BookType, filterset_class=BookFilter)
Тепер на клієнті можна використовувати фільтри:
query {
books(genre: "Fantasy", titleContains: "Harry", publicationDateAfter: "1990-01-01") {
title
author
publicationDate
}
}
Ваші користувачі тепер зможуть без проблем фільтрувати та сортувати дані через API! Фільтрація зробить інтерфейс гнучким, а сортування дозволить знаходити важливу інформацію швидше.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ