源码分析
def filter_queryset(self, request, queryset, view):
# 获取咱们设定的待搜索的fields
search_fields = self.get_search_fields(view, request)
# 获取需要search的参数,默认为search,即去找url中的?search=xxx
search_terms = self.get_search_terms(request)
# 都没找到时就不过滤了
if not search_fields or not search_terms:
return queryset
# 拼接查询的传,比如search_fields = ['=id', 'name']
# 他就会拼接出['id__iexact', 'name__icontains']这两个list
# 这里[]里面的写法叫做:列表生成式(有点像lambda)
# [x*x for x in range(2,8)] 原型:[2*2,3*3,4*4,5*5,6*6,7*7]
# 打印结果:[4, 9, 16, 25, 36, 49]
orm_lookups = [
self.construct_search(str(search_field))
for search_field in search_fields
]
base = queryset
conditions = []
for search_term in search_terms:
queries = [
# Q查询——对对象的复杂查询
# 目前我的理解是这样,我们常用的id_contians只能对对象进行一次简单的过滤
# 运用Q可以运用 | & 等连接符设定复杂的过滤条件
# 比如Q(question__startswith='Who') | Q(question__startswith='What')
# 等价于 WHERE question LIKE 'Who%' OR question LIKE 'What%'
# 参考http://www.cnblogs.com/linjiqin/p/3817814.html
models.Q(**{orm_lookup: search_term})
for orm_lookup in orm_lookups
]
# reduce() 函数会对参数序列中元素进行累积。
# 这里就是把所有的queries用|连接起来
# 在所有的列中搜索某个值
conditions.append(reduce(operator.or_, queries))
# 合并搜索条件
queryset = queryset.filter(reduce(operator.and_, conditions))
if self.must_call_distinct(queryset, search_fields):
# Filtering against a many-to-many field requires us to
# call queryset.distinct() in order to avoid duplicate items
# in the resulting queryset.
# We try to avoid this if possible, for performance reasons.
# 当存在多对多的field的时候会调用distinct()去重,虽然我没看懂他的检查原理
queryset = distinct(queryset, base)
return queryset
其中这个代码很有意思,调用了一个getattr的函数,这是一个python的系统函数,可以获取某个对象的某个属性,比如getattr(view, 'search_fields', None)
等价于调用view.search_fields
。第三个参数就是当没有取到参数时候返回的默认值了。
def get_search_fields(self, view, request):
"""
Search fields are obtained from the view, but the request is always
passed to this method. Sub-classes can override this method to
dynamically change the search fields based on request content.
"""
return getattr(view, 'search_fields', None)
自定义修改
class MySearchFilter(filters.SearchFilter):
def filter_queryset(self, request, queryset, view):
# 获取允许搜索配置信息
search_fields = self.get_search_fields(view, request)
# 检查request参数中有没有我们允许搜索的
params_list = []
for field in search_fields:
# 去掉第一位的头
param = request.query_params.get(field[1:] if self.lookup_prefixes.get(field[0]) else field, None)
if param:
# 如果匹配到信息就加进队列里面
params_list.append(models.Q(**{self.construct_search(str(field)): param}))
base = queryset
# 用与操作合并所有条件
queryset = queryset.filter(reduce(operator.and_, params_list))
if self.must_call_distinct(queryset, search_fields):
queryset = distinct(queryset, base)
return queryset