如何在Django中动态组合or查询过滤器

主要用于不确定字段要匹配的结果个数时使用,后面会详细距离其使用场景。

目前有以下需求需要查询,不考虑使用原生sql:

要用标题去模糊匹配用户输入的不定数量的字符串,比如:用户输入了china,华夏,我要用title去做模糊匹配,实际情况用户输入的词个数我们并不知道。

django磨人的or查询使用的是Q来实现,大概使用方式如下:

1
model.objects.filter(Q(title__icontains='中国') | Q(title__icontains='华夏') | Q(title__icontains='china'))

那我们如何动态的拼接起来呢?

方式一

1
2
3
4
5
6
7
8
9
10
11
# 要匹配的关键词
kw=["中国", "华夏", "china"]
# 查询条件写入列表
queries = [Q(title__icontains=i) for i in kw]
# 取出最后一个条件
q_objs = queries.pop()
# 遍历所有条件并连接
for i in queries:
q_objs |= i
# 执行查询
Article.objects.filter(q_objs).filter(status=1)

以上代码执行的sql大概如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
SELECT
`article`.`id`,
`article`.`time_update`,
`article`.`time_create`,
`article`.`title`,
`article`.`status`
FROM
`article`
WHERE
(
`article`.`status` = 1
AND ( `article`.`title` LIKE '%中国%' OR `article`.`title` LIKE '%china%' OR `article`.`title` LIKE '%华夏%' )
)

以上便实现了,or查询的动态过滤

方式二

跟方式一类似,构建更复杂的查询,还可以选择使用内置 Q() 对象的常量 Q.ORQ.AND 以及 add() 方法

1
2
3
4
5
6
7
8
9
# 要匹配的关键词
kw=["中国", "华夏", "china"]
# 初始化一个Q对象去使用(Q(id__in=[])这样写不会产生直接的or条件)
q_objs = Q(id__in=[])
# 遍历所有条件并连接
for i in kw:
q_objs.add(Q(title__icontains=i), Q.OR)
# 执行查询
Article.objects.filter(q_objs).filter(status=1)

以上代码生成sql语句跟方法一一致

1
2
3
4
5
6
7
8
9
10
11
12
13
SELECT
`article`.`id`,
`article`.`time_update`,
`article`.`time_create`,
`article`.`title`,
`article`.`status`
FROM
`article`
WHERE
(
`article`.`status` = 1
AND ( `article`.`title` LIKE '%中国%' OR `article`.`title` LIKE '%china%' OR `article`.`title` LIKE '%华夏%' )
)