Django агрегация для 3 связанных таблиц с отношением один к одному и один ко многим

У меня есть 3 модели django (таблицы), допустим, певец, альбом и песня.

singer имеет колонки id, album_id (внешний ключ к таблице album), first_name, last_name

album имеет колонки id, title, num_of_album_wishlist, num_of_album_stars

song имеет колонки id, album (внешний ключ к таблице album) num_of_streams, song_duration_in_secs

Певец имеет отношение один к одному с альбомом, а альбом имеет отношение один ко многим с песней.

эти таблицы являются лишь фиктивным представлением и не обязательно имеют смысл, но адекватно демонстрируют проблему.

Нужно создать кверисет, в котором для каждого певца (в кверисете), нужно узнать сумму num_of_album_wishlist, num_of_album_stars из таблицы song, а также total_num_of_songs для пользователя, sum of num_of_streams и song_duration_in_secs в одном кверисете.

Singer.objects.annotate(
    wishlist=Sum('album__num_of_album_wishlist'),
    stars=Sum('album__num_of_album_stars'),
    streams=Sum('album__song_group__num_of_streams'),
    duration=Sum('album__song_group__song_duration_in_secs')
)

Приведенный выше запрос дает неверные результаты, связанные с отношением один ко многим между таблицей альбомов и таблицей песен.

subq = Singer.objects.annotate(
    streams=Sum('album__song_group__song_duration_in_secs'),
    duration=Sum('album__song_group__song_duration_in_secs')
    ).filter(pk=OuterRef('pk'))

queryset = Singer.objects.prefetch_related(
    'album').annotate(
    streams=Subquery(subq.values('streams')),
    duration=Subquery(subq.values('duration')),
    wishlist=Sum('album__num_of_album_wishlist'),
    stars=Sum('album__num_of_album_stars'),
    )

Использование подзапроса для решения этой проблемы работает, но создает различные блоки для объединения в SQL-запросе (что в дальнейшем приводит к длительному времени выполнения запроса)

Есть предложения, как решить эту проблему?

Вы можете попробовать выполнить предварительную выборку album__song_group в подзапросе. Это должно стать быстрее.

Вернуться на верх