Оптимизация пагинации django

Имеется сайт с довольно большой базой данных, более 40000 записей. При пагинации каждый раз выполняется запрос в бд, который занимает 1.5 секунды, что довольно долго, как для сайта. Пробовал кэширование view, но при переходе на следующую страницу пагинации кэш не срабатывает и запрос выполняется заново(после кэширования этой страницы, заново запрос не отправляется). Как возможно оптимизировать пагинацию, что бы снизить нагрузку на бд и сервер, а также ускорить время обработки запроса? Код view прилагаю.

class PostsView(View):
def get(self, request):
    mobile = request.user_agent.is_mobile
    time_now = timezone.now()
    posts_all = Post.objects.all()
    premium_posts = posts_all.filter(moderated=True, premium_till__gte=time_now)
    normal_posts = posts_all.filter(Q(moderated=True, premium_till__lt=time_now) | Q(moderated=True, premium_till=None))
    posts = list(chain(premium_posts, normal_posts))

    page = request.GET.get('page')
    paginator = Paginator(posts, 20)

    if page is None:
        difference = paginator.num_pages - 1
    else:
        difference = paginator.num_pages - int(page)

    try:
        posts_list = paginator.page(page)
    except PageNotAnInteger:
        posts_list = paginator.page(1)
    except EmptyPage:
        posts_list = paginator.page(paginator.num_pages)

    return render(request, "blog_app/posts_list.html", {"posts_list": posts_list, "difference" : difference, "time_now" : time_now, "mobile" : mobile})

Ответы (2 шт):

Автор решения: Za Ars
  1. Добавьте индексы в модель на все поля, используемые в фильтре и сортировке

  2. Если используются связанные объекты - добавьте select_related / prefetch_related

  3. Если используется сложная сортировка - подумайте о предсортировке (добавьте поле в модель, в котором будет храниться порядковый номер для типа сортировки и сортируйте по нему)

  4. Если все плохо, то остается кеширование объектов через django.core.cache.cache и ждать еще ответов


А вообще добавьте django-debug-toolbar и посмотрите, какие запросы выполняются, кол-во дублей и время выполнения. Тогда можно будет проще понять в какую сторону идти


UPD: Кешировать можно раз в день или реже, в management_command.

→ Ссылка
Автор решения: Артемий Шувалов

Может быть, имеет смысл пагинировать на уровне SQL запроса? Когда делается Post.objects.all() ведь выгребается всё в буфер. Вроде, у ORM django есть вариант сразу в этом запросе задавать offset и limit. Нечто вида Post.objects.all()[1:50]

→ Ссылка