一、使用drf过滤器

drf过滤器: 在GenericAPIView这个视图基类的 def filter_queryset(self, queryset) 方法中 ,通过 self.filter_backends 的配置来拿到过滤器并使用。但drf 默认在settings文件中的配置是空,既不使用过滤器。

drf默认为我们提供了两个过滤器类

class OrderingFilter(BaseFilterBackend) 用于 排序

使用时: 在url添加配置中参与排序的字段

class SearchFilter(BaseFilterBackend) 用于 搜索

使用时: 在url添加配置中参与搜索的字段

from rest_framework.generics import ListAPIView
# OrderingFilter排序、SearchFilter搜索
from rest_framework.filters import OrderingFilter, 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 = [OrderingFilter, SearchFilter]
    # 参与排序的字段: ordering=-price,id,students
    ordering_fields = ['price', 'id', 'students']
    # 参与搜索的字段: search=python  (name字段中带python就ok)
    search_fields = ['name', 'brief']
# 携带排序的过滤参数的url: http://127.0.0.1:8000/course/free?ordering=-price 降序
# 携带搜索的过滤参数的url: http://127.0.0.1:8000/course/free?search=python (name字段中带python就ok)

二、自定义过滤器

  • 自定义过滤器类。继承BaseFilterBackend(实际上看过源码你就知道继不继承都可以,因为BaseFilterBackend类并没有做什么实质性的事情)
  • 实现def filter_queryset(self, request, queryset, view) 方法,必须返回一个queryset对象
  • 自定义过滤器类

    from rest_framework.filters import BaseFilterBackend
    # 自定义过滤器类
    class LimitFilter(BaseFilterBackend):
        # 必须实现filter_queryset方法,返回queryset对象
        def filter_queryset(self, request, queryset, view):
            # 取出过滤条件
            limit = request.query_params.get("limit")	# 过滤参数
                return queryset[:int(limit)]
            except:
                return queryset
    

    使用自定义过滤器类

    from . import filters
    class FreeCourseListAPIView(ListAPIView):
        queryset = models.Course.objects.filter(is_delete=False, is_show=True).order_by(
            "-orders").all()
        serializer_class = serializers.FreeCourseModelSerializer
        # 局部配置过滤器类
        filter_backends = [filters.LimitFilter]
    # 携带自定义过滤字段参数的url: http://127.0.0.1:8000/course/free?limit=1
    

    三、使用django-filter模块

    django-filter模块可以做一些更高级的过滤,也可以自定义过滤,比如分类筛选、筛选区间等

    安装依赖: pip install django-filter

    django-filter模块为django和drf都做了相应的支持。如果是drf做前后端分离的项目就使用from django_filters.rest_framework import DjangoFilterBackend

    局部配置过滤器类:filter_backends = [DjangoFilterBackend]

    参与分类筛选的字段:filter_fields = ['course_category'](看源码得知,其实源码中反射了2个字段名,分别是:filterset_fields、filter_fields,反射了两个类名,分别是:filterset_class、filter_class、

    # 分类筛选:django-filter:filter_backends配置DjangoFilterBackend,再在filter_fields中配置分组筛选的字段
    from django_filters.rest_framework import DjangoFilterBackend
    class FreeCourseListAPIView(ListAPIView):
        queryset = models.Course.objects.filter(is_delete=False, is_show=True).order_by(
            "-orders").all()
        serializer_class = serializers.FreeCourseModelSerializer
        # 局部配置过滤器类
        filter_backends = [DjangoFilterBackend]
        # 参与分类筛选的字段:
        filter_fields = ['course_category']
    # 携带分类筛选字段的参数的url: http://127.0.0.1:8000/course/free?course_category=2
    

    3.1 基于django-filter模块自定义过滤器类实现价格区间

  • 自定义过滤器类。继承from django_filters.rest_framework.filterset import FilterSet
  • 自定义分类筛选的字段。在Meta类中通过fields来指定筛选的字段。和定义序列化类方法一致
  • 自定义过滤器类

    # 基于django-filter插件,完成指定区间筛选(一般都是对应数字字段)
    from django_filters.rest_framework.filterset import FilterSet
    from django_filters import filters
    from . import models
    class CourseFilterSet(FilterSet):
        # field_name:要过滤的数据库字段,lookup_expr:过滤条件
        min_price = filters.NumberFilter(field_name='price', lookup_expr='gte') # 条件:大于price
        max_price = filters.NumberFilter(field_name='price', lookup_expr='lte') # 条件:小于price
        class Meta:
            model = models.Course
            # 参与分类筛选的字段:
            fields = ['course_category', 'max_price', 'min_price']
    

    使用自定义过滤器类

    # 分类筛选:django-filter:filter_backends配置DjangoFilterBackend,再在filter_fields中配置分组筛选的字段
    from django_filters.rest_framework import DjangoFilterBackend
    # 自定义过滤器类
    from . import filters
    class FreeCourseListAPIView(ListAPIView):
        queryset = models.Course.objects.filter(is_delete=False, is_show=True).order_by(
            "-orders").all()
        serializer_class = serializers.FreeCourseModelSerializer
        # 局部配置过滤器类
        filter_backends = [DjangoFilterBackend]
        # 使用基于django-filter自定义过滤器类
        filter_class = filters.CourseFilterSet
    # 携带分类筛选字段的参数的url: http://127.0.0.1:8000/course/free?min_price=20&max_price=80
    

    自定义过滤器字段对照表

    models.AutoField:                   {'filter_class': NumberFilter},
    models.CharField:                   {'filter_class': CharFilter},
    models.TextField:                   {'filter_class': CharFilter},
    models.BooleanField:                {'filter_class': BooleanFilter},
    models.DateField:                   {'filter_class': DateFilter},
    models.DateTimeField:               {'filter_class': DateTimeFilter},
    models.TimeField:                   {'filter_class': TimeFilter},
    models.DurationField:               {'filter_class': DurationFilter},
    models.DecimalField:                {'filter_class': NumberFilter},
    models.SmallIntegerField:           {'filter_class': NumberFilter},
    models.IntegerField:                {'filter_class': NumberFilter},
    models.PositiveIntegerField:        {'filter_class': NumberFilter},
    models.PositiveSmallIntegerField:   {'filter_class': NumberFilter},
    models.FloatField:                  {'filter_class': NumberFilter},
    models.NullBooleanField:            {'filter_class': BooleanFilter},
    models.SlugField:                   {'filter_class': CharFilter},
    models.EmailField:                  {'filter_class': CharFilter},
    models.FilePathField:               {'filter_class': CharFilter},
    models.URLField:                    {'filter_class': CharFilter},
    models.GenericIPAddressField:       {'filter_class': CharFilter},
    models.CommaSeparatedIntegerField:  {'filter_class': CharFilter},
    models.UUIDField:                   {'filter_class': UUIDFilter},