假设我有2个模型通过多对多连接加入:
class Person(models.Model):
name = models.CharField(max_length=200, null=False, blank=False)
sports = models.ManyToManyField('Sport')
class Sport(models.Model):
name = models.CharField(max_length=200, null=False, blank=False)
people = models.ManyToManyField('Person')
我想执行一个AND查询来过滤那些在给定运动ID列表的情况下玩所有运动的人。如下所示:
Person.objects.filter(sports__id__all=[1,2,3])
或者,换句话说,排除任何不参加所有运动的人。
答案 0 :(得分:1)
People
播放所有给定Sport
s)解决方案并非无足轻重。但是,如果你可以计算列表的长度,那么可以通过计算运动列表和sports
a之间的重叠的数量来完成。 Person
播放:
from django.db.models import Count
sports_list = [1, 2, 3]
Person.objects.filter(
sports__in=sports_list
).annotate(
overlap=Count('sports')
).filter(overlap=len(sports_list))
因此,如果Person
中sports_list
的体育数量是sports_list
中的元素数量,那么我们就知道该人玩全部< / em>那些运动。
Sport
sport_list
s
请注意,sport_lists
应包含唯一的 Sport
个对象(或ID)。但是,您可以构建sports
的集,例如:
# in case a sport can occur *multiple times in the list
from django.db.models import Count
sports_set = set([1, 2, 3, 2, 3, 3])
Person.objects.filter(
sports__in=sports_set
).annotate(
overlap=Count('sports')
).filter(overlap=len(sports__set))
在窗帘后面,我们将构建一个类似的查询:
SELECT `person`.*
FROM `person`
INNER JOIN `person_sport` ON `person`.`id` = `person_sport`.`person_id`
WHERE `person_sport`.`sport_id` IN (1, 2, 3)
GROUP BY `person`.`id`
HAVING COUNT(`person_sport`.`sport_id`) = 3
People
不播放所有给定Sport
s)相关问题可能是排除这些人:参加所有指定运动的人。我们也可以做到这一点,但随后会出现一个问题:根本没有运动的人也会被排除在外,因为第一个.filter(..)
会删除这些人。但是,我们可以略微更改代码,以便包含这些代码:
# opposite problem: excluding those people
from django.db.models import Q, Count
sports_set = set([1, 2, 3, 2, 3, 3])
Person.objects.filter(
Q(sports__in=sports_set) | Q(sports__isnull=True)
).annotate(
overlap=Count('sports')
).exclude(overlap=len(sports__set))