相信你已经搭建好了django+rest_framework,并且成功启动了你的项目。接下来如果想要使用django_filters或者django_rest_framework_filters过滤器,那么你还需如下配置:

# settings.py
INSTALLED_APPS = [...'rest_framework','django_filters','rest_framework_filters'
]REST_FRAMEWORK = {'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend','rest_framework_filters.backends.RestFrameworkFilterBackend','rest_framework.filters.SearchFilter','rest_framework.filters.OrderingFilter',...),
}rest_framework_filters是django_filters的高级扩展,使用了rest_framework_filters就无需在引入django_filters。

以上是全局配置方法, 当然也可以在每个viewset中配置独有的过滤器后端。

以上配置完成,就可以愉快的和前端小伙伴联调测试了。

如果你还不止步于此,想一探究竟django filter是怎么做到如此简单方便又好用的设计呢?或者你遇到了更为复杂需求,比如正则过滤、多值查询、复杂跨表查询等,这就需要对源码有一定了解了。下面进入正题:

一、filter backend是何时、何地、如何被DRF调用的

以下是drf部分源代码:

class GenericAPIView(views.APIView):queryset = Noneserializer_class = None...lookup_field = 'pk'lookup_url_kwarg = None# The filter backend classes to use for queryset filteringfilter_backends = api_settings.DEFAULT_FILTER_BACKENDSdef 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 needto call it either from a list view, or from a custom `get_object`method if you want to apply the configured filtering backend to thedefault queryset."""for backend in list(self.filter_backends):queryset = backend().filter_queryset(self.request, queryset, self)return queryset...class ListModelMixin:"""List a queryset."""def list(self, request, *args, **kwargs):queryset = self.filter_queryset(self.get_queryset())...

当列表页请求过来时,ListModelMixin.list函数调用GenericViewSet.filter_queryset对查询集过滤,filter_queryset这个函数的作用是循环遍历过滤器后端并初始化,并且链式调用每个过滤器的filter_queryset方法,对查询集进行过滤。由此也可以看出django配置文件中配置的DEFAULT_FILTER_BACKENDS参数取的是各个过滤器的交集结果,且的关系。

下面继续看DjangoFilterBackend.filter_queryset方法:

class DjangoFilterBackend(metaclass=RenameAttributes):filterset_base = filterset.FilterSetraise_exception = True    ...def filter_queryset(self, request, queryset, view):filterset = self.get_filterset(request, queryset, view)if filterset is None:return querysetif not filterset.is_valid() and self.raise_exception:raise utils.translate_validation(filterset.errors)return filterset.qs...def get_filterset_kwargs(self, request, queryset, view):return {'data': request.query_params,'queryset': queryset,'request': request,}def get_filterset(self, request, queryset, view):filterset_class = self.get_filterset_class(view, queryset)if filterset_class is None:return Nonekwargs = self.get_filterset_kwargs(request, queryset, view)return filterset_class(**kwargs)...def get_filterset_class(self, view, queryset=None):"""Return the `FilterSet` class used to filter the queryset."""filterset_class = getattr(view, 'filterset_class', None)filterset_fields = getattr(view, 'filterset_fields', None)...

DjangoFilterBackend.filter_queryset 作用是实例化filterset,并且返回filterset的qs属性。到此也就完成了过滤任务。

filterset是什么呢?其实就是你在viewset中配置的filterset_class自定义过滤器属性。

filterset.qs是什么呢?往下看。

继续看这个filterset_class:

你在viewset中配置的filterset_class应该是这个样子的:

import django_filters
class ProductFilter(django_filters.FilterSet):name = django_filters.CharFilter(lookup_expr='icontains')class Meta:model = Productfields = ['price', 'release_date']

也就是说上面讲到的DjangoFilterBackend.filter_queryset是实例化了你这个自定义的过滤器,最终返回了你这个过滤器的qs属性。

再看django_filters.FilterSet这个父类:

class FilterSet(BaseFilterSet, metaclass=FilterSetMetaclass):passclass BaseFilterSet:def __init__(self, data=None, queryset=None, *, request=None, prefix=None):if queryset is None:queryset = self._meta.model._default_manager.all()model = queryset.modelself.is_bound = data is not Noneself.data = data or {}self.queryset = querysetself.request = requestself.form_prefix = prefixself.filters = copy.deepcopy(self.base_filters)...@propertydef form(self):if not hasattr(self, '_form'):Form = self.get_form_class()if self.is_bound:self._form = Form(self.data, prefix=self.form_prefix)else:self._form = Form(prefix=self.form_prefix)return self._formdef filter_queryset(self, queryset):for name, value in self.form.cleaned_data.items():queryset = self.filters[name].filter(queryset, value)assert isinstance(queryset, models.QuerySet), \"Expected '%s.%s' to return a QuerySet, but got a %s instead." \% (type(self).__name__, name, type(queryset).__name__)return queryset@propertydef qs(self):if not hasattr(self, '_qs'):qs = self.queryset.all()if self.is_bound:# ensure form validation before filteringself.errorsqs = self.filter_queryset(qs)self._qs = qsreturn self._qs...

