我有一个模特:
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
看起来应该很简单,但我的大脑不合作!
答案 0 :(得分:111)
重申Tomasz所说的话。
many-to-many和many-to-one测试中有许多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时,会在整个地方使用双下划线(__)语法。
答案 1 :(得分:22)
请注意,如果用户可能位于查询中使用的多个区域中,您可能希望添加.distinct()。否则你会多次获得一个用户:
users_in_zones = User.objects.filter(zones__in=[zone1, zone2, zone3]).distinct()
答案 2 :(得分:3)
另一种方法是通过中间表。我会在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))
这很不错,因为它没有任何中间联接,可能导致重复的用户被退回