Django ManyToMany filter()


131

我有一个模型:

class Zone(models.Model):
    name = models.CharField(max_length=128)
    users = models.ManyToManyField(User, related_name='zones', null=True, blank=True)

我需要根据以下内容构造一个过滤器:

u = User.objects.filter(...zones contains a particular zone...)

它必须是User的过滤器,并且必须是单个过滤器参数。这样做的原因是,我正在构造一个URL查询字符串以过滤管理员用户更改列表:http://myserver/admin/auth/user/?zones=3

看起来应该很简单,但我的大脑却不配合!


8
我不知道如果我得到你的权利-是不是User.objects.filter(zones__id=<id>)还是User.objects.filter(zones__in=<id(s)>)好的呢?
TomaszZieliński,2010年

没关系:) BTW User.objects.filter(zones__in=<id(s)>)可能应该是User.objects.filter(zones__id__in=<id(s)>)
TomaszZieliński2010年

21
只是想为任何使用Google搜索的人指出,只有在设置related_name时它才有效。例如,zone_set无效。浪费了一个半小时:-)

Answers:


155

只是重申托马斯所说的话。

多对多多对一测试中,有很多FOO__in=...样式过滤器的示例。这是您特定问题的语法:

users_in_1zone = User.objects.filter(zones__id=<id1>)
# same thing but using in
users_in_1zone = User.objects.filter(zones__in=[<id1>])

# filtering on a few zones, by id
users_in_zones = User.objects.filter(zones__in=[<id1>, <id2>, <id3>])
# and by zone object (object gets converted to pk under the covers)
users_in_zones = User.objects.filter(zones__in=[zone1, zone2, zone3])

使用querysets时,会在各处使用双下划线(__)语法。


谢谢@maxm。更新了一些示例的最新链接。
istruble 2012年

9
加倍下划线(损失3小时,输给那个人)
重获新生

能否请您说,如果我想要一组区域中的用户而不是其中任何一个,该怎么办?假设找到位于zone1,zone3,..和zone 10中的用户
FRR

看一下...__in后面的例子# filtering on a few zones, by id。这些显示对多个ID /对象的过滤(在这种情况下)。只需传入您关心的zone1,zone3和zone10 id /对象即可。或根据需要添加第4个。
istruble

谢谢。我只是针对单个值进行过滤,而不是针对包含单个值的数组进行过滤。
zypro

36

请注意,如果用户可能在查询中使用的多个区域中,则可能要添加.distinct()。否则,您会多次获得一个用户:

users_in_zones = User.objects.filter(zones__in=[zone1, zone2, zone3]).distinct()

1

另一种方法是通过中间表。我会在Django ORM中这样表达:

UserZone = User.zones.through

# for a single zone
users_in_zone = User.objects.filter(
  id__in=UserZone.objects.filter(zone=zone1).values('user'))

# for multiple zones
users_in_zones = User.objects.filter(
  id__in=UserZone.objects.filter(zone__in=[zone1, zone2, zone3]).values('user'))

如果不需要.values('user')指定它会很好,但是Django(版本3.0.7)似乎需要它。

上面的代码最终将生成类似于以下内容的SQL:

SELECT * FROM users WHERE id IN (SELECT user_id FROM userzones WHERE zone_id IN (1,2,3))

这很好,因为它没有任何可能导致重复用户返回的中间联接


iya 这本身并不是答案。您应该添加评论或编辑QB的答案,而不是添加其他部分答案。
安迪·贝克

是的-如果您要编辑答案以使其完全完成(除非您有足够的业力来编辑QB的答案?),那将是最好的选择。理想情况下,在StackOverflow上有一个“正确答案”。它通常不能很好地解决问题,但值得一试。
安迪·贝克

@AndyBaker同意!回想起来,QB的答案可能应该是对istruble答案的评论,而我认为我的观点与众不同,值得单独回答,但是很好
Sam Mason
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.