欢迎来到Cecilia陈的博客

孤独,是人一生最好的修行。

25 过滤组件

一、filter过滤类源码分析

现在我们就先以一个简单的群查的接口为例:

现在我们就上述群查接口先来走一下filter源码,我们既然要用filter第三方的组件,我们就得搞清楚,他要在哪里配置呀,不然也不会用呀

所以我们该从哪里作为入口,去找这个filter的源码呢?

我们先想一下,我们既然继承了这个ListAPIView这个类,能直接帮我们实现了群查的功能,也就是说我们只需要配置一下model和序列化,他就可以帮我们实现群查的数据,并且得到结果返回,那么我们在之前介绍视图家族的时候,分析过drf中的视图家族了。详细请看:

10 drf 视图基类(APIView、GenericAPIView)

11 drf 视图家族(Mixins、Genric)

现在我们就从ListAPIView上手,去看看它的内部做是否有filter的线索吧

  1. 首先我们进入ListAPIView类中

  2. 但是实现群查的是它的第一个父类mixins中调用的filter_queryset()方法,只是在这之前,要先找在哪里配置的

  3. 根据以上图中的分析,我们来到GenericAPIView通用视图类中

  4. 在GenericAPIView通用视图类中我们发现了,filter_backend是在api_settings中配置的,所以我们在去到api_settings中看看他配置的是啥?---哈哈:这里剧透,配置的内容是空列表,所以也就很明显的得出结果,这个一定是要我们自己认为的手动配置的,至于如何配,我们先来验证一下,它到底是如何配置的吧

  5. 基于以上的验证,果然django自己配置的filter是空的列表。

  6. 这也就说明了,filter_backend一定是需要我们自己去配置的,而且再rest_framework内部一定实现了方法来启动我们配置的filter_backend

  7. 所以我们现在来看一下再rest_framework中到底是哪里实现了filter配置启动的方法

  8. 我们就直接从api_settings为入口找到rest_framework的配置文件包

  9. 好了到这里,我们就直接引出下面我们要说的两个部分的内容,排序、 搜索

二、排序

根据以上的推理我们已经找到了filter配置的入口了,那紧接着我们就来看一下在上面的OrderingFilter类到底做了哪些事情

  1. 我们可以看到这个OrderingFilter类是继承了BaseFilterBackend这个类的,我们直接定位到上面的filter.py文件

  2. 看到父类中说明,在它的子类中,必须重写这个filter_queryset方法才可以用,那行吧,我们在来看看他的子类OreringFilter

  3. 将上述方法的结果返回给get_ordering,get_ordering将结果返回给filter_queryset,然后执行queryset.order(字段名)。queryset就是我们自已在view视图类中定义好的模型类。

下面就是重点来了,如何使用这个排序过滤,返回正确的结果:

from . import models
from rest_framework.generics import ListAPIView

# 我们知道OrderingFiler是在哪一个类中的了,所以这个直接导入
from rest_framework.filters import OrderingFilter
class FreeCourseListAPIView(ListAPIView):
    queryset = models.User.objects.filter(is_delete=False, is_show=True).order_by('-orders').all()
    serializer_class = serializer.RegisterModelSerializer
    
    # 还记得在GenericAPIView中的filter_backends吧,我们这是可以在我们自己的视图类中配置了
    # 相当于是局部配置了,优先使用
    # 在url中我们系需要携带排序的参数即可
    # http://127.0.0.1:8000/user/?odering=price,id,students这里的意思是当price相同的时候id排序
    filter_backends = [OrderingFilter]
    # 这是我们自己要规定排序的字段
    ordering_fields = ['price', 'id', 'students']

三、搜索

搜索的源码过程,和排序的一摸一样,参照排序的源码,即可,直接定位到filter.py文件中的SearchFilter类即可,

from . import models
from rest_framework.generics import ListAPIView

