【问题标题】:Django ORM: dynamically add multiple conditions for ManyToManyFieldDjango ORM:为ManyToManyField动态添加多个条件
【发布时间】:2014-11-07 23:28:41
【问题描述】:

假设我们有如下定义的 Django 模型:

class Tag(models.Model):
  name=models.CharField(unique=True,max_length=50)

class Article(models.Model):
  title=models.CharField(max_length=100)
  text=models.TextField() 
  tag = models.ManyToManyField(Tag)

我们有一个标签列表:

tag_list = ['tag1','tag2','tag3']

目标是选择具有来自tag_list 的所有标签的文章。这个question 展示了一种通过顺序添加过滤条件来实现此目的的方法:

articles = Articles.objects.filter(Q(tag__name=tag_list[0])).filter(Q(tag__name=tag_list[1])).filter(Q(tag__name=tag_list[2]))

但是我们需要动态添加条件。下面的查询不起作用:

qlist=[]
for tag in tag_list:
  qlist.append(Q(tag__name=tag))
articles = Articles.objects.filter(reduce(operator.and_, qlist))

我最终从列表中查询了至少有一个标签的文章,然后手动过滤了查询结果:

qlist=[]
qlist.append(Q(tag__name__in=tag_list))
articles = Articles.objects.filter(reduce(operator.and_, qlist)).distinct()

for article in articles:
  article_tag_list=[]
  for tag in article.tag.all():
    article_tag_list.append(tag.name)
  if set(tag_list).issubset(set(article_tag_list)):
    ...

有没有办法为ManyToManyField动态添加查询条件?

【问题讨论】:

    标签: django django-models orm


    【解决方案1】:

    试试这个:

    q_objects = Q()
    for tag in tag_list:
      q_objects &= Q(tag__name=tag)
    articles = Articles.objects.filter(q_objects)
    

    上面的模式我用过很多次了,一直都对我有用。

    更新:虽然一般来说这种方法有效,但它不适用于这个问题。上面的代码要求单个相关对象的所有条件都为真。在这种情况下,这意味着必须有一个具有三个不同名称的标签,这显然是荒谬的。

    这是最终为 OP 工作的代码:

    for tag in tag_list:
        articles = articles.filter(tag__name=tag)
    

    Django docs provide a detailed explanation 将多个规则放在一个 filter() 和将它们放在多个 filter()s 之间的区别(在查询多值关系时)。

    【讨论】:

    • Ludwik,由于某种原因,这种方法对我不起作用。 'Q(tag_name=tag)&(text__icontains=word)' 之类的多个条件确实与这种方法相结合,但是一旦我为 'ManyToManyField' 空集添加多个条件,就会返回。
    • 我会尝试调试其他元素。你真的有一篇文章包含你在tag_list 列表中的所有标签吗?该列表是否真的包含您认为它包含的内容?如果你手动搜索这些标签(做.filter(Q(tag__name='tag1') & Q(tag__name='tag2') & Q(tag__name='tag3')))结果真的不一样吗?
    • Ludwik,我确实像你说的那样调试了它。这种手动过滤器也给出了一个空集。我发现另一个question 说要走的路是链接过滤器for tag in tag_list: articles=articles.filter(tag__name=tag)
    • 是的,这也应该有效。它适用于您的情况吗?
    • Ludwik,是的,它确实有效。而且我已经看到了证据,例如在 question 的 cmets 中,您最初提出的方法不起作用。
    猜你喜欢
    • 2015-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-28
    • 2021-08-28
    • 2016-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多