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

会在所有查询集usernameemail的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'],
    }

当使用GETmethod获取对象列表或对象权限时,必须要求当前用户有对modelview权限。比如当获取用户列表时,必须要求当前访问用户有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}

CustomObjectPermissionsdjango-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()函数。

Copyright © itrunner.cn 2020 all right reserved,powered by Gitbook该文章修订时间: 2022-08-28 07:44:16

results matching ""

    No results matching ""