# 我们知道OrderingFiler是在哪一个类中的了,所以这个直接导入
from rest_framework.filters import OrderingFilter,SearchFilter
class FreeCourseListAPIView(ListAPIView):
    queryset = models.User.objects.filter(is_delete=False, is_show=True).order_by('-orders').all()
    serializer_class = serializer.RegisterModelSerializer

    # SearchFilter搜索
    filter_backends = [OrderingFilter,SearchFilter]
    # 参与排序的字段: ordering=-price,id
    ordering_fields = ['price', 'id', 'students']
    # 参与搜索的字段: search=python  (name字段中带python就ok)
    search_fields = ['name', 'brief']

四、自定义limt限制过滤器

我们基于以上的排序、搜索过滤器来明白了,他们两个类都继承了BaseFilterBackend类,并且父类中明确规定一定要重写filter_queryset()方法,所以我们在用这两个过滤组件的时候,只是在视图类局部配置了过滤类,参与排序/搜索的字段。这都是类中自己封装好的

如果我们想要自定义过滤类的话,需要去继承BaseFilterBackend类,重写它的filter_queryset()方法即可

比如我们现在就写一个限制返回多少条结果给前台页面的过滤器

# 在当前的app下,新建一个.py文件
# 将BaseFilterBackend导入
from rest_framework.filters import BaseFilterBackend

# 自定义一个LimtFilter类,继承BaseFilterBackend
class LimitFilter(BaseFilterBackend):
    # 重写filter_queryset方法
    def filter_queryset(self, request, queryset, view):
        # queryset就是这句话
        # queryset = models.User.objects.filter(is_delete=False, is_show=True).order_by('-orders').all()
        
        limit = request.query_params.get('limit') # 在url携带的参数去limit
        try:
            return queryset[:int(limit)] # 然后返回
        except:
            return queryset # 没有limit时,全部结果返回
# 视图类view中配置
from . import models
from rest_framework.generics import ListAPIView

# 导入我们自己定义的过滤类
from .myfilter import LimitFilter 
class FreeCourseListAPIView(ListAPIView):
    queryset = models.User.objects.filter(is_delete=False, is_show=True).order_by('-orders').all()
    serializer_class = serializer.RegisterModelSerializer
	
    # 将自己些定义的过滤类配置上就ok了
    filter_backends = [LimitFilter]

注意:自定义的过滤器可以不用继承父类,但是必须要实现filter_queryset()方法

五、分类筛选

django和drf都实现不了分类,只能依赖第三方插件

# 首先我们安装第三方组件
pip install djanog_filter

原生用法:

# view.py视图类中
from . import models
from rest_framework.generics import ListAPIView

# 分类筛选
from django_filters.rest_framework import DjangoFilterBackend
class FreeCourseListAPIView(ListAPIView):
    queryset = models.User.objects.filter(is_delete=False, is_show=True).order_by('-orders').all()
    serializer_class = serializer.RegisterModelSerializer
    
    # 分类筛选中要配置DjangoFilterBackend
    filter_backends = [DjangoFilterBackend]

    # 参与分类筛选的字段,
    # course_category 一定是外键
    # ?course_category=1 得到course_category=1的数据
    # 参与分类筛选的字段,所有字段都可以,但是用于分组的字段更有意义
    filter_fields = ['course_category']
    filterset_fields = ['course_category']
    filter_class = ['course_category']
    filterset_class = ['course_category']

六、区间筛选(分类筛选的高级使用)

基于django-filter插件,完成指定区间筛选(一般都是对于数字字段)

# 创建一个filter.py文件
from django_filters.rest_framework.filterset import FilterSet
from django_filters import filters
from . import models
class CourseFilterSet(FilterSet):
    max_price = filters.NumberFilter(field_name='price', lookup_expr='lte')
    min_price = filters.NumberFilter(field_name='price', lookup_expr='gte')
    class Meta:
        model = models.Course
        fields = ['course_category', 'max_price', 'min_price']

源码分析:

from django_filters.rest_framework import DjangoFilterBackend
  1. 进入DjangoFilterBackend,找到filter_queryset()方法

  2. 进入get_filterset()方法

  3. 进入get_filterset_class()方法

七、分页

同样以上面的群查数据的接口为例:我们先来分析一下分页过滤如何去操作,群查的listAPIView是继承了Mixins.ListModelMixin和GenericAPIView的,我们在将排序和搜索的时候,我们在父类GenericAPIView中没有看到和分页有关的参数配置

