Получить количество строк одной модели, ссылающихся на объект другой модели в Django
Существует две модели: Articles и Views - Статьи и просмотры статей соответственно.
В модели Views есть поле article, ссылающееся через models.ForeignKey на объект модели Article. Мне необходимо получить список всех статей, отсортированный по количеству просмотров.
Пытался делать так:
Articles.objects.filter(status="published").order_by("views.count"):
но views.count не определяется. Ещё нужно сделать тоже самое, только для просмотров за последнюю неделю, то есть удовлетворяющих фильтру
view_datetime__gte=(datetime.datetime.now() + datetime.timedelta(days=-7)).strftime("%Y-%m-%d %H:%M:%S")
Как вариант решения я придумал создать двумерный список, в котором будут перебираться все статьи, отсортированные по чему угодно, каждой из которых сопоставляется её количество просмотров за всё время и за неделю, вроде такого:
[
[Article0, 78, 13]
[Article1, 96, 7]
[Article2, 48, 21]
[Article3, 456, 122]
]
Откуда отсортировать статьи по количеству просмотров проблем не составит, но есть стойкое ощущение, что возможна более простая реализация.
Ответы (1 шт):
Спасибо @Jack_oS за подсказку с агрегацией.
Для получения статей, отсортированных по количеству просмотров за всё время мы достаточно просто можем использовать обычный метод Count внутри annotate:
from django.db.models import Count
...
poparts = Articles.objects.annotate(views_num=Count('views__id')).order_by('-views_num')
В моём случае у каждого просмотра есть свой вес (это нужно для ручного добавления или убавления просмотров администраторами), поэтому код будет следующим:
from django.db.models import Sum
...
poparts = Articles.objects.annotate(views_num=Sum('views__view_weight')).order_by('-views_num')
Для сортировки по просмотрам, полученным исключительно за последнюю неделю, мы можем применить фильтр прямо к QuerySet'у объектов модели Articles, и получить вот такую вот штуку:
poparts = Articles.objects.filter(
views__view_datetime__gte=(datetime.datetime.now() + datetime.timedelta(days=-7)).strftime("%Y-%m-%d %H:%M:%S")
).annotate(views_num=Sum('views__view_weight')).order_by('-views_num')