Django ORM使用Subquery通过关键字在文本中搜索

时间:2018-10-16 08:48:48

标签: django orm subquery

我在Django中有两个模型。具有字段标题的文章模型和具有字段标题的关键字模型。我需要查询数据库并仅过滤那些标题包含关键字模型中任何关键字的文章。我尝试使用子查询进行搜索,但这是行不通的。

如果我尝试将filter的ID与in一起使用,则可以使用

from django.db.models import Subquery

Article.objects.filter(id__in=Subquery(Keyword.objects.values('id')))

但是,如果我尝试将filtericontains一起使用,那是行不通的:

Article.objects.filter(title__icontains=Subquery(Keyword.objects.values('title')))

最后一个查询返回空查询集 如何解决?

1 个答案:

答案 0 :(得分:0)

最简单的方法有两个查询:一个查询获取所有Keyword,然后一个查询使用此关键字过滤Article,例如:

from functools import reduce
from operator import or_

kws = Keyword.objects.values_list('title', flat=True)
articles = Article.objects.filter(
    reduce(or_, [Q(title__icontains=kw) for kw in kws])
)

但是,由于我们首先加载了所有标题,因此上述内容可能会导致庞大的查询。

另一种方法是使用JOIN制作一个.extra,然后执行“原始” SQL查询注释:

Article.objects.extra(
    tables=[Keyword._meta.db_table]
).annotate(
    key_title=RawSQL('{}.{}'.format(Keyword._meta.db_table, Keyword.title.field_name), ())
).filter(title__icontains=F('key_title'))

这将导致如下查询:

SELECT article.id, article.title, (keyword.title) AS key_title
FROM article , keyword
WHERE article.title LIKE CONCAT('%', REPLACE(REPLACE(REPLACE(((keyword.title)), '\\\\', '\\\\\\\\'), '%', '\\%'), '_', '\\_'), '%')

此处REPLACE(..)部分用于转义关键字标题,因为如果它包含%,则可能导致{{1}中出现“通配符” }表达式。这些都是由LIKE查找引入的,因此我们不必为此担心。