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

Django ALL in query (as opposed to OR / plain IN clause)

假设我有 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。所以像:

Person.objects.filter(sports__id__all=[1,2,3])

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

过滤(保留 People 播放 all 给定 Sports)

解决方案并不简单。但是,如果您可以计算列表的 length,那么可以通过计算运动列表之间 overlap 的数量来完成,并且sportsPerson 播放:

from django.db.models import <b>Count</b>

sports_list = [1, 2, 3]

Person.objects.filter(
    <b>sports__in=sports_list</b>
).annotate(
    <b>overlap=Count('sports')</b>
).filter(<b>overlap=len(sports_list)</b>)

所以如果 sports_listPerson 的运动数量是 sports_list 中元素的数量,那么我们知道那个人玩 所有那些运动。

sport_list

中的非唯一 Sport

请注意 sport_lists 应包含 unique Sport 对象(或 ID)。但是,您可以构建 sportsset,例如:

# in case a sport can occur *multiple times in the list

from django.db.models import Count

sports<b>_set</b> = <b>set(</b>[1, 2, 3, 2, 3, 3]<b>)</b>

Person.objects.filter(
    sports__in=sports<b>_set</b>
).annotate(
    overlap=Count('sports')
).filter(overlap=len(sports_<b>_set</b>))

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 播放 全部 给定 Sports)

一个相关的问题可能是排除那些人:参加所有指定运动的人。我们也可以这样做,但是这样会出现一个问题:根本不参加运动的人也会被排除在外,因为第一个 .filter(..) 会移除这些人。但是,我们可以稍微更改代码,以便将这些也包括在内:

# opposite problem: excluding those people

from django.db.models import <b>Q,</b> Count

sports<b>_set</b> = <b>set(</b>[1, 2, 3, 2, 3, 3]<b>)</b>

Person.objects.filter(
    <b>Q(</b>sports__in=sports_set<b>) | Q(sports__isnull=True)</b>
).annotate(
    overlap=Count('sports')
)<b>.exclude</b>(overlap=len(sports__set))