查询中的 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 给定 Sport
s)
解决方案并不简单。但是,如果您可以计算列表的 length,那么可以通过计算运动列表之间 overlap 的数量来完成,并且sports
一 Person
播放:
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_list
中 Person
的运动数量是 sports_list
中元素的数量,那么我们知道那个人玩 所有那些运动。
在 sport_list
中的非唯一 Sport
请注意 sport_lists
应包含 unique Sport
对象(或 ID)。但是,您可以构建 sports
的 set,例如:
# 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
不 播放 全部 给定 Sport
s)
一个相关的问题可能是排除那些人:参加所有指定运动的人。我们也可以这样做,但是这样会出现一个问题:根本不参加运动的人也会被排除在外,因为第一个 .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))
假设我有 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 给定 Sport
s)
解决方案并不简单。但是,如果您可以计算列表的 length,那么可以通过计算运动列表之间 overlap 的数量来完成,并且sports
一 Person
播放:
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_list
中 Person
的运动数量是 sports_list
中元素的数量,那么我们知道那个人玩 所有那些运动。
在 sport_list
中的非唯一 Sport
请注意 sport_lists
应包含 unique Sport
对象(或 ID)。但是,您可以构建 sports
的 set,例如:
# 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
不 播放 全部 给定 Sport
s)
一个相关的问题可能是排除那些人:参加所有指定运动的人。我们也可以这样做,但是这样会出现一个问题:根本不参加运动的人也会被排除在外,因为第一个 .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))