Django ManyTomany 过滤器()

我有一个模型:

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...)

它必须是一个过滤器上的用户,它必须是一个单一的过滤器参数。原因是我正在构建一个 URL 查询字符串来过滤管理员用户变更列表: http://myserver/admin/auth/user/?zones=3

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

180427 次浏览

只是重复一下托马斯说的话。

多对多多对一测试中有许多 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])

在使用 查询集时,双下划线(_ _)语法被广泛使用。

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

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

另一种方法是遍历中间表。我会在 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))

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

如果在类似这个问题的场景中有更复杂的查询,那么可以使用 问:对象。

基于这个问题,假设您希望过滤具有特定 id 的用户(例如10) ,并过滤名称以“ europe”开头的 Zones

查询应该是这样的:

filtered_zone = Q(zones__in=Zone.objects.filter(name__startswith='europe')
result = User.objects.filter(Q(id=10) & Q(filtered_zone))