Как оптимизировать 2 annotate

2 annotate создают большую нагрузку на БД: вот почему.

Перед прочтением лучше узнать о ленивых запросах.

Но и доставать кол-во комментариев для постов по одному запросу всё ещё жутко не эффективно. Можно оптимизировать это так:

1. Достать id самых популярных постов (annotate по лайкам)

most_popular_posts = Post.objects.annotate(likes_count=Count('likes')).order_by('-likes_count')
most_popular_posts_ids = [post.id for post in most_popular_posts]

2. Достать количество комментариев для этих постов с помощью annotate (annotate по комментариям

posts_with_comments = Post.objects.filter(id__in=most_popular_posts_ids).annotate(comments_count=Count('comments'))
ids_and_comments = posts_with_comments.values_list('id', 'comments_count')
count_for_id = dict(ids_and_comments)    

ids_and_comments — это список пар из id и количества комментариев. Выглядит так: <QuerySet [(165, 138), (244, 138), (95, 143), (444, 112), (368, 126)]>. С помощью dict() он преобразуется в обычный словарь, где ключ — это id поста, а значение — количество комментариев.

3. Присоединить количество комментариев к постам (Объединение)

for post in most_popular_posts:
    post.comments_count = count_for_id[post.id]

У поста нет такого поля, но вы можете вот так “временно” их добавлять. Это происходит только на уровне Python, в БД ничего не меняется.

4. Как пользоваться

Теперь у поста есть оба атрибута:

for post in most_popular_posts:
    print(post.likes_count)
    print(post.comments_count)

Попробуйте бесплатные уроки по Python

Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.

Переходите на страницу учебных модулей «Девмана» и выбирайте тему.