查询中的Django ALL(与OR / plain IN子句相对)

时间:2018-06-11 19:28:55

标签: sql django django-orm

假设我有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])

或者,换句话说,排除任何不参加所有运动的人。

1 个答案:

答案 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))

因此,如果Personsports_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))

SQL查询

在窗帘后面,我们将构建一个类似的查询:

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