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]
в агрегированных результатах)