那么我们就转移目标去Mixins.ListModelMixin类中去找

  1. 直接进入到Mixins.ListModelMixin类中,我们会发现list方法内部在群查完得到结果以后,直接开始page配置了

    1575465362473

  2. 进入到paginate_queryset()方法,我们来看一下,它的源码

    1575465673225

    以上只是找到了分页类的位置在哪里,下面我们真正的来到paginate_queryset()方法

1. 分页过滤中有三种分页

PageNumberPagination :普通分页
LimitOffsetPagination : 显示条数分页,偏移
CursorPagination :游标分页,和普通分页的区别在于,将url中前一页,后一页的直接加密

2. 分析普通分页

测试:

# 在view类视图中我们去配置一下
from rest_framework.generics import ListAPIView
from . import models, serializers

# 普通分页
from rest_framework.pagination import PageNumberPagination
class FreeCourseListAPIView(ListAPIView):
    queryset = models.Course.objects.filter(is_delete=False, is_show=True).order_by('-orders').all()
    serializer_class = serializers.FreeCourseModelSerializer
     

    # 分页器
    pagination_class = PageNumberPagination # 这里我们会发现,返回不了结果

原因如下:

解决如下:

# 通过自定义分页的方法,来配置
# 在当前的apps下创建一个paginations.py的文件

from rest_framework.pagination import PageNumberPagination

# 我们直接来继承子类就好了,只是需要配置它的一些参数即可
class MyPageNumberPagination(PageNumberPagination):
    # 默认一页条数
    page_size = 2
    # 选择哪一页的key
    page_query_param = 'page'
    # 用户自定义一页条数
    page_size_query_param = 'page_size'
    # 用户自定义一页最大控制条数
    max_page_size = 10
# view.py视图类中
from rest_framework.generics import ListAPIView
from . import models, serializers

# 自定义的普通分页
from .paginations import MyPageNumberPagination
class FreeCourseListAPIView(ListAPIView):
    queryset = models.Course.objects.filter(is_delete=False, is_show=True).order_by('-orders').all()
    serializer_class = serializers.FreeCourseModelSerializer
                                                     
    # 分页器
    pagination_class = MyPageNumberPagination

3. 分析limit限制分页

用法:

# 在创建的paginations.py的文件中,自定义一个限制条数分页
class MyLimitOffsetPagination(LimitOffsetPagination):
    # 默认一页条数
    default_limit = 2
    # 从offset开始往后显示limit条
    limit_query_param = 'limit'
    offset_query_param = 'offset'
    max_limit = 2
# view.py视图类中
from rest_framework.generics import ListAPIView
from . import models, serializers

# 自定义的普通分页
from .paginations import MyLimitOffsetPagination
class FreeCourseListAPIView(ListAPIView):
    queryset = models.Course.objects.filter(is_delete=False, is_show=True).order_by('-orders').all()
    serializer_class = serializers.FreeCourseModelSerializer
                                                     
    # 分页器
    pagination_class = MyLimitOffsetPagination

4. 分析course游标分页

总结:只是将url中做了一层加密,其他的没有不同,只需要知道用法即可,源码流程就参照以上分析,自己走即可

用法:

# 在创建的paginations.py的文件中,自定义一个游标分页
class MyCursorPagination(CursorPagination):
    cursor_query_param = 'cursor'
    page_size = 2
    page_size_query_param = 'page_size'
    max_page_size = 2
    # ordering = 'id'  # 默认排序规则,不能和排序过滤器OrderingFilter共存
# view.py视图类中
from rest_framework.generics import ListAPIView
from . import models, serializers

# 自定义的普通分页
from .paginations import MyCursorPagination
class MyCursorPagination(ListAPIView):
    queryset = models.Course.objects.filter(is_delete=False, is_show=True).order_by('-orders').all()
    serializer_class = serializers.FreeCourseModelSerializer
                                                     
    # 分页器
    pagination_class = MyLimitOffsetPagination
posted @ 2019-12-04 23:44  Cecilia陈  阅读(345)  评论(0编辑  收藏  举报