zoukankan      html  css  js  c++  java
  • day84

    filter过滤类源码分析

    我们从视图类中群查接口入口,去看看它内部是怎么实现过滤的

    首先我们进入ListAPIView类中

    实现群查功能的是它的第一个父类mixins中调用的,我们进入它的第一个父类

    我们看到它的群查接口有一个filter_queryset方法,此时一定要清楚属性的查找顺序,

    此时的self是指的视图类,如果视图类中没有这个方法,那就去它的父类中去找,那我这里就直接告诉你它是在GenericAPIView通用视图类中

    '''
    源码
    '''
    
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">filter_queryset</span><span class="hljs-params">(self, queryset)</span>:</span>
        <span class="hljs-string">"""
        Given a queryset, filter it with whichever filter backend is in use.
    
        You are unlikely to want to override this method, although you may need
        to call it either from a list view, or from a custom `get_object`
        method if you want to apply the configured filtering backend to the
        default queryset.
        """</span>
        <span class="hljs-keyword">for</span> backend <span class="hljs-keyword">in</span> list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        <span class="hljs-keyword">return</span> queryset</code></pre>
    

    根据以上代码剖析,我们知道了循环了一个配置属性,我们往上找找看有么有这个属性

    在GenericAPIView通用视图类中我们发现了,filter_backend是在api_settings中配置的,所以我们在去到api_settings中看看他配置的是啥?

    基于以上的验证,django自己配置的filter是空的列表,也就是没有默认不去过滤,需要我们自己去配置,而且在rest_framework内部一定实现了方法来启动我们配置的filter_backend

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

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

      img

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

    排序组件 OrderingFilter

    源码分析

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

    我们看到在OrderingFilter类 和 SearchFilter类中都继承了BaseFilterBackend,我们看下BaseFilterBackend类

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

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

    使用说明

    • 在使用的视图类中导入排序类 OrderingFilter
    • 配置 filter_backends
    • 配置参与排序的字段 ordering_fields
    • (了解)可选配置:ordering_param规定接口中使用过滤的关键字
    from . import models
    from rest_framework.generics import ListAPIView
    # 在使用的视图类中导入排序类 `OrderingFilter
    from rest_framework.filters import OrderingFilter
    

    class FreeCourseListAPIView(ListAPIView):
    queryset = models.Course.objects.filter(is_delete=False, is_show=True, ).order_by('-orders').all()
    serializer_class = serializers.FreeCourseModelSerializer

    <span class="hljs-comment"># 配置 排序的过滤类,相当于是局部配置了,优先使用</span>
    filter_backends = [OrderingFilter]
    
    <span class="hljs-comment"># 配置参与排序的字段 ?ordering=排序的关键字</span>
    <span class="hljs-comment"># ?ordering=price,按价格升序</span>
    <span class="hljs-comment"># ?ordering=-price,按价格降序</span>
    ordering_fiedls = [<span class="hljs-string">'price'</span>, <span class="hljs-string">'id'</span>]</code></pre>
    

    搜索组件 SearchFilter

    搜索组件的源码过程和排序的一模一样都是在同一个py文件中,可以直接定位过来

    • 在使用的视图类中导入排序类 SearchFilter
    • 配置 filter_backends
    • 配置参与排序的字段 search_fields
    • (了解)可选配置:search_param规定接口中使用过滤的关键字
    from . import models
    from rest_framework.generics import ListAPIView
    # 在使用的视图类中导入排序类 `OrderingFilter
    from rest_framework.filters import SearchFilter
    

    class FreeCourseListAPIView(ListAPIView):
    queryset = models.Course.objects.filter(is_delete=False, is_show=True, ).order_by('-orders').all()
    serializer_class = serializers.FreeCourseModelSerializer

    <span class="hljs-comment"># 配置 搜索的过滤类,相当于是局部配置了,优先使用</span>
    filter_backends = [SearchFilter]
    
    <span class="hljs-comment"># 配置参与搜索的字段     ?search=python</span>
    search_fields = [<span class="hljs-string">'name'</span>]</code></pre>
    

    自定义limit限制条件过滤器

    我们基于以上的排序、搜索过滤器明白了它们是怎么实现的,如果我们想要自定义过滤类的话可以实现

    • 继承了 BaseFilterBackend 类,自定义的过滤器可以不用继承父类,但是必须要实现 filter_queryset 方法
    • 重写 filter_queryset 方法
    • 返回 queryset 对象

    比如说我们现在就写一个限制返回多少条数据的过滤器

    # 在当前的app下,新建一个.py文件
    # 导入 BaseFilterBackend 基础类
    from rest_framework.filters import BaseFilterBackend
    # 自定义的过滤器,继承 BaseFilterBackend 类
    class LimitFilter(BaseFilterBackend):
        # 重写 `filter_queryset` 方法
        def filter_queryset(self, request, queryset, view):
            # 从请求接口中拿到过滤的关键字 limit
            limit = request.query_params.get('limit')
            try:
                # queryset就是这句话:
                # queryset = models.User.objects.filter(is_delete=False, is_show=True).order_by('-orders').all()
                # 对结果切片 达到限制查询结果的功能
                return queryset[:int(limit)]
            except:
                # 返回 `queryset` 对象
                return queryset
    
    # 视图类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

    <span class="hljs-comment"># 将自己些定义的过滤类配置上就ok了</span>
    filter_backends = [LimitFilter]</code></pre>
    

    筛选插件 djanog_filter

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

    pip install djanog_filter

    源码分析

    from django_filters.rest_framework import DjangoFilterBackend

    进入DjangoFilterBackend,找到filter_queryset()方法

    进入get_filterset()方法

    进入get_filterset_class()方法

    分类筛选 DjangoFilterBackend

    用法:

    # 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

    <span class="hljs-comment"># 分类筛选中要配置DjangoFilterBackend</span>
    filter_backends = [DjangoFilterBackend]
    
    <span class="hljs-comment"># 配置参与分类筛选的字段,所有字段都可以,一般用于分组的字段更有意义</span>
    <span class="hljs-comment"># course_category 是外键</span>
    <span class="hljs-comment"># ?course_category=1 得到外键course_category=1的数据</span>
    
    <span class="hljs-comment"># 以下四种配置的关键字:都可以,因为在源码中做了兼容</span>
    filter_fields = [<span class="hljs-string">'course_category'</span>]
    filterset_fields = [<span class="hljs-string">'course_category'</span>]
    filter_class = [<span class="hljs-string">'course_category'</span>]
    filterset_class = [<span class="hljs-string">'course_category'</span>]
    </code></pre>
    

    区间筛选(自定义区间筛选类)

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

    • 新定义一个类,继承 FilterSet
    • 定义配置类 Meta
    • 视图类中配置使用 filter_class
    # 创建一个filter.py文件
    # 基于django-filter插件
    from django_filters.rest_framework.filterset import FilterSet
    from . import models
    from django_filters import filters
    

    class CourseFilterSet(FilterSet):
    # 自定义限制字段,fileld_name:数据库中的字段,lookup_expr:设置条件
    # 最大价格,lte小于等于
    max_price = filters.NumberFilter(field_name='price', lookup_expr='lte')
    # 最小价格,gte大于等于
    min_price = filters.NumberFilter(field_name='price', lookup_expr='gte')

    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Meta</span>:</span>
        model = models.Course
        fields = [<span class="hljs-string">'course_category'</span>, <span class="hljs-string">'max_price'</span>, <span class="hljs-string">'min_price'</span>]
    

    from .filters import CourseFilterSet
    from . import models
    from rest_framework.generics import ListAPIView
    
    class FreeCourseListAPIView(ListAPIView):
        queryset = models.Course.objects.filter(is_delete=False, is_show=True, ).order_by('-orders').all()
        serializer_class = serializers.FreeCourseModelSerializer
    
        # 配置过滤类,优点是可以自定义区间过滤条件
        # ?max_price=100&min_price=10
        filter_class = CourseFilterSet

    分页

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

    由此可见分页过滤中有三种分页

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

    普通分页 PageNumberPagination

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

    # 基础分页
    class CoursePageNumberPagination(PageNumberPagination):
    # 配置条数,默认一页显示的条数
    page_size = 2
    # 查询页码的关键字,接口携带的参数 ?page=1
    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 = CoursePageNumberPagination

    偏移分页 LimitOffsetPagination

    from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination
    

    # 偏移分页:可以规定从第几条开始
    class CourseLimitOffsetPagination(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 = CourseLimitOffsetPagination

    游标分页 CursorPagination

    from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination
    

    class CourseCursorPagination(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 = CourseCursorPagination
  • 相关阅读:
    Java实现 LeetCode 27 移除元素
    Java实现 LeetCode 26 删除排序数组中的重复项
    Java实现 LeetCode 26 删除排序数组中的重复项
    Java实现 LeetCode 26 删除排序数组中的重复项
    Java实现 LeetCode 25 K个一组翻转链表
    Java实现 LeetCode 25 K个一组翻转链表
    Java实现 LeetCode 25 K个一组翻转链表
    Java实现 LeetCode 24 两两交换链表中的节点
    Java实现 LeetCode 24 两两交换链表中的节点
    Java实现 LeetCode 24 两两交换链表中的节点
  • 原文地址:https://www.cnblogs.com/gfhh/p/12190200.html
Copyright © 2011-2022 走看看