Как выполнить агрегационный запрос в 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'ом после выполнения запроса?


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