Фильтрация
Корневой QuerySet, предоставляемый менеджером, описывает все объекты в таблице базы данных. Однако, обычно, вам потребуется выбрать только подмножество всех объектов.
По умолчанию, общие представления списка REST фреймворка возвращают весь queryset для менеджера модели. Часто вы захотите ограничить элементы, возвращаемые queryset, вашим API.
Самый простой способ отфильтровать queryset любого представления, являющегося подклассом GenericAPIView , — переопределить метод .get_queryset().
Переопределение этого метода позволяет настраивать queryset, возвращаемый представлением, различными способами.
Фильтрация по текущему пользователю
Возможно, вам нужно отфильтровать queryset, чтобы гарантировать, что возвращаются только результаты, относящиеся к текущему аутентифицированному пользователю, который делает запрос.
Вы можете сделать это, отфильтровав по значению request.user.
Например:
from myapp.models import Purchase
from myapp.serializers import PurchaseSerializer
from rest_framework import generics
class PurchaseList(generics.ListAPIView):
serializer_class = PurchaseSerializer
def get_queryset(self):
"""
This view should return a list of all the purchases
for the currently authenticated user.
"""
user = self.request.user
return Purchase.objects.filter(purchaser=user)
Фильтрация по URL
Другой стиль фильтрации может включать ограничение queryset на основе какой-либо части URL.
Например, если ваша конфигурация URL содержала запись такого типа:
re_path('^purchases/(?P<username>.+)/$', PurchaseList.as_view()),
Тогда вы могли бы написать представление, которое возвращало queryset покупок, отфильтрованный по части имени пользователя в URL:
class PurchaseList(generics.ListAPIView):
serializer_class = PurchaseSerializer
def get_queryset(self):
"""
This view should return a list of all the purchases for
the user as determined by the username portion of the URL.
"""
username = self.kwargs['username']
return Purchase.objects.filter(purchaser__username=username)
Фильтрация по параметрам запроса
Окончательный пример фильтрации начального queryset — определение начального queryset на основе параметров запроса в URL.
Мы можем переопределить .get_queryset() для работы с URL, такими как http://example.com/api/purchases?username=denvercoder9, и отфильтровать queryset только в том случае, если параметр username указан в URL:
class PurchaseList(generics.ListAPIView):
serializer_class = PurchaseSerializer
def get_queryset(self):
"""
Optionally restricts the returned purchases to a given user,
by filtering against a `username` query parameter in the URL.
"""
queryset = Purchase.objects.all()
username = self.request.query_params.get('username')
if username is not None:
queryset = queryset.filter(purchaser__username=username)
return queryset
Универсальная фильтрация
Помимо возможности переопределять стандартный queryset, REST фреймворк также включает поддержку универсальных бэкэндов фильтрации, что позволяет легко создавать сложные поиски и фильтры.
Универсальные фильтры также могут представлять собой HTML-элементы управления в обозреваемом API и административном API.
Установка бэкэндов фильтров
По умолчанию бэкэнды фильтров могут быть установлены глобально, используя параметр DEFAULT_FILTER_BACKENDS . Например.
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}
Вы также можете установить бэкэнды фильтров на основе представления или набора представлений, используя GenericAPIView основанные на классах представления.
import django_filters.rest_framework
from django.contrib.auth.models import User
from myapp.serializers import UserSerializer
from rest_framework import generics
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = [django_filters.rest_framework.DjangoFilterBackend]
Фильтрация и поиск объектов
Обратите внимание, что если бэкэнд фильтра настроен для представления, то помимо использования для фильтрации списков, он также будет использоваться для фильтрации queryset, используемых для возвращения отдельного объекта.
Например, учитывая предыдущий пример и продукт с id 4675, следующий URL вернет соответствующий объект или вернет ответ 404, в зависимости от того, соответствуют ли условия фильтрации данному экземпляру продукта:
http://example.com/api/products/4675/?category=clothing&max_price=10.00
Переопределение начального queryset
Обратите внимание, что вы можете использовать как переопределённый .get_queryset() , так и универсальную фильтрацию вместе, и всё будет работать как ожидается. Например, если Product имел многие-ко-многим отношения с User, с именем purchase, вы могли бы написать представление такого типа:
class PurchasedProductsList(generics.ListAPIView):
"""
Return a list of all the products that the authenticated
user has ever purchased, with optional filtering.
"""
model = Product
serializer_class = ProductSerializer
filterset_class = ProductFilter
def get_queryset(self):
user = self.request.user
return user.purchase_set.all()
Руководство по API
DjangoFilterBackend
Библиотека django-filter включает класс DjangoFilterBackend , который поддерживает высоконастраиваемую фильтрацию полей для REST фреймворка.
Для использования DjangoFilterBackend, сначала установите django-filter.
pip install django-filter
Затем добавьте 'django_filters' в INSTALLED_APPS Django:
INSTALLED_APPS = [
...
'django_filters',
...
]
Теперь вы должны либо добавить бэкэнд фильтра в свои настройки:
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}
Либо добавить бэкэнд фильтра в отдельное представление или набор представлений.
from django_filters.rest_framework import DjangoFilterBackend
class UserListView(generics.ListAPIView):
...
filter_backends = [DjangoFilterBackend]
Если вам нужен только простой фильтр на основе равенства, вы можете установить атрибут filterset_fields в представлении или наборе представлений, перечислив набор полей, по которым вы хотите фильтровать.
class ProductList(generics.ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['category', 'in_stock']
Это автоматически создаст класс FilterSet для указанных полей и позволит вам отправлять запросы такого типа:
http://example.com/api/products?category=clothing&in_stock=True
Для более сложных требований к фильтрации вы можете указать класс FilterSet , который должен использоваться представлением. Вы можете узнать больше об FilterSet в документации django-filter. Также рекомендуется прочитать раздел интеграции DRF.
SearchFilter
Класс SearchFilter поддерживает простой поиск на основе одного параметра запроса и основан на функциональности поиска в Django admin's search functionality.
При использовании обозреваемый API будет включать элемент управления SearchFilter:
Класс SearchFilter будет применён только в том случае, если у представления есть атрибут search_fields . Атрибут search_fields должен быть списком имён полей текстового типа в модели, таких как CharField или TextField.
from rest_framework import filters
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = [filters.SearchFilter]
search_fields = ['username', 'email']
Это позволит клиенту фильтровать элементы в списке, выполняя запросы, такие как:
http://example.com/api/users?search=russell
Вы также можете выполнить связанный поиск по ForeignKey или ManyToManyField с использованием нотации с двойным подчеркиванием в API поиска:
search_fields = ['username', 'email', 'profile__profession']
Для полей JSONField и HStoreField вы можете фильтровать по вложенным значениям в структуре данных, используя ту же нотацию с двойным подчеркиванием:
search_fields = ['data__breed', 'data__owner__other_pets__0__name']
По умолчанию, поиск использует регистронезависимые частичные совпадения. Параметр поиска может содержать несколько поисковых терминов, разделенных пробелами и/или запятыми. Если используются несколько поисковых терминов, объекты будут возвращены в списке только в том случае, если все предоставленные термины совпадают. Поисковые запросы могут содержать цитируемые фразы с пробелами, каждая фраза рассматривается как один поисковой термин.
Поведение поиска может быть задано путём добавления префиксов к именам полей в search_fields одним из следующих символов (что эквивалентно добавлению __<lookup> к полю):
| Префикс | Поиск | |
|---|---|---|
^ | istartswith | Поиск по началу строки. |
= | iexact | Точные совпадения. |
$ | iregex | Поиск по регулярному выражению. |
@ | search | Полнотекстовый поиск (в настоящее время поддерживается только бэкэнд PostgreSQL Django's PostgreSQL backend). |
| Нет | icontains | Поиск по содержанию (по умолчанию). |
Например:
search_fields = ['=username', '=email']
По умолчанию параметр поиска называется 'search', но это может быть переопределено с помощью настройки SEARCH_PARAM.
Для динамического изменения полей поиска на основе содержимого запроса можно создать подкласс SearchFilter и переопределить функцию get_search_fields(). Например, следующий подкласс будет выполнять поиск только по title если параметр запроса title_only присутствует в запросе:
from rest_framework import filters
class CustomSearchFilter(filters.SearchFilter):
def get_search_fields(self, view, request):
if request.query_params.get('title_only'):
return ['title']
return super().get_search_fields(view, request)
Дополнительные сведения см. в Документации Django.
OrderingFilter
Класс OrderingFilter поддерживает простой порядок сортировки результатов, управляемый параметрами запроса.
По умолчанию параметр запроса называется 'ordering', но это может быть переопределено с помощью настройки ORDERING_PARAM.
Например, для сортировки пользователей по имени пользователя:
http://example.com/api/users?ordering=username
Клиент также может указать обратный порядок сортировки, добавив префикс '-' к имени поля, например:
http://example.com/api/users?ordering=-username
Также могут быть указаны несколько порядков сортировки:
http://example.com/api/users?ordering=account,username
Указание полей, по которым можно сортировать
Рекомендуется явно указать поля, которые API должен допускать для сортировки. Вы можете сделать это, установив атрибут ordering_fields в представлении, например:
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = [filters.OrderingFilter]
ordering_fields = ['username', 'email']
Это помогает предотвратить утечку данных, например, разрешение пользователям сортировать по полю пароля или другим конфиденциальным данным.
Если вы не устанавливаете атрибут ordering_fields в представлении, класс фильтра по умолчанию разрешит пользователю сортировать по любым читаемым полям в сериализаторе, указанном атрибутом serializer_class.
Если вы уверены, что queryset, используемый представлением, не содержит конфиденциальных данных, вы также можете явно указать, что представление должно разрешать сортировку по любому полю модели или агрегату queryset, используя специальное значение '__all__'.
class BookingsListView(generics.ListAPIView):
queryset = Booking.objects.all()
serializer_class = BookingSerializer
filter_backends = [filters.OrderingFilter]
ordering_fields = '__all__'
Указание стандартного порядка сортировки
Если атрибут ordering установлен в представлении, он будет использоваться в качестве стандартного порядка сортировки.
Обычно вы управляете этим, устанавливая order_by на начальном queryset, но использование параметра ordering в представлении позволяет указать порядок сортировки таким образом, что он может быть автоматически передан в качестве контекста в отображаемую шаблонную страницу. Это позволяет автоматически отображать заголовки столбцов по-разному, если они используются для сортировки результатов.
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = [filters.OrderingFilter]
ordering_fields = ['username', 'email']
ordering = ['username']
Атрибут ordering может быть строкой или списком/кортежем строк.
Настраиваемая универсальная фильтрация
Вы также можете предоставить свой собственный бэкэнд универсальной фильтрации или написать приложение для установки, которое другие разработчики могли бы использовать.
Для этого переопределите BaseFilterBackend, и переопределите метод .filter_queryset(self, request, queryset, view). Метод должен вернуть новый, отфильтрованный queryset.
Помимо того, что клиенты могут выполнять поиск и фильтрацию, универсальные бэкэнды фильтров могут быть полезны для ограничения объектов, которые должны быть видны данному запросу или пользователю.
Пример
Например, вам может потребоваться ограничить пользователей, чтобы они могли видеть только объекты, которые они создали.
class IsOwnerFilterBackend(filters.BaseFilterBackend):
"""
Filter that only allows users to see their own objects.
"""
def filter_queryset(self, request, queryset, view):
return queryset.filter(owner=request.user)
Мы могли бы добиться такого же поведения, переопределив get_queryset() в представлениях, но использование фильтрационного бэкенда позволяет проще добавить это ограничение к нескольким представлениям или применить его ко всему API.
Настройка интерфейса
Общие фильтры также могут представлять интерфейс в браузируемом API. Для этого необходимо реализовать метод to_html(), который возвращает отрендеренное HTML-представление фильтра. Этот метод должен иметь следующий сигнатуру:
to_html(self, request, queryset, view)
Метод должен вернуть отрендеренную HTML-строку.
Пакеты сторонних разработчиков
Следующие пакеты сторонних разработчиков предоставляют дополнительные реализации фильтров.
Пакет фильтров Django REST Framework
Пакет django-rest-framework-filters работает вместе с классом DjangoFilterBackend, и позволяет легко создавать фильтры по отношениям или создавать несколько типов поиска для заданного поля.
Фильтр полного поиска по словам Django REST Framework
Пакет djangorestframework-word-filter разработан как альтернатива filters.SearchFilter, которая будет искать целые слова в тексте или точное совпадение.
Фильтр Django URL
django-url-filter предоставляет безопасный способ фильтрации данных через понятные для пользователя URL-адреса. Он работает очень похоже на сериализаторы и поля DRF в том смысле, что они могут быть вложены, за исключением того, что они называются наборами фильтров и фильтрами. Это обеспечивает простой способ фильтрации связанных данных. Кроме того, эта библиотека является универсальной, поэтому ее можно использовать для фильтрации других источников данных, а не только Django QuerySet.
drf-url-filters
drf-url-filter — это простое приложение Django для применения фильтров к ModelViewSet drf Queryset чистым, простым и настраиваемым способом. Он также поддерживает валидацию входящих параметров запроса и их значений. Для валидации входящих параметров запроса используется прекрасная Python-библиотека Voluptuous. Преимущество voluptuous заключается в том, что вы можете определить собственные валидации в соответствии с требованиями ваших параметров запроса.
Copyright © 2011–present Encode OSS Ltd.
Licensed under the BSD License.
https://www.django-rest-framework.org/api-guide/filtering/