终于找到qs属性了,这也就是真正对字段进行过滤的地方了。qs通过调用BaseFilterSet.filter_queryset方法对字段进行过滤。

对哪些字段过滤则是在BaseFilterSet.form做了处理。form中关注一个self.data属性,这个属性是在FilterSet实例化时被赋值,也就是在上面提到的DjangoFilterBackend.filter_queryset 中实例化FilterSet时被赋值,即self.data=request.query_params。

BaseFilterSet.filter_queryset中还有一个关键的属性self.filters。这个属性定义了如何对request.query_params参数过滤处理。从上面代码可以看出该参数拷贝自copy.deepcopy(self.base_filters),而base_filters则由BaseFilterSet.get_filters()调用得到:

class FilterSetMetaclass(type):def __new__(cls, name, bases, attrs):attrs['declared_filters'] = cls.get_declared_filters(bases, attrs)new_class = super().__new__(cls, name, bases, attrs)new_class._meta = FilterSetOptions(getattr(new_class, 'Meta', None))new_class.base_filters = new_class.get_filters()
class BaseFilterSet:@classmethoddef get_filters(cls):"""Get all filters for the filterset. This is the combination of declared andgenerated filters."""# No model specified - skip filter generationif not cls._meta.model:return cls.declared_filters.copy()# Determine the filters that should be included on the filterset.filters = OrderedDict()fields = cls.get_fields()undefined = []for field_name, lookups in fields.items():field = get_model_field(cls._meta.model, field_name)# warn if the field doesn't exist.if field is None:undefined.append(field_name)for lookup_expr in lookups:filter_name = cls.get_filter_name(field_name, lookup_expr)# If the filter is explicitly declared on the class, skip generationif filter_name in cls.declared_filters:filters[filter_name] = cls.declared_filters[filter_name]continueif field is not None:filters[filter_name] = cls.filter_for_field(field, field_name, lookup_expr)# Allow Meta.fields to contain declared filters *only* when a list/tupleif isinstance(cls._meta.fields, (list, tuple)):undefined = [f for f in undefined if f not in cls.declared_filters]if undefined:raise TypeError("'Meta.fields' must not contain non-model field names: %s"% ', '.join(undefined))# Add in declared filters. This is necessary since we don't enforce adding# declared filters to the 'Meta.fields' optionfilters.update(cls.declared_filters)return filters

get_filters中关注一下三行:

...
filters = OrderedDict()
​​​​​​​# get_fields获取你自定的过滤器中Meta.fields属性,上面举例中的['price', 'release_date']
fields = cls.get_fields()
...
# 经过一系列处理,会根据字段类型自动将['price', 'release_date']生成对应的filter,
# 形如:
#filters = {'price': django_filters.filters.NumberFilter}# declared_filters 则是自定义过滤器中声明的filter,
# 形如 declared_filters = {'name':django_filters.CharFilter(lookup_expr='icontains')
}
filters.update(cls.declared_filters)

分析到此,要查询的字段self.form有了,使用的过滤器self.filter也有了,那么queryset = self.filters[name].filter(queryset, value),这句代码也就不难理解了。

但还有一点就是对filter这个函数的理解,看下源码就不难理解了:

class Filter:...def filter(self, qs, value):if value in EMPTY_VALUES:return qsif self.distinct:qs = qs.distinct()lookup = '%s__%s' % (self.field_name, self.lookup_expr)qs = self.get_method(qs)(**{lookup: value})return qsclass CharFilter(Filter):field_class = forms.CharField

相信你看到这里,也就了解了django filter的整个来龙去脉。相信你也能自定义如下一个支持正则表达式的过滤器:

import django_filters class RegexFilter(filters.CharFilter):@classmethoddef valid(cls, value):try:re.compile(value)return Trueexcept re.error as e:raise Error('正则表达式语法错误')def filter(self, qs, value):self.valid(value)if value in EMPTY_VALUES:return qsif self.distinct:qs = qs.distinct()lookup = '%s__%s' % (self.field_name, self.lookup_expr)qs = self.get_method(qs)(**{lookup: value})return qsclass ProductFilter(django_filters.FilterSet):name = django_filters.CharFilter(lookup_expr='icontains') name__regex = RegexFilter(field_name='name', lookup_expr='regex', label='名称') class Meta: model = Product fields = ['price', 'release_date']# /product/products/?name=123 查询包含123的产品
# /product/products/?name__regex=^123.*  查询以123开头的产品

