Rest-framework专栏讲解(十九):Filtering
Rest-framework专栏讲解(十九):Filtering
目录
过滤
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 的用户名部分过滤的 purchase 查询集:
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', None)
if username is not None:
queryset = queryset.filter(purchaser__username=username)
return queryset
通用过滤器
除了能够覆盖默认的查询集外,REST framework 还包括对通用过滤后端的支持,使你可以轻松构建复杂的搜索和过滤器。
通用过滤器也可以在可浏览的 API 和管理 API 中将自己渲染为 HTML 控件。
设置过滤器后端
可以使用 DEFAULT_FILTER_BACKENDS
setting 全局设置默认的过滤器后端
。例如。
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()
和通用过滤,并且所有内容都将按预期工作。例如,如果产品与用户具有多对多关系,则可能需要编写一个如下所示的视图:
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 framework 对字段过滤进行高度定制。
要使用 DjangoFilterBackend
,首先安装 django-filter
。然后将 django_filters
添加到 Django 的 INSTALLED_APPS
中
python -m 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]
如果你只需要简单的基于等式的过滤,则可以在视图或视图集上设置 filter_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 integration。
SearchFilter
SearchFilter
类支持简单的基于单个查询参数的搜索,并且基于 Django 管理员的搜索功能。
在使用时,可浏览的 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
你还可以使用查找 API 双下划线表示法对 ForeignKey 或 ManyToManyField 执行相关查找:
search_fields = ['username', 'email', 'profile__profession']
对于 JSONField
和 HStoreField
字段, 您可以使用相同的双下划线符号根据数据结构内的嵌套值进行过滤(就是高级):
search_fields = ['data__breed', 'data__owner__other_pets__0__name']
默认情况下,搜索将使用不区分大小写的部分匹配。搜索参数可能包含多个搜索词,它们应该是空格和(或)逗号分隔的。如果使用多个搜索条件,则只有在所有提供的条件匹配的情况下,对象才会返回到列表中。
搜索行为可以通过将各种字符预先添加到 search_fields
来限制。
^
:匹配起始部分。=
:完全匹配。@
:全文搜索。(目前只支持 Django 的 MySQL 后端。)$
:正则匹配。
例如:
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(CustomSearchFilter, self).get_search_fields(view, request)
有关更多详细信息,请参阅 Django 文档。
OrderingFilter
OrderingFilter
类支持简单查询参数控制结果的排序。
默认情况下,查询参数被命名为 ordering
,但这可能会被 ORDERING_PARAM
配置覆盖。
例如,要通过 username 对用户排序:
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
属性可以是一个字符串或者字符串列表(元组)。
DjangoObjectPermissionsFilter
DjangoObjectPermissionsFilter
旨在与 django-guardian 软件包一起使用,添加了自定义 'view'
的权限。过滤器将确保查询集仅返回用户具有适当查看权限的对象。
如果你使用的是 DjangoObjectPermissionsFilter
,那么你可能还需要添加适当的对象权限类,以确保用户只有在具有适当对象权限的情况下才能对实例进行操作。做到这一点的最简单方法是继承 DjangoObjectPermissions
并为 perms_map
属性添加 'view'
权限。
使用 DjangoObjectPermissionsFilter
和 DjangoObjectPermissions
的完整示例可能如下所示。
class CustomObjectPermissions(permissions.DjangoObjectPermissions):
"""
Similar to `DjangoObjectPermissions`, but adding 'view' permissions.
"""
perms_map = {
'GET': ['%(app_label)s.view_%(model_name)s'],
'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
'HEAD': ['%(app_label)s.view_%(model_name)s'],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
class EventViewSet(viewsets.ModelViewSet):
"""
Viewset that only lists events if user has 'view' permissions, and only
allows operations on individual events if user has appropriate 'view', 'add',
'change' or 'delete' permissions.
"""
queryset = Event.objects.all()
serializer_class = EventSerializer
filter_backends = (filters.DjangoObjectPermissionsFilter,)
permission_classes = (myapp.permissions.CustomObjectPermissions,)
自定义通用过滤器
你还可以提供自己的通用过滤器后端,或者编写一个可供其他开发人员使用的可安装应用程序。
为此,请继承 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)
自定义接口
通用过滤器也可以在可浏览的 API 中渲染接口。为此,你应该实现一个 to_html()
方法,该方法返回过滤器的渲染 HTML 表示。此方法应具有以下签名:
to_html(self, request, queryset, view)
该方法应该返回一个渲染的 HTML 字符串。
Pagination & schemas
通过实现 get_schema_fields()
方法,你还可以使过滤器控件可用于 REST framework 提供的模式自动生成。此方法应具有以下签名:
get_schema_fields(self, view)
该方法应该返回一个 coreapi.Field
实例列表。
第三方库
Django REST框架过滤器软件包
在 Django 框架过滤器封装与 DjangoFilterBackend
类一起工作, 并允许您轻松地在跨关系的创建过滤器, 或在指定字段创建多个过滤器查找类型。
Django REST框架全字搜索过滤器
djangorestframework-word-filter作为 filter.SearchFilter
替代品, 它将在文本中搜索完整的单词或完全匹配。
Django URL过滤器
django-url-filter 提供了一种通过友好的 url 过滤数据的安全方法, 它的工作原理与 DRF 序列化程序和字段非常相似, 在某种意义上它们可以嵌套, 但它们被称为 filtersets
和 filters
, 这提供了过滤相关数据的简单方法, 而且这个库也是通用的, 所以它可以用来过滤其他数据源, 而不仅仅是 Django QuerySet
。
drf-url-filters
DRF-URL-Filters 是一个简单的 Django 应用程序, 它以干净、简单和可配置的方式在 drfmodelviewset
的 Queryset
上应用过滤器, 它还支持对传入查询参数的验证, 一个漂亮的 python 包 Voluptuous
用于对传入的查询参数进行验证, 关于 Voluptuous
的最好的部分是您可以根据查询参数要求定义自己的验证。