Нужна оптимизация запросов Django (2 запроса вместо 21)

#python #django #postgresql #orm #django-orm

Вопрос:

У меня есть проект Django на EBS, и один метод, который возвращает набор Article запросов объектов, занимает довольно много времени. Я хотел бы оптимизировать его так, чтобы это был всего один или два запроса, но я не уверен, что это вообще возможно.

Модели: WebSite , Article

Запрос представляет собой список Articles с разных веб-сайтов.

WebSite модель имеет home_order home_article_count атрибуты a и, которые используются для выбора ряда статей и в каком порядке они расположены.

Пример для веб-сайтов A, B и C:

  1. A A.home_order = 2 , A.home_article_count=3
  2. B B.home_order = 1 , B.home_article_count=5
  3. C C.home_order = 3 , C.home_article_count=7

Результатом будет (aA означает статью с веб-сайта A):

аБ,аБ,аБ,аБ,аБ,аА,аА,аА,переменный ток,переменный ток,переменный ток,переменный ток,переменный ток, переменный ток

Списки начинаются с aB того , что означает статьи с веб-сайта B , потому B.home_order=1 что есть 5 aB статей как B.home_article_count=5 .

Прямо сейчас это Manager метод, который возвращает статьи:

 def home(self) -> QuerySet:
    articles_pks = []
    for ws in WebSite.objects.active().order_by('home_order', 'name'):
        articles_pks.extend(ws.articles.filter(is_active=True).order_by('-created')[:ws.home_article_count].values_list('pk',flat=True))
    return self.get_queryset().filter(pk__in=articles_pks)
 

Это означает, что для 20 объектов веб-сайта существует 21 запрос (один, который получает веб-сайты, а другие 20 для статей).

Можно ли объединить его в один или два запроса, соблюдая WebSite.home_order при этом и WebSite.home_article_count ?

Комментарии:

1. Это весь код для дома? return self.get_queryset().filter(pk__in=articles_pks) не заботится о заказе или вы просто удалили какой-то код, чтобы сделать его коротким?

2. @bdbd Это весь код, спасибо за уведомление.

3. До сих пор это выглядит как старый, с которым заказ отменен self.get_queryset().filter(pk__in=articles_pks) . Это намеренно?

4. @bdbd Нет, это не предназначено, но я могу отсортировать его в памяти, это не имеет большого значения.

Ответ №1:

Попробуйте объединить наборы запросов с помощью OR | оператора:

 def home(self) -> QuerySet:
    articles_pks = Article.objects.none()
    for ws in WebSite.objects.active().order_by('home_order', 'name'):
        articles_pks = articles_pks | ws.articles.filter(is_active=True).order_by('-created')[:ws.home_article_count]
    return self.get_queryset().filter(pk__in=articles_pks.values_list('pk',flat=True)))
 

В итоге у вас должно быть всего два запроса: один для получения веб-сайтов и один для создания и использования списка статей.