Фильтрация¶
Корневой QuerySet, предоставляемый менеджером, описывает все объекты в таблице базы данных. Обычно, однако, вам нужно выбрать только подмножество полного набора объектов.
‒ Django documentation
По умолчанию общие представления списков в REST framework возвращают весь набор запросов для менеджера модели. Часто вы хотите, чтобы ваш API ограничивал элементы, возвращаемые набором запросов.
Самый простой способ фильтровать набор запросов любого представления, которое является подклассом GenericAPIView
, - это переопределить метод .get_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-адресу¶
Другой стиль фильтрации может включать ограничение набора запросов на основе некоторой части URL.
Например, если ваша конфигурация URL содержит запись, подобную этой:
re_path('^purchases/(?P<username>.+)/$', PurchaseList.as_view()),
Затем вы можете написать представление, которое возвращает набор запросов на покупку, отфильтрованный по имени пользователя в части 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)
Фильтрация по параметрам запроса¶
Последним примером фильтрации начального набора запросов может быть определение начального набора запросов на основе параметров запроса в url.
Мы можем переопределить .get_queryset()
для работы с такими URL, как http://example.com/api/purchases?username=denvercoder9
, и фильтровать набор запросов, только если в URL включен параметр username
:
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
Общая фильтрация¶
Помимо возможности переопределения набора запросов по умолчанию, REST framework также включает поддержку общих бэкендов фильтрации, которые позволяют легко создавать сложные поиски и фильтры.
Общие фильтры также могут быть представлены в виде элементов управления 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]
Фильтрация и поиск объектов¶
Обратите внимание, что если бэкэнд фильтра настроен для представления, то помимо того, что он используется для фильтрации представлений списка, он также будет использоваться для фильтрации наборов запросов, используемых для возврата одного объекта.
Например, учитывая предыдущий пример и продукт с идентификатором 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:doc:` <https://django-filter.readthedocs.io/en/latest/index.html>` включает класс 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
s в django-filter documentation. Также рекомендуется прочитать раздел DRF integration.
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
.
„^“ Начинается с поиска.
„=“ Точные совпадения.
„@“ Полнотекстовый поиск. (В настоящее время поддерживается только Django’s PostgreSQL backend).
„$“ Регекс поиск.
Например:
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(CustomSearchFilter, self).get_search_fields(view, request)
Более подробную информацию см. в разделе Django documentation.
ЗаказФильтр¶
Класс 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
.
Если вы уверены, что кверисет, используемый представлением, не содержит конфиденциальных данных, вы также можете явно указать, что представление должно разрешить упорядочивание по любому полю модели или агрегату кверисета, используя специальное значение '__all__'
.
class BookingsListView(generics.ListAPIView):
queryset = Booking.objects.all()
serializer_class = BookingSerializer
filter_backends = [filters.OrderingFilter]
ordering_fields = '__all__'
Указание порядка по умолчанию¶
Если в представлении установлен атрибут ordering
, он будет использоваться в качестве упорядочивания по умолчанию.
Обычно вы контролируете это, устанавливая order_by
в начальном наборе запросов, но использование параметра 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)
. Метод должен вернуть новый, отфильтрованный набор запросов.
Помимо того, что клиенты могут выполнять поиск и фильтрацию, общие бэкенды фильтров могут быть полезны для ограничения того, какие объекты должны быть видны для каждого конкретного запроса или пользователя.
Пример¶
Например, вам может понадобиться ограничить доступ пользователей только к созданным ими объектам.
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.
Пагинация и схемы¶
Вы также можете сделать элементы управления фильтром доступными для автогенерации схемы, которую обеспечивает REST framework, реализовав метод get_schema_fields()
. Этот метод должен иметь следующую сигнатуру:
get_schema_fields(self, view)
Метод должен возвращать список экземпляров coreapi.Field
.
Пакеты сторонних производителей¶
Следующие пакеты сторонних производителей предоставляют дополнительные реализации фильтров.
Пакет фильтров фреймворка Django REST¶
Класс django-rest-framework-filters package работает вместе с классом DjangoFilterBackend
и позволяет легко создавать фильтры по отношениям или создавать несколько типов поиска фильтра для определенного поля.
Django REST framework полный фильтр поиска слов¶
djangorestframework-word-filter разработан как альтернатива filters.SearchFilter
, который будет искать полное слово в тексте, или точное совпадение.
Django URL Filter¶
django-url-filter предоставляет безопасный способ фильтрации данных через удобные для человека URL. Он работает очень похоже на сериализаторы и поля DRF в том смысле, что они могут быть вложенными, за исключением того, что они называются filtersets и filters. Это обеспечивает простой способ фильтрации связанных данных. Также эта библиотека является универсальной, поэтому ее можно использовать для фильтрации других источников данных, а не только Django QuerySet
s.
drf-url-filters¶
drf-url-filter - это простое Django приложение для применения фильтров к drf ModelViewSet
„s Queryset
чистым, простым и настраиваемым способом. Оно также поддерживает валидацию входящих параметров запроса и их значений. Для проверки входящих параметров запроса используется красивый пакет python Voluptuous
. Самое лучшее в voluptuous то, что вы можете определить свои собственные валидации в соответствии с требованиями к параметрам запроса.