filters.py

过滤

管理器提供的根 QuerySet 描述了数据库表中的所有对象。不过,通常情况下,你只需要选择完整对象集的一个子集。

Django 文档

REST 框架的通用列表视图的默认行为是返回模型管理器的整个查询集。通常,你希望你的 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() 来处理诸如 http://example.com/api/purchases?username=denvercoder9 之类的 URL,并且仅在 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 框架还支持通用过滤后端,使你能够轻松构建复杂的搜索和过滤器。

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

Filter Example

设置过滤器后端

可以使用 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() 和通用过滤,并且所有内容都将按预期工作。例如,如果 Product 与名为 purchaseUser 具有多对多关系,你可能想要编写这样的视图

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' 添加到 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 Filter

仅当视图设置了 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 双下划线符号表示法对 ForeignKey 或 ManyToManyField 执行相关查找

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

对于 JSONFieldHStoreField 字段,您可以使用相同双下划线符号表示法根据数据结构中的嵌套值进行筛选

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 Filter

默认情况下,查询参数名为 '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 字符串。

第三方软件包

以下第三方包提供了其他过滤器实现。

Django REST 框架过滤器软件包

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

Django REST 框架全词搜索过滤器

djangorestframework-word-filter 作为 filters.SearchFilter 的替代品开发,它将在文本中搜索整个单词或完全匹配。

Django URL 过滤器

django-url-filter 提供了一种通过对用户友好的 URL 过滤数据的方法。它的工作方式与 DRF 序列化器和字段非常相似,因为它们可以嵌套,但它们被称为过滤器集和过滤器。这提供了过滤相关数据的简单方法。此外,此库是通用用途的,因此它可用于过滤其他数据源,而不仅仅是 Django QuerySet

drf-url-filters

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