AND 查询 Django ORM 中的外键 table
AND query against foreign key table in django ORM
给定:
class Video(models.Model):
tags = models.ManyToManyField(Tag)
class Tag(models.Model):
name = models.CharField(max_length=20)
我知道我可以使用 Video.objects.filter(tags__name__in=['foo','bar'])
找到所有具有 foo
或 bar
标签的 Videos
,但是为了找到具有 foo
AND bar
的那些,我必须两次加入外键(如果我手写 SQL ).有没有办法在 Django 中完成此操作?
我已经尝试过 .filter(Q(tag__name='foo') & Q(tag__name='bar'))
但这只会创建无法满足查询的条件,其中单个 Tag
同时具有 foo
和 bar
作为其名称。
这并不像看起来那么简单。此外 JOIN
使用相同的 table 两次通常根本不是一个好主意:假设您的列表包含十个元素。你要JOIN
十次吗?这很容易变得不可行。
然而,我们可以做的是计算重叠部分。因此,如果给定一个元素列表,我们首先要确保这些元素是唯一的:
tag_list = ['foo', 'bar']
tag_set = <b>set(</b>tag_list<b>)</b>
接下来我们计算集合中实际存在的Video
标签的数量,然后我们检查该数字是否与我们集合中的元素数量相同,例如:
from django.db.models import <b>Q</b>
Video.objects.filter(
<b>Q(tag__name__in=tag_set) | Q(tag__isnull=True)</b>
).annotate(
<b>overlap=Count('tag')</b>
).filter(
overlap=<b>len(tag_set)</b>
)
请注意,Q(tag__isnull-True)
用于启用 Video
s 而没有 标签。这可能看起来没有必要,但如果 tag_list
为空,我们因此想要获得 all 个视频(因为它们的共同标签为零)。
我们还假设 Tag
的名称是 唯一的 ,否则某些标签可能会被计算两次。
在幕后,我们将执行如下查询:
SELECT `video`.*, COUNT(`video_tag`.`tag_id`) AS overlap
FROM `video`
LEFT JOIN `video_tag` ON `video_tag`.`video_id` = `video`.`id`
LEFT JOIN `tag` ON `tag`.`id` = `video_tag`.`tag_id`
WHERE `tag`.`name` IN ('foo', 'bar')
GROUP BY `video`.`id`
HAVING overlap = 2
给定:
class Video(models.Model):
tags = models.ManyToManyField(Tag)
class Tag(models.Model):
name = models.CharField(max_length=20)
我知道我可以使用 Video.objects.filter(tags__name__in=['foo','bar'])
找到所有具有 foo
或 bar
标签的 Videos
,但是为了找到具有 foo
AND bar
的那些,我必须两次加入外键(如果我手写 SQL ).有没有办法在 Django 中完成此操作?
我已经尝试过 .filter(Q(tag__name='foo') & Q(tag__name='bar'))
但这只会创建无法满足查询的条件,其中单个 Tag
同时具有 foo
和 bar
作为其名称。
这并不像看起来那么简单。此外 JOIN
使用相同的 table 两次通常根本不是一个好主意:假设您的列表包含十个元素。你要JOIN
十次吗?这很容易变得不可行。
然而,我们可以做的是计算重叠部分。因此,如果给定一个元素列表,我们首先要确保这些元素是唯一的:
tag_list = ['foo', 'bar']
tag_set = <b>set(</b>tag_list<b>)</b>
接下来我们计算集合中实际存在的Video
标签的数量,然后我们检查该数字是否与我们集合中的元素数量相同,例如:
from django.db.models import <b>Q</b>
Video.objects.filter(
<b>Q(tag__name__in=tag_set) | Q(tag__isnull=True)</b>
).annotate(
<b>overlap=Count('tag')</b>
).filter(
overlap=<b>len(tag_set)</b>
)
请注意,Q(tag__isnull-True)
用于启用 Video
s 而没有 标签。这可能看起来没有必要,但如果 tag_list
为空,我们因此想要获得 all 个视频(因为它们的共同标签为零)。
我们还假设 Tag
的名称是 唯一的 ,否则某些标签可能会被计算两次。
在幕后,我们将执行如下查询:
SELECT `video`.*, COUNT(`video_tag`.`tag_id`) AS overlap
FROM `video`
LEFT JOIN `video_tag` ON `video_tag`.`video_id` = `video`.`id`
LEFT JOIN `tag` ON `tag`.`id` = `video_tag`.`tag_id`
WHERE `tag`.`name` IN ('foo', 'bar')
GROUP BY `video`.`id`
HAVING overlap = 2