Django筛选查询集__in列表中的* every *项

假设我有以下模型

class Photo(models.Model): tags = models.ManyToManyField(Tag) class Tag(models.Model): name = models.CharField(max_length=50) 

在视图中,我有一个名为类别的活动filter的列表。 我想过滤所有标签出现在类别的照片对象。

我试过了:

 Photo.objects.filter(tags__name__in=categories) 

但是这匹配任何类别的项目,而不是所有的项目。

所以如果类别是['假期','夏天']我想要照片的假期和夏天的标签。

这可以实现吗?

概要:

根据评论中的jpic和sgallen的build议,一个选项是为每个类别添加.filter() 。 每个额外的filter添加更多的连接,这不应该是一小组类别的问题。

有聚合 方法 。 这个查询会更短,也许更快的一大类。

您也可以select使用自定义查询 。


一些例子

testing设置:

 class Photo(models.Model): tags = models.ManyToManyField('Tag') class Tag(models.Model): name = models.CharField(max_length=50) def __unicode__(self): return self.name In [2]: t1 = Tag.objects.create(name='holiday') In [3]: t2 = Tag.objects.create(name='summer') In [4]: p = Photo.objects.create() In [5]: p.tags.add(t1) In [6]: p.tags.add(t2) In [7]: p.tags.all() Out[7]: [<Tag: holiday>, <Tag: summer>] 

使用链式filter的方法:

 In [8]: Photo.objects.filter(tags=t1).filter(tags=t2) Out[8]: [<Photo: Photo object>] 

结果查询:

 In [17]: print Photo.objects.filter(tags=t1).filter(tags=t2).query SELECT "test_photo"."id" FROM "test_photo" INNER JOIN "test_photo_tags" ON ("test_photo"."id" = "test_photo_tags"."photo_id") INNER JOIN "test_photo_tags" T4 ON ("test_photo"."id" = T4."photo_id") WHERE ("test_photo_tags"."tag_id" = 3 AND T4."tag_id" = 4 ) 

请注意,每个filter向查询添加更多JOINS

使用注释 方法 :

 In [29]: from django.db.models import Count In [30]: Photo.objects.filter(tags__in=[t1, t2]).annotate(num_tags=Count('tags')).filter(num_tags=2) Out[30]: [<Photo: Photo object>] 

结果查询:

 In [32]: print Photo.objects.filter(tags__in=[t1, t2]).annotate(num_tags=Count('tags')).filter(num_tags=2).query SELECT "test_photo"."id", COUNT("test_photo_tags"."tag_id") AS "num_tags" FROM "test_photo" LEFT OUTER JOIN "test_photo_tags" ON ("test_photo"."id" = "test_photo_tags"."photo_id") WHERE ("test_photo_tags"."tag_id" IN (3, 4)) GROUP BY "test_photo"."id", "test_photo"."id" HAVING COUNT("test_photo_tags"."tag_id") = 2 

AND ed Q对象将无法正常工作:

 In [9]: from django.db.models import Q In [10]: Photo.objects.filter(Q(tags__name='holiday') & Q(tags__name='summer')) Out[10]: [] In [11]: from operator import and_ In [12]: Photo.objects.filter(reduce(and_, [Q(tags__name='holiday'), Q(tags__name='summer')])) Out[12]: [] 

结果查询:

 In [25]: print Photo.objects.filter(Q(tags__name='holiday') & Q(tags__name='summer')).query SELECT "test_photo"."id" FROM "test_photo" INNER JOIN "test_photo_tags" ON ("test_photo"."id" = "test_photo_tags"."photo_id") INNER JOIN "test_tag" ON ("test_photo_tags"."tag_id" = "test_tag"."id") WHERE ("test_tag"."name" = holiday AND "test_tag"."name" = summer ) 

这也可以通过dynamic查询生成使用Django的ORM和一些Python魔术:)

 from operator import and_ from django.db.models import Q categories = ['holiday', 'summer'] res = Photo.filter(reduce(and_, [Q(tags__name=c) for c in categories])) 

这个想法是为每个类别生成适当的Q对象,然后使用AND运算符将它们合并到一个QuerySet中。 例如,对于你的例子,它会等于

 res = Photo.filter(Q(tags__name='holiday') & Q(tags__name='summer')) 

另一种适用于PostgreSQL的方法是使用django.contrib.postgres.fields.ArrayField

从文档复制的示例:

 >>> Post.objects.create(name='First post', tags=['thoughts', 'django']) >>> Post.objects.create(name='Second post', tags=['thoughts']) >>> Post.objects.create(name='Third post', tags=['tutorial', 'django']) >>> Post.objects.filter(tags__contains=['thoughts']) <QuerySet [<Post: First post>, <Post: Second post>]> >>> Post.objects.filter(tags__contains=['django']) <QuerySet [<Post: First post>, <Post: Third post>]> >>> Post.objects.filter(tags__contains=['django', 'thoughts']) <QuerySet [<Post: First post>]> 

ArrayField具有一些更强大的function,如重叠和索引转换 。