我正在使用Django,Python 3.7和PostgreSQL 9.5。我有这些模型:
class Article(models.Model):
...
label = models.TextField(default='', null=True)
class Label(models.Model):
name = models.CharField(max_length=200)
我想编写一个Django查询,以检索标签中包含标签名称的所有文章。在PostGres中,我可以这样构造查询:
select a.* from myapp_article a join myapp_label l on a.label ilike '%' || l.name || '%';
但是由于“ on”子句和“ ilike”,我不知道如何在Django中实现这一点。我该怎么拉?
答案 0 :(得分:1)
如果您必须在Article的标签上进行不区分大小写的搜索以查找匹配的名称,则可以使用regex并将其传递给所有标签名称的统一列表,如下所示:
Article.objects.filter(label__iregex=r'(' + '|'.join(Label.objects.all().values_list('name', flat=True)) + ')')
上面的查询所做的是,它使标签的列表平坦:
['label1' , 'label2', 'label3']
,然后像这样将字符串连接起来:
'(label1|label2|label3)'
并使用类似的SQL查询:
SELECT * from FROM "app_article" WHERE "app_article"."label" ~* (label1|label2|label3)
否则,对于区分大小写的方法,可以使用以下方法:
names_list = Label.objects.all().values_list('name', flat=True)
Article.objects.filter(label__in=names_list)
答案 1 :(得分:0)
在您的class Article
中,您必须将标签声明为class Label
的外键
class Article(models.Model):
...
label = models.ForeignKey(Label, default='', on_delete=models.CASCADE)
然后您可以访问它。
答案 2 :(得分:0)
这不会转换为相同的SQL查询,但是使用内部查询会产生相同的结果。
inner_query = Label.objects.annotate(article_label=OuterRef('label')).filter(article_label__icontains=F('name'))
articles = Article.objects.annotate(labels=Subquery(inner_query.values('name')[:1])).filter(labels__isnull=False)
这应该大致翻译为:
select a.* from myapp_article a where exists (select l.* from myapp_label l where a.label ilike '%' || l.name || '%')
但是,由于Django当前有关于在注解中使用OuterRef的issue,这种方法行不通。我们需要使用建议的here解决方法,直到解决该问题以使此查询正常工作为止,如下所示:
首先定义自定义表达式
class RawCol(Expression):
def __init__(self, model, field_name):
field = model._meta.get_field(field_name)
self.table = model._meta.db_table
self.column = field.column
super().__init__(output_field=CharField())
def as_sql(self, compiler, connection):
sql = f'"{self.table}"."{self.column}"'
return sql, []
然后使用此表达式构建查询
articles = Article.objects.all().annotate(
labels=Subquery(
Label.objects.all().annotate(
article_label=RawCol(Article, 'label')
).filter(article_label__icontains=F('name')).values('name')[:1]
)
).filter(labels__isnull=False)
这应该返回Article模型的实例,该实例的label字段包含Label模型的name字段中的值