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

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

Похожие вопросы
Strikt Москва
от 100 000 до 180 000 ₽
ITK academy Саратов
от 75 000 ₽
Sim-Ba Pay Санкт-Петербург
от 180 000 ₽