为了账号安全,请及时绑定邮箱和手机立即绑定

Django过滤查询集__in为列表中的* every *项

Django过滤查询集__in为列表中的* every *项

阿晨1998 2019-08-16 17:09:37
Django过滤查询集__in为列表中的* every *项假设我有以下型号class Photo(models.Model):     tags = models.ManyToManyField(Tag)class Tag(models.Model):     name = models.CharField(max_length=50)在视图中,我有一个列表,其中包含名为categories的活动过滤器。我想过滤具有类别中所有标签的Photo对象。我试过了:Photo.objects.filter(tags__name__in=categories)但这匹配类别中的任何项目,而不是所有项目。因此,如果类别是['假日','夏天'],我希望照片有假日和夏季标签。这可以实现吗?
查看完整描述

3 回答

?
千万里不及你

TA贡献1784条经验 获得超9个赞

摘要:

正如jpic和sgallen在评论中所建议的那样,一个选项是.filter()为每个类别添加。每个附加项都会filter添加更多联接,这对于一小组类别来说应该不是问题。

聚合 方法。对于大量类别,此查询将更短并且可能更快。

您还可以选择使用自定义查询


一些例子

测试设置:

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

    def __unicode__(self):
        return self.nameIn [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>]

使用链式过滤器方法:

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 CountIn [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

ANDed Q对象不起作用:

In [9]: from django.db.models import QIn [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 )


查看完整回答
反对 回复 2019-08-16
?
holdtom

TA贡献1805条经验 获得超10个赞

另一种有效的方法,虽然只有PostgreSQL,但它正在使用django.contrib.postgres.fields.ArrayField:


从docs复制的示例:


>>> 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有一些更强大的功能,如重叠和索引转换。


查看完整回答
反对 回复 2019-08-16
?
浮云间

TA贡献1829条经验 获得超4个赞

这也可以通过使用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'))


查看完整回答
反对 回复 2019-08-16
  • 3 回答
  • 0 关注
  • 1502 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信