filters.py

管理器提供的根查询集描述了数据库表中的所有对象。通常,你可能只需要选择完整对象集的一个子集。

— Django 文档

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):
        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):
        username = self.kwargs['username']
        return Purchase.objects.filter(purchaser__username=username)

基于查询参数过滤

最后一个过滤初始查询集的例子是根据 URL 中的查询参数确定初始查询集。

我们可以通过覆盖 .get_queryset() 来处理类似 http://example.com/api/purchases?username=denvercoder9 的 URL,并仅在 URL 中包含 username 参数时过滤查询集:

class PurchaseList(generics.ListAPIView):
    serializer_class = PurchaseSerializer

    def get_queryset(self):
        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 还支持通用过滤后台,这些后台允许你轻松构建复杂的搜索和过滤器。

通用过滤器还可以在可浏览的 API 和管理 API 中呈现为 HTML 控件。

图片

设置过滤后台

可以通过 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]

过滤和对象查找

请注意,如果为视图配置了过滤器后台,那么除了用于列表视图外,它还将用于返回单个对象的查询集。

例如,给定上一个例子,以及一个 id 为 4675 的产品,以下 URL 将根据给定产品实例是否满足过滤条件返回相应的对象或 404 响应:

http://example.com/api/products/4675/?category=clothing&max_price=10.00

覆盖初始查询集

请注意,可以同时使用覆盖的 .get_queryset() 和通用过滤器,一切都会如预期般正常工作。例如,如果 ProductUser 具有多对多关系,名称为 purchase,你可能希望编写如下视图:

class PurchasedProductsList(generics.ListAPIView):
    model = Product
    serializer_class = ProductSerializer
    filterset_class = ProductFilter

    def get_queryset(self):
        user = self.request.user
        return user.purchase_set.all()

DjangoFilterBackend

django-filter 库包含一个 DjangoFilterBackend 类,该类为 REST framework 支持高度可定制的字段过滤。

要使用 DjangoFilterBackend,首先安装 django-filter

pip install django-filter

然后将 'django_filters' 添加到 Django 的 INSTALLED_APPS

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 类,供视图使用。你可以在 django-filter 文档中阅读更多关于 FilterSet 的内容。也建议你阅读 DRF 集成部分。

SearchFilter

SearchFilter 类支持基于单个查询参数的简单搜索,基于 Django 管理界面的搜索功能。

当启用时,可浏览的 API 将包含一个 SearchFilter 控件:

图片

只有当视图设置了 search_fields 属性时,才会应用 SearchFilter 类。search_fields 属性应该是模型上的文本类型字段名称列表,如 CharFieldTextField

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

你还可以使用外键或多对多字段的查找 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全文搜索(目前仅支持 Django 的 PostgreSQL 后端)。
icontains包含搜索(默认)。

例如:

search_fields = ['=username', '=email']

默认情况下,搜索参数名为 'search',但可以通过 SEARCH_PARAM 设置进行覆盖。

为了根据请求内容动态更改搜索字段,可以继承 SearchFilter 并覆盖 get_search_fields() 函数。例如,以下子类仅在请求中有 title_only 查询参数时在 title 上进行搜索:

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 属性指定的序列化器中的任何可读字段进行排序。

如果你确信视图使用的查询集中不包含任何敏感数据,也可以明确指定视图应允许对 任何 模型字段或查询集聚合进行排序,通过使用特殊值 '__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):
    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 filters 包

django - rest - framework - filters 包与 DjangoFilterBackend 类一起工作,允许你轻松地跨关系创建过滤器,或为给定字段创建多种过滤查找类型。

Django REST framework 全词搜索过滤器

djangorestframework - word - filter 是一个替代 filters.SearchFilter 的方案,它将在文本中搜索全词或精确匹配。

Django URL Filter

django - url - filter 提供了一种通过用户友好的 URL 安全地过滤数据的方式。它与 DRF 序列化器和字段非常相似,除了它们被称为过滤器集和过滤器。这为过滤相关数据提供了简便的方式。此外,该库是通用目的的,因此可以用于过滤其他数据源,而不仅仅是 Django QuerySet

drf - url - filters

drf - url - filter 是一个简单的 Django 应用,以干净、简单和可配置的方式对 drf ModelViewSetQueryset 应用过滤。它还支持对传入查询参数及其值进行验证。一个美丽的 Python 包 Voluptuous 被用于对传入查询参数进行验证。Voluptuous 的最佳部分是你可以根据查询参数要求定义自己的验证规则。

图片图片图片


观复
16 声望1 粉丝

君子慎独,不欺暗室,卑以自牧,含章可贞