django_rest_framework_filters源码大同小异,就不详细探讨了。

完活,收工。

django_filters、django_rest_framework_filters源码解析相关推荐

  1. 谷歌BERT预训练源码解析(二):模型构建

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_39470744/arti ...

  2. 谷歌BERT预训练源码解析(三):训练过程

    目录 前言 源码解析 主函数 自定义模型 遮蔽词预测 下一句预测 规范化数据集 前言 本部分介绍BERT训练过程,BERT模型训练过程是在自己的TPU上进行的,这部分我没做过研究所以不做深入探讨.BE ...

  3. 谷歌BERT预训练源码解析(一):训练数据生成

    目录 预训练源码结构简介 输入输出 源码解析 参数 主函数 创建训练实例 下一句预测&实例生成 随机遮蔽 输出 结果一览 预训练源码结构简介 关于BERT,简单来说,它是一个基于Transfo ...

  4. Gin源码解析和例子——中间件(middleware)

    在<Gin源码解析和例子--路由>一文中,我们已经初识中间件.本文将继续探讨这个技术.(转载请指明出于breaksoftware的csdn博客) Gin的中间件,本质是一个匿名回调函数.这 ...

  5. Colly源码解析——结合例子分析底层实现

    通过<Colly源码解析--框架>分析,我们可以知道Colly执行的主要流程.本文将结合http://go-colly.org上的例子分析一些高级设置的底层实现.(转载请指明出于break ...

  6. libev源码解析——定时器监视器和组织形式

    我们先看下定时器监视器的数据结构.(转载请指明出于breaksoftware的csdn博客) /* invoked after a specific time, repeatable (based o ...

  7. libev源码解析——定时器原理

    本文将回答<libev源码解析--I/O模型>中抛出的两个问题.(转载请指明出于breaksoftware的csdn博客) 对于问题1:为什么backend_poll函数需要指定超时?我们 ...

  8. libev源码解析——I/O模型

    在<libev源码解析--总览>一文中,我们介绍过,libev是一个基于事件的循环库.本文将介绍其和事件及循环之间的关系.(转载请指明出于breaksoftware的csdn博客) 目前i ...

  9. libev源码解析——调度策略

    在<libev源码解析--监视器(watcher)结构和组织形式>中介绍过,监视器分为[2,-2]区间5个等级的优先级.等级为2的监视器最高优,然后依次递减.不区分监视器类型和关联的文件描 ...

最新文章

  1. ligertree内容过多导致崩溃_师傅崩溃了!自身是8年的老师傅,料也没问题,但每次都堵管...
  2. 《明解C语言》pdf
  3. python中的垃圾回收机制_python里面的垃圾回收机制
  4. 1005:I Think I Need a Houseboat-poj
  5. [js] 写一个方法实现promise失败后自动重试
  6. java secondtotime_Java中的LocalTime toSecondOfDay()方法
  7. 2011年白银机会远超黄金 四妙招帮您赚大
  8. Xcode下的中文乱码问题
  9. 一道海量日志的随机选取问题
  10. spring cloud gateway 源码解析(4)跨域问题处理
  11. php code128扫码不能识别,求助excel中code128字体打印出的条码扫描枪读不出
  12. 数据结构实验一,第10题:基于顺序存储结构的图书信息表的图书去重
  13. TortoiseHg笔记
  14. 自行车码表使用说明—SIGMA(西格玛) BC906
  15. Automatic Targetless LiDAR-CameraCalibration: A Survey论文解读
  16. Vim的几个高级玩法
  17. XYIXY.COM短网址在线生成,快速、稳定、永久有效,免费开放网址缩短API接口。...
  18. 苹果系统版本依次顺序_iphone所有型号上市顺序
  19. Python 用OPEN读文件报错 ,路径以及r
  20. 【自学Java】Java语言特点

热门文章

  1. javascript 常用函数大全
  2. 2021最新Node.js官网安装教程,配置环境变量(图文详细)
  3. android长按home键设置的,Android中屏蔽电源键长按、Home键、Home键长按
  4. macOS系统升级后idea使用svn报错
  5. 最新版苹果cms源码韩剧TV电影视频在线观看网站源码
  6. 虹膜识别论文3:DeepIrisNet:IEEE 2016 学习心得
  7. 北航研究生课程机器学习考试试题2020年秋季回忆
  8. iBypasser完美支持iOS9-14系统·支持ID登录消息推送
  9. HTML菊花图案绘制,用HTML5中的canvas元素画菊花
  10. 淘宝开店8大绝招(转)