Это не так прямолинейно, как может показаться.Более того, JOIN
два раза использовать одну и ту же таблицу обычно не очень хорошая идея: представьте, что ваш список содержит десять элементов.Вы собираетесь 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
пусто, мы, таким образом, хотим получить все видео (поскольку у них нет общих тегов).
Мы также предполагаем, чтоимена Tag
s уникальные , в противном случае некоторые теги могут быть подсчитаны дважды.
За занавесами мы выполним запрос, подобный:
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