Django ORM: Как фильтровать отношение "многие-ко-многим" без фильтрации связанных объектов
У меня есть несколько моделей Django, таких как следующие:
class Film:
genre = models.ManyToManyField("Genre", blank=True)
class Genre(TimeStampedModel):
name = models.CharField(max_length=50, blank=False)
slug = AutoSlugField(max_length=55, unique=True, populate_from="name")
Теперь я хочу выбрать все фильмы, которые связаны с жанром "Театр" и получить список всех связанных с ними жанров:
In [1]: from apps.base.models import Film, Genre
In [2]: qs = Film.objects.filter(genre__slug='theatre')
In [3]: set(qs.values_list("genre__slug", flat=True))
Out[3]: {'theatre'}
Однако это не тот результат, который я хочу получить. Если посмотреть на жанры первого фильма, возвращенного как часть набора запросов, мы можем увидеть больше жанров:
In [4]: set(qs[0].genre.all().values_list("slug", flat=True))
Out[4]: {'dance', 'education', 'theatre', 'video-recording'}
Как я могу эффективно отфильтровать фильмы до тех, которые связаны с жанром "Театр" и получить связанные с ними жанры?
Решением этой проблемы является запрос по жанру, фильтрующий по фильмам, возвращенным ранее:
In[23]: set(Genre.objects.filter(film__in=qs))
Out[23]:
{<Genre: Dance>,
<Genre: Education>,
<Genre: Theatre>,
<Genre: Video Recording>}
Я полагаю, что получить доступ к этой информации непосредственно из исходного запроса невозможно, поскольку другие жанры отфильтрованы предложением WHERE
, которое используется для отбора фильмов.
Однако такой подход кажется немного неэффективным, если используется prefetch_related('genre')
, поскольку в результате один и тот же запрос будет выполняться дважды, один раз для prefetch_related('genre')
и один раз для приложения, описанного выше.