1.2.16 过滤
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 配置包含这样的条目:
url('^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
filter_class = ProductFilter
def get_queryset(self):
user = self.request.user
return user.purchase_set.all()
DjangoFilterBackend
django-filter
库包含一个 DjangoFilterBackend
类,它支持 REST framework 对字段过滤进行高度定制。
要使用 DjangoFilterBackend
,首先安装 django-filter
。然后将 django_filters
添加到 Django 的 INSTALLED_APPS
中
pip install django-filter
你现在应该将过滤器后端添加到设置中:
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,)
filter_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
。
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')
默认情况下,搜索将使用不区分大小写的部分匹配。搜索参数可能包含多个搜索词,它们应该是空格和(或)逗号分隔的。如果使用多个搜索条件,则只有在所有提供的条件匹配的情况下,对象才会返回到列表中。
搜索行为可以通过将各种字符预先添加到 search_fields
来限制。
- '^' 匹配起始部分。
- '=' 完全匹配。
- '@' 全文搜索。(目前只支持 Django 的 MySQL 后端。)
- '$' 正则匹配。
例如:
search_fields = ('=username', '=email')
默认情况下,搜索参数被命名为 'search'
,但这可能会被 SEARCH_PARAM
setting 覆盖。
有关更多详细信息,请参阅 Django 文档。
OrderingFilter
OrderingFilter
类支持简单查询参数控制结果的排序。
默认情况下,查询参数被命名为 'ordering'
,但这可能会被 ORDERING_PARAM
setting 覆盖。
例如,要通过 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
的完整示例可能如下所示。
permissions.py:
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'],
}
views.py:
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)
我们可以通过覆盖视图上的 get_queryset()
来实现相同的行为,但使用过滤器后端允许你更轻松地将此限制添加到多个视图,或者将其应用于整个 API。
自定义接口
通用过滤器也可以在可浏览的 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
实例列表。