day84

我是研究僧i 提交于 2020-01-14 09:37:17

filter过滤类源码分析

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

首先我们进入ListAPIView类中

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

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

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

'''
源码
'''

    def filter_queryset(self, queryset):
        """
        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.
        """
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

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

在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

    # 配置 排序的过滤类,相当于是局部配置了,优先使用
    filter_backends = [OrderingFilter]

    # 配置参与排序的字段 ?ordering=排序的关键字
    # ?ordering=price,按价格升序
    # ?ordering=-price,按价格降序
    ordering_fiedls = ['price', 'id']

搜索组件 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

    # 配置 搜索的过滤类,相当于是局部配置了,优先使用
    filter_backends = [SearchFilter]

    # 配置参与搜索的字段     ?search=python
    search_fields = ['name']

自定义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
    
    # 将自己些定义的过滤类配置上就ok了
    filter_backends = [LimitFilter]

筛选插件 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
    
    # 分类筛选中要配置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插件,完成指定区间筛选(一般都是对于数字字段)

  • 新定义一个类,继承 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')

    class Meta:
        model = models.Course
        fields = ['course_category', 'max_price', 'min_price']
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

filter过滤类源码分析

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

首先我们进入ListAPIView类中

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

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

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

'''
源码
'''

    def filter_queryset(self, queryset):
        """
        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.
        """
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

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

在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

    # 配置 排序的过滤类,相当于是局部配置了,优先使用
    filter_backends = [OrderingFilter]

    # 配置参与排序的字段 ?ordering=排序的关键字
    # ?ordering=price,按价格升序
    # ?ordering=-price,按价格降序
    ordering_fiedls = ['price', 'id']

搜索组件 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

    # 配置 搜索的过滤类,相当于是局部配置了,优先使用
    filter_backends = [SearchFilter]

    # 配置参与搜索的字段     ?search=python
    search_fields = ['name']

自定义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
    
    # 将自己些定义的过滤类配置上就ok了
    filter_backends = [LimitFilter]

筛选插件 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
    
    # 分类筛选中要配置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插件,完成指定区间筛选(一般都是对于数字字段)

  • 新定义一个类,继承 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')

    class Meta:
        model = models.Course
        fields = ['course_category', 'max_price', 'min_price']
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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!