DRF-Filter
管理器提供的根查询集描述了数据库表中的所有对象。不过, 通常情况下, 我们只需要选择完整对象集的子集。
DRF框架的通用列表视图的默认行为是返回模型管理器的整个查询集。通常, 我们希望我们返回的 API
返回结果是已经过滤后的查询集,而不是全量。
重写get_queryset()
筛选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 config
:
url('^purchases/(?P<username>.+)/$', PurchaseList.as_view()),
然后,让我们用路由中的username
来进行筛选:
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的查询参数里。比如,我们的URL config
:
url('^purchases/$', PurchaseList.as_view()),
然后,我们访问http://example.com/api/purchases?username=denvercoder9
即,将需要进行筛选的参数放在了URL后由?
开头、&
连接的查询参数集中。此时,我们通过查询参数集中的键值对来进行筛选:
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
注意:路由匹配中的参数存放在
.kwargs
中,?
携带的查询参数存放在.query_params
。二者不可混淆。注意: 凡是
?
携带参数,url末尾不要加/
filter_backends
除了能够重写默认查询集外, DRF还包括对通用filter backend
的支持, 使我们可以轻松地构造复杂的搜索和筛选器。
原生的backend
SearchFilter
SearchFilter
类支持基于简单的单个查询参数的搜索。
from rest_framework import filters
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = (filters.SearchFilter,)
search_fields = ('username', 'email')
默认的搜索的参数为search
,当然这也可以在SEARCH_PARAM
配置中重写。
访问
http://example.com/api/purchases?search=denvercoder9
会在所有查询集username
和email
的field字段对应域中搜索值为denvercoder9
的对象。
可以通过在search_fields
中加上各种前缀字符来限制搜索行为:
^
Starts-with search.=
Exact matches.@
Full-text search. (Currently only supported Django's MySQL backend.)$
Regex search.
For example:
search_fields = ('=username', '=email')
OrderingFilter
OrderingFilter
支持对返回的查询集结果的简单排序。
默认排序的查询参数为ordering
, 当然这也可以在ORDERING_PARAM
配置中重写。
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = (filters.OrderingFilter,)
ordering_fields = ('username', 'email')
ordering = ('email',) #若添加此行,则表示默认按照'email`进行排序
正序访问:
http://example.com/api/users?ordering=username
反序访问:
http://example.com/api/users?ordering=-username
DjangoObjectPermissionsFilter
DjangoObjectPermissionsFilter
旨在和django-guardian
一起使用, 并添加了自定义 view
权限。筛选器将确保查询仅返回用户具有对应view
权限的对象。
首先将视图方法与对应的权限映射起来。
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'],
}
当使用GET
method获取对象列表或对象权限时,必须要求当前用户有对model
的view
权限。比如当获取用户列表时,必须要求当前访问用户有auth.view_model
权限,view
不在Django
默认的权限里,需要自己添加。可参见:Django 内置权限管理
然后在我们的视图里添加该权限过滤器,view.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,)
过滤器添加完毕。
当我们调用接口获取Event
列表时,DRF首先会判断我们是否有对Event
的全局访问权限。若无,则直接返回You do not have permission to perform this action
。否则,按照是否拥有具体的Event
对象的访问权限,返回具体Event
的列表。
比如,用户:A、B、C,Event
:event1, event2, event3.
若A无Event的全局访问权限,则A访问Event列表的结果是You do not have permission to perform this action
;
若B有Event的全局访问权限,而只有event1,event2的访问权限,则B访问Event列表的结果是{event1, event2}
;
若C有Event的全局访问权限,且有event1,event2,event3的访问权限,则C访问Event列表的结果是{event1, event2, event3}
。
CustomObjectPermissions
和django-guardian
结合起来使用来完成权限管控,是不是很强大?
自定义backend
Example:
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()
能够达到同样的目的,但是自定义FilterBackend
能够作用于多个视图,而不用每个视图都去重写get_queryset()
函数。