@HELLSP4RK

Как выполнить агрегационный запрос в 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'ом после выполнения запроса?
  • Вопрос задан
  • 82 просмотра
Пригласить эксперта
Ответы на вопрос 1
@deliro
Агрессивное программирование
Нет группировки по number, потому что есть группировка по number, type.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы