接着上节《Django如何快速开发RESTful风格的接口?》,本节来讲解列表接口中如何快速实现列表接口的参数查询,文章有点长,含部分源码解析,可以收藏后再看哦~
参数查询
从上节的ListModelMixin
的源码可以知道,在self.get_queryset()
获取到数据后,是由self.filter_queryset()
方法对数据进行过滤的。
而具体的self.filter_queryset()
逻辑是在GenericAPIView
中实现的。
self.filter_backends
主要获取配置的filterBackend类,调用类的filter_queryset()
方法来实现参数查询的逻辑,self.filter_backends
默认是api_settings.DEFAULT_FILTER_BACKENDS
,这个就是在项目下的settings.py文件中进行配置的,因此filter_backends有两种配置方案,第一种是直接在settings.py文件指定DEFAULT_FILTER_BACKENDS
,这个会对所有的View都生效,第二种方案是在单独的View类中进行配置,仅对当前这个View生效。
下面介绍几种常用列表参数查询的方案。
SearchFilter
为了演示,这里都是采用第二种方案,在View类中进行指定。拿「接口测试平台」专栏中的项目列表来举例,具体代码如下:
class ProjectsViewSet(mixins.ListModelMixin,
GenericViewSet):
queryset = Projects.objects.filter()
serializer_class = ProjectsSerializer
filter_backends = (SearchFilter,)
search_fields = ("name",)
重启项目,打开swagger界面可以看到列表接口多了一个search
字段,使用这个search字段就可以在配置的search_fields
中的字段进行搜索了。
接下来通过源码来看看它是如何实现的。上面已经知道,是通过配置的filterBackend类的filter_queryset方法实现的。所以可以直接看SearchFilter
的filter_queryset方法。
SearchFilter还提供了4种匹配方案,没有前缀的,比如上面示例代码就是icontains
包含。
- ‘^name’ 名称以「查询字段」开头
- ‘=name’ 名称完全等于「查询字段」
- ‘@name’ 全文搜索
- ‘$name’ 正则表达式搜索
DjangoFilterBackend
这是插件Django-filter提供的功能,使用之前需要先安装Django-filter
依赖。它提供了两种查询方案,filterset_fields
和filterset_class
。
接下来通过源码来看看它是如何实现的。同样是查看DjangoFilterBackend的filter_queryset
方法。
可以看到首先要获取到filterset,这个是调用了self.get_filterset
方法,然后获取到qs属性,这个在后面进行详细介绍。
然后通过self.get_filterset_class
获取过滤类然后从请求中获取请求参数,调用具体的过滤类,将请求参数作为传参。根据配置的方式不同,self.get_filterset_class
有不同的逻辑,下面分开进行介绍。
filterset_fields
class ProjectsViewSet(mixins.ListModelMixin,
GenericViewSet):
queryset = Projects.objects.filter()
serializer_class = ProjectsSerializer
filter_backends = (DjangoFilterBackend,)
filterset_fields = ("name",)
重启项目,打开swagger界面可以看到列表接口多了一个name
字段,然后就可以使用name进行搜索了。
从源码可以看出,当filterset_fields
不为空时,是使用AutoFilterSet类来进行过滤的。但是它没办法完成模糊搜索等高级功能,因此需要用到下面的方式来自定以Filter类。
filterset_class
class ProjectsFilter(FilterSet):
# 指定需要模糊查询的字段
name = CharFilter(field_name='name', lookup_expr='icontains') # icontains,包含且忽略大小写
class Meta:
# 指定模型
models = Projects
fields = ['name']
class ProjectsViewSet(mixins.ListModelMixin,
GenericViewSet):
queryset = Projects.objects.filter()
serializer_class = ProjectsSerializer
filter_backends = (DjangoFilterBackend,)
filterset_class = ProjectsFilter
从源码可以看出,当filterset_class
不为空时,返回是是第定义的filterset_class,即自定义的ProjectFilter
类。
然后就是上诉两个方案相同的逻辑,即self.get_filterset
方法最后返回的filter_class(**kwargs)
。不同的是一个使用的是默认的AutoFilterSet
,一个是自定义的ProjectsFilter
。但是相同的逻辑是它们都继承于FilterSetMetaclass
,从类名可以看出来,这是一个元类,即生成类的类,它的主要功能就是根据字段名设置过滤字段,例如默认的AutoFilterSet
将会根据字段设置base_filter,并且匹配规则为’exact’,即完全匹配,这也就是为什么filterset_fields
无法完成模糊搜索等高级功能的原因。
而自定义的Filter类,则会根据配置生成对应字段的匹配规则,如下:
然后就回到self.filter_queryset
返回的filter_query.qs
这里。因为新生成的具有过滤规则的类都是继承BaseFilterSet
类,所以这里其实是获取的BaseFilterSet
类的qs
属性,主要的逻辑就是根据这是的filters定义的字段匹配规则进行QuerySet的逻辑处理。
最终的效果就是QuerySet的query字段中查询语句后面拼接了参数过滤,如下图
篇幅有限,本章到这里就结束啦,是不是还有很多疑问,下章继续给大家分析QuerySet是如果将结果进行返回的。