Django-Filter源码解析一
Django Filter源码解析
最近在看Django-FIlter项目的源码,学习一下别人的开发思想;
整体介绍
首先,我从其中一个测试用例作为入口,开始了debug之路,一点一点的断点,分析它的执行顺序,如图:
ok,下面从代码的层面进行分析:
url
url(r'^books/$', FilterView.as_view(model=Book)),
view函数,这里的实现方式应该是借鉴了Django中自带的ListView,其同样的继承了MultipleObjectTemplateResponseMixin, BaseListView,继承的好处在于可以复用其已经封装好的方法,最终可以简单的实现展示,详情可以看
class FilterView(MultipleObjectTemplateResponseMixin, BaseFilterView):
"""
Render some list of objects with filter, set by `self.model` or
`self.queryset`.
`self.queryset` can actually be any iterable of items, not just a queryset.
"""
template_name_suffix = '_filter'
基础过滤view,这里做的就是类似BaseListView的功能,获取计算出来的查询集,将结果渲染后返回;
class BaseFilterView(FilterMixin, MultipleObjectMixin, View):
"""
显示对象的过滤功能的基view,实现的方式类似BaseListView
"""
def get(self, request, *args, **kwargs):
# 获取过滤的类
filterset_class = self.get_filterset_class()
# 传入类,构造参数,返回类的对象
self.filterset = self.get_filterset(filterset_class)
# 重新赋值MultipleObjectMixin中的object_list
if self.filterset.is_valid() or not self.get_strict():
self.object_list = self.filterset.qs
else:
self.object_list = self.filterset.queryset.none()
context = self.get_context_data(filter=self.filterset,
object_list=self.object_list)
return self.render_to_response(context)接下来就分成三件事:a.获取过滤类,b.根据过滤类获取过滤对象,c.过滤,下面的代码就做到了前面两步;
class FilterMixin(metaclass=FilterMixinRenames):
"""
A mixin that provides a way to show and handle a FilterSet in a request.
提供控制过滤的方法
"""
def get_filterset_class(self):
"""
Returns the filterset class to use in this view
返回过滤类
"""
if self.filterset_class: # 避免重复创建
return self.filterset_class
elif self.model:
# 使用了工厂模式
return filterset_factory(model=self.model, fields=self.filterset_fields)
else:
msg = "'%s' must define 'filtserset_class' or 'model'"
raise ImproperlyConfigured(msg % self.__class__.__name__)
def get_filterset(self, filterset_class):
"""
Returns an instance of the filterset to be used in this view.
"""
kwargs = self.get_filterset_kwargs(filterset_class)
return filterset_class(**kwargs)
def filterset_factory(model, fields=ALL_FIELDS):
# 根据model生成相对应的FilterSet,比如model是Book,那么就会生成BookFilterSet的实例
meta = type(str('Meta'), (object,), {'model': model, 'fields': fields})
# 使用type进行创建类,并且继承了FilterSet类
filterset = type(str('%sFilterSet' % model._meta.object_name),
(FilterSet,), {'Meta': meta})
return filterset接下来就是重头戏,开始过滤了!下面会被调用是因为调用了FilterSet中的qs方法;
class BaseFilterSet(object):
# ...
def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
# 如果没传进来则在全部的基础进行过滤
if queryset is None:
queryset = self._meta.model._default_manager.all()
model = queryset.model
# ...
self.filters = copy.deepcopy(self.base_filters)
# propagate the model and filterset to the filters
for filter_ in self.filters.values():
filter_.model = model
filter_.parent = self
def filter_queryset(self, queryset):
"""
Filter the queryset with the underlying form's `cleaned_data`. You must
call `is_valid()` or `errors` before calling this method.
This method should be overridden if additional filtering needs to be
applied to the queryset before it is cached.
"""
for name, value in self.form.cleaned_data.items():
# 重复执行,queryset会在每次执行后的queryset上继续执行,达到过滤的效果
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
@property
def qs(self):
if not hasattr(self, '_qs'):
qs = self.queryset.all()
if self.is_bound:
# ensure form validation before filtering
self.errors
qs = self.filter_queryset(qs)
self._qs = qs
return self._qs
或许你会疑惑self.filters
(self.base_filters
)里面的内容是什么,其实就是每个需要过滤的数据库字段到具体的Filter的映射,那这个是哪里进行计算赋值的呢?其实是被元类给拦截了,下面则会把该的内容是从类的get_filters方法中获取得到的,
class FilterSet(BaseFilterSet, metaclass=FilterSetMetaclass):
pass
class FilterSetMetaclass(type):
# 元类,FilterSet创建时最终会创建FilterSetMetaclass的实例
def __new__(cls, name, 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(object):
# ...
@classmethod
def get_filters(cls):
"""
Get all filters for the filterset. This is the combination of declared and
generated filters.
获取到每个需要过滤的数据库字段到Filter的映射
比如:{title: CharFilter}
"""
# No model specified - skip filter generation
if 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 generation
if filter_name in cls.declared_filters:
filters[filter_name] = cls.declared_filters[filter_name]
continue
if field is not None:
filters[filter_name] = cls.filter_for_field(field, field_name, lookup_expr)
# filter out declared filters
undefined = [f for f in undefined if f not in cls.declared_filters]
if undefined:
raise TypeError(
"'Meta.fields' contains fields that are not defined on this FilterSet: "
"%s" % ', '.join(undefined)
)
# Add in declared filters. This is necessary since we don't enforce adding
# declared filters to the 'Meta.fields' option
filters.update(cls.declared_filters)
return filters
@classmethod
def filter_for_field(cls, field, field_name, lookup_expr='exact'):
field, lookup_type = resolve_field(field, lookup_expr)
default = {
'field_name': field_name,
'lookup_expr': lookup_expr,
}
filter_class, params = cls.filter_for_lookup(field, lookup_type)
default.update(params)
assert filter_class is not None, (
"%s resolved field '%s' with '%s' lookup to an unrecognized field "
"type %s. Try adding an override to 'Meta.filter_overrides'. See: "
"https://django-filter.readthedocs.io/en/master/ref/filterset.html"
"#customise-filter-generation-with-filter-overrides"
) % (cls.__name__, field_name, lookup_expr, field.__class__.__name__)
return filter_class(**default)
@classmethod
def filter_for_lookup(cls, field, lookup_type):
"""
过滤
:param field:
:param lookup_type:
:return:
"""
DEFAULTS = dict(cls.FILTER_DEFAULTS)
if hasattr(cls, '_meta'):
DEFAULTS.update(cls._meta.filter_overrides)
data = try_dbfield(DEFAULTS.get, field.__class__) or {}
filter_class = data.get('filter_class')
params = data.get('extra', lambda field: {})(field)
# if there is no filter class, exit early
if not filter_class:
return None, {}
# perform lookup specific checks
if lookup_type == 'exact' and getattr(field, 'choices', None):
return ChoiceFilter, {'choices': field.choices}
if lookup_type == 'isnull':
data = try_dbfield(DEFAULTS.get, models.BooleanField)
filter_class = data.get('filter_class')
params = data.get('extra', lambda field: {})(field)
return filter_class, params
if lookup_type == 'in':
class ConcreteInFilter(BaseInFilter, filter_class):
pass
ConcreteInFilter.__name__ = cls._csv_filter_class_name(
filter_class, lookup_type
)
return ConcreteInFilter, params
if lookup_type == 'range':
class ConcreteRangeFilter(BaseRangeFilter, filter_class):
pass
ConcreteRangeFilter.__name__ = cls._csv_filter_class_name(
filter_class, lookup_type
)
return ConcreteRangeFilter, params
return filter_class, params
具体的数据库字段类型对应的Filter如下,上面也就是根据这些来找到对应的Filter,发现没,是BaseFilterSet类的FILTER_DEFAULTS变量
FILTER_FOR_DBFIELD_DEFAULTS = {
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},
# Forward relationships
models.OneToOneField: {
'filter_class': ModelChoiceFilter,
'extra': lambda f: {
'queryset': remote_queryset(f),
'to_field_name': f.remote_field.field_name,
'null_label': settings.NULL_CHOICE_LABEL if f.null else None,
}
},
models.ForeignKey: {
'filter_class': ModelChoiceFilter,
'extra': lambda f: {
'queryset': remote_queryset(f),
'to_field_name': f.remote_field.field_name,
'null_label': settings.NULL_CHOICE_LABEL if f.null else None,
}
},
models.ManyToManyField: {
'filter_class': ModelMultipleChoiceFilter,
'extra': lambda f: {
'queryset': remote_queryset(f),
}
},
# Reverse relationships
OneToOneRel: {
'filter_class': ModelChoiceFilter,
'extra': lambda f: {
'queryset': remote_queryset(f),
'null_label': settings.NULL_CHOICE_LABEL if f.null else None,
}
},
ManyToOneRel: {
'filter_class': ModelMultipleChoiceFilter,
'extra': lambda f: {
'queryset': remote_queryset(f),
}
},
ManyToManyRel: {
'filter_class': ModelMultipleChoiceFilter,
'extra': lambda f: {
'queryset': remote_queryset(f),
}
},
}
ok,到这里就简单的介绍完毕了。
Django-Filter源码解析一的更多相关文章
- django -admin 源码解析
admin源码解析 单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单 ...
- Django settings源码解析
Django settings源码 Django中有两个配置文件 局部配置:配置文件settings.py,即项目同名文件夹下的settings.py文件 全局配置:django内部全局的配置文件se ...
- Django APIView源码解析
APIView使用:luffy项目中关于APIView的使用 在Django之 CBV和FBV中,我们是分析的from django.views import View下的执行流程,以下是代码 fro ...
- ReactiveCocoa源码解析(五) SignalProtocol的observe()、Map、Filter延展实现
上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...
- ReactiveSwift源码解析(五) SignalProtocol的observe()、Map、Filter延展实现
上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...
- 源码解析Django CBV的本质
Django CBV模式的源码解析 通常来说,http请求的本质就是基于Socket Django的视图函数,可以基于FBV模式,也可以基于CBV模式. 基于FBV的模式就是在Django的路由映射表 ...
- Django生命周期 URL ----> CBV 源码解析-------------- 及rest_framework APIView 源码流程解析
一.一个请求来到Django 的生命周期 FBV 不讨论 CBV: 请求被代理转发到uwsgi: 开始Django的流程: 首先经过中间件process_request (session等) 然后 ...
- django之admin源码解析
解析admin的源码 第一步:项目启动,加载settings文件中的 INSTALLED_APPS 里边有几个app就加载几个,按照注册顺序来执行. 第二步:其中加载的是admin.py,加载每一个a ...
- Django框架 之 admin管理工具(源码解析)
浏览目录 单例模式 admin执行流程 admin源码解析 单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在 ...
随机推荐
- bzoj千题计划183:bzoj1197: [HNOI2006]花仙子的魔法
http://www.lydsy.com/JudgeOnline/problem.php?id=1197 题意转化:在n维空间中放m个n维球,问最多将空间分成几部分 f[i][j] 表示在i维空间中放 ...
- HDU 3511 圆扫描线
找最深的圆,输出层数 类似POJ 2932的做法 圆扫描线即可.这里要记录各个圆的层数,所以多加一个维护编号的就行了. /** @Date : 2017-10-18 18:16:52 * @FileN ...
- 8 Productivity hacks for Data Scientists & Business Analysts
8 Productivity hacks for Data Scientists & Business Analysts Introduction I was catching up with ...
- expect 交互 telnet 交互
telnet 交互 #!/bin/bash Ip="10.0.1.53" a="\{\'method\'\:\'doLogin\'\,\'params\'\:\{\'uN ...
- 运算符,比如+, -, >, <, 以及下标引用[start:end]等等,从根本上都是定义在类内部的方法。
python解释器在碰到+号运算符时,会调用加号前面的对象的__add__方法 class a: def __add__(self,b): print "ghh" aa=a() a ...
- 使用Cobbler批量部署Linux和Windows:CentOS/Ubuntu批量安装(二)
通过前面服务端的部署,已经配置好了 Cobbler Server 端,接下来开始进行 CentOS/Ubuntu 的批量安装,在进行 CentOS/Ubuntu 批量安装时,也需要通过Cobbler来 ...
- 如何动态修改windows下的host文件
事件背景:为了测试数据提交后,需要在另一个环境的多个测试节点下去验证测试数据是否添加成功,找了一大堆放法,用了比较笨的方法实现了.不多废话思路如下: 为了万无一失,先备份hosts文件内容: 1.读取 ...
- reportng之测试报告升级美化
背景:偶然看到一个人的自动化框架的测试报告好漂亮,心痒痒,今天弄了一下午,还是不行,结果到现在就现在,我特么成功了,不为什么 Mark一下: 本地化修改 获取源码,修改reportng.propert ...
- opencv 车牌字符分割 ANN网络识别字符
最近在复习OPENCV的知识,学习caffe的深度神经网络,正好想起以前做过的车牌识别项目,可以拿出来研究下 以前的环境是VS2013和OpenCV2.4.9,感觉OpenCV2.4.9是个经典版本啊 ...
- [转]solver优化方法
原文地址:http://www.cnblogs.com/denny402/p/5074212.html 到目前为止,caffe总共提供了六种优化方法: Stochastic Gradient Desc ...