Django manytomany动态过滤

时间:2014-11-02 05:13:49

标签: django many-to-many

class Keyword(models.Model):
    keyword = models.CharField(max_length=100, unique=True)


class Item(models.Model):
    keywords = models.ManyToManyField(Keyword)

如何动态创建以下内容?即我可以添加'c'

items = Item.objects.filter(keywords__keyword__exact = 'a')
                    .filter(keywords__keyword__exact = 'b')

我尝试过构建Q对象:

tags = ['a', 'b']
queries = [Q(keywords__keyword__exact=tag) for tag in tags]

query = Q()
for q in queries:
    query &= q

items = Item.objects.filter(query)

但是这似乎使用像keyword = a & keyword = b之类的查询,它总是返回空。

1 个答案:

答案 0 :(得分:3)

对于这个问题,您的解决方案远远过于设计。

编辑:我没有意识到这个问题是在关键字之间寻找逻辑AND而不是逻辑OR。我已经用两个选项更新了这个答案。

对于OR案例

当您想要检索包含任何列出的关键字的项目时,这非常有用

Django提供a very good API for Queryset filtering,它为我们提供了一种更简单的方法来创建您想要的行为。没有必要为此创建一个令人困惑的Q对象解决方案。我将使用您的上一个代码段来解释更直接的解决方案,并使用Queryset查找关键字inhere's a link to the docs)。

tags = ['a', 'b']

# Removed the complex Q object definition.

items = Item.objects.filter(keywords__keyword__in=tags)

这应该为您提供一个结果Queryset,其中包含与关键字对象关联的Item对象,该关键字对象包含与列表tags中的任何值匹配的关键字。例如:

tags = ['a', 'b']

keyword_one = Keyword.objects.create(keyword='a')
keyword_two = Keyword.objects.create(keyword='b')
keyword_three = Keyword.objects.create(keyword='c')

item_one = Item.objects.create()
item_one.keywords.add(keyword_one)
item_one.save()

item_two = Item.objects.create()
item_two.keywords.add(keyword_two)
item_two.save()

item_three = Item.objects.create()
item_three.keywords.add(keyword_three)
item_three.save()

# items will only contain item_one and item_two, but not item_three.
tag_items = Item.objects.filter(keywords__keyword__in=tags)

对于AND案例:

当您想要检索列出了所有关键字的项目时,这非常有用

Here's an approach which will serve our purposes

相关代码示例:

tags = ['a', 'b']

items_with_one_of_tags = Item.objects.filter(keywords__keyword__in=tags)
items_with_all_of_tags = items_with_one_of_tags.annotate(number_of_keywords=Count('keywords')).filter(number_of_keywords=len(tags))

此方法的工作方式是在Queryset中创建一个聚合列,其中包含与每个Item对象关联的关键字对象的数量,这些对象与至少一个关键字对象相关联。然后,此聚合列(名为number_of_keywords)仅用于过滤列出具有确切关键字数量的Item对象。

通过此方法,Annotation方法可以删除具有某些关键字的Item对象。请注意,这不会返回具有列出的关键字且仅列出关键字的Item对象集。仅保证返回的Item对象将具有每个关键字。有an example in that linked discussion thread使用嵌套查询可以排除Item对象,除了列出的关键字之外还有额外的关键字。