Django - Агрегировать в массив

Я использую SQLite и имею модель django со следующими данными:

Name Value
A 1
B 2
B 4
C 7
C 5

Я хотел бы использовать агрегацию моей таблицы, чтобы я мог получить данные в следующем формате:

Name Value
A 1
B [2,4]
C [7,5]

Как это сделать с помощью Django.

Я думаю, что это может быть ответом, но Django, похоже, не имеет ArrayAgg. Это похоже на функцию PostgreSQL

Test_Model.objects.annotate(dname=ArrayAgg('name')).values()

Знаете ли вы, как я могу достичь этого без использования PostgreSQL? Спасибо!

Для PostgreSQL в Django есть ArrayAgg функция [Django-doc]:

from django.contrib.postgres.aggregates import ArrayAgg

Test_Model.objects.values('name').annotate(dname=ArrayAgg('value')).order_by('name')

Вы можете использовать groupby из itertools, затем создать агрегат для любых баз данных:

>>> from itertools import groupby
>>> [{'Name': key, 'Value': list(item.Value for item in grp)} for key, grp in
    groupby(Test_Model.objects.order_by('Name'), key=lambda x: x.Name)] 
[{'Name': 'A', 'Value': [1]},
 {'Name': 'B', 'Value': [2, 4]},
 {'Name': 'C', 'Value': [7, 5]}]

Сначала создайте свой собственный агрегат следующим образом

from django.db.models import Aggregate

class GroupConcat(Aggregate):
    function = 'GROUP_CONCAT'
    template = '%(function)s(%(distinct)s%(expressions)s)'

    def __init__(self, expression, distinct=False, **extra):
        super(GroupConcat, self).__init__(
            expression,
            distinct='DISTINCT ' if distinct else '',
            output_field=CharField(),
            **extra)

После создания этого используйте следующий запрос

Test_Model.objects.values('name').annotate(dname=GroupConcat('value')).order_by('name')

Основываясь на @deepak-tripathi 'answer, их решение возвращает строку. Чтобы вернуть список, используйте агрегатную функцию SQLite JSON_GROUP_ARRAY и Django JSONField:

from django.db.models import JSONField
from django.db.models.aggregates import Aggregate

class JsonGroupArray(Aggregate):
    function = 'JSON_GROUP_ARRAY'
    output_field = JSONField()
    template = '%(function)s(%(distinct)s%(expressions)s)'

(На самом деле переопределенный __init__() не нужен, если только вам не нужна функциональность DISTINCT, в этом случае можно просто использовать allow_distinct=True)

А затем, как говорит @deepak-tripathi, использовать его:

Test_Model.objects.values('name').annotate(
    dname=JsonGroupArray('value', filter(value__isnull=False)),
).order_by('name')

(фильтр isnull служит для того, чтобы избежать [None] в агрегированных результатах)

Вернуться на верх