Как выполнить агрегационный запрос в Django?
Поставлена такая задача:
В коллекции пользователей 'Account' лежат документы вида:
{ 'number': '7800000000000', 'name': 'Пользователь №', 'sessions': [ { 'created_at': ISODate('2016-01-01T00:00:00'), 'session_id': '6QBnQhFGgDgC2FDfGwbgEaLbPMMBofPFVrVh9Pn2quooAcgxZc', 'actions': [ { 'type': 'read', 'created_at': ISODate('2016-01-01T01:20:01'), }, { 'type': 'read', 'created_at': ISODate('2016-01-01T01:21:13'), }, { 'type': 'create', 'created_at': ISODate('2016-01-01T01:33:59'), } ], } ] }Необходимо написать агрегационный запрос, который по каждому пользователю выведет последнее действие и общее количество для каждого из типов 'actions'. Итоговые данные должны представлять собой список документов вида:
{ 'number': '7800000000000', 'actions': [ { 'type': 'create', 'last': 'created_at': ISODate('2016-01-01T01:33:59'), 'count': 12, }, { 'type': 'read', 'last': 'created_at': ISODate('2016-01-01T01:21:13'), 'count': 12, }, { 'type': 'update', 'last': null, 'count': 0, }, { 'type': 'delete', 'last': null, 'count': 0, }, ] }
Написал три таких модели:
class Account(Model):
number = AutoField(primary_key=True, verbose_name='Номер')
name = CharField(max_length=20, verbose_name='Имя')
class Session(Model):
user = ForeignKey(Account, on_delete=CASCADE, related_name='sessions', verbose_name='Пользователь')
session_id = CharField(max_length=session_id_max_length, blank=True, editable=False, unique=True, verbose_name='ID сессии')
created_at = DateTimeField(auto_now_add=True, verbose_name='Дата создания')
class Action(Model):
class Type(TextChoices):
CREATE = 'create', 'Создание'
READ = 'read', 'Чтение'
UPDATE = 'update', 'Изменение'
DELETE = 'delete', 'Удаление'
session = ForeignKey(Session, on_delete=CASCADE, related_name='actions', verbose_name='Сессия')
type = CharField(max_length=6, choices=Type.choices, verbose_name='Тип')
created_at = DateTimeField(auto_now_add=True, verbose_name='Дата создания')
Как можно видеть, таблицы связаны между собой по цепочке через FK, Account <- Session <- Action, то есть из модели Account нельзя получить объекты модели Action напрямую. Все, чего я смог добиться на данный момент - это запрос вида
Action.objects.values('session__user__number', 'type').annotate(last=Max('created_at'), count=Count('type'))
Он возвращает QuerySet вида
<QuerySet [
{'session__user__number': 1, 'type': 'update', 'last': datetime.datetime(2021, 6, 16, 16, 56, 55, 370580, tzinfo=<UTC>), 'count': 1},
{'session__user__number': 2, 'type': 'read', 'last': datetime.datetime(2021, 6, 16, 17, 23, 17, 215523, tzinfo=<UTC>), 'count': 1},
{'session__user__number': 2, 'type': 'update', 'last': datetime.datetime(2021, 6, 16, 16, 56, 11, 916884, tzinfo=<UTC>), 'count': 2}
]>
что достаточно близко, но нет группировки по пользователям ('session__user__number': 2 дублируется два раза). Есть ли какой-то способ получить результат, максимально близкий к образцу, без дополнительных манипуляций с QuerySet'ом после выполнения запроса?