Как аннотировать сумму над данными Django JSONField (массив объектов)?

У меня есть модели, подобные этой

# models.py
class MyModel( models.Model ):
    orders = models.JsonField(null= True, blank=True, default=list)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)

Я сохранил данные в формате json в этой структуре.

[
    {
        "order_name": "first order",
        "price": 200
    },
    {
        "order_name": "second order",
        "price": 800
    },
    {
        "order_name": "third order",
        "price": 100
    }
]

Я хочу суммировать цену всех json объектов т.е. 200+800+100

Я не работал с JSONArrayField, но я провел небольшое исследование и обнаружил, что следующий пример может дать вам подсказку:

MyModel.objects.annotate(
    order_price_sum=Sum(
        Cast(
            KeyTextTransform("price", "orders"), models.FloatField()
        )
    ),
)

Я попытался реализовать его для вашего конкретного вопроса, вы можете найти больше полезной информации в следующей ссылке: https://dev.to/saschalalala/aggregation-in-django-jsonfields-4kg5

Workaround: Я пытался выяснить, как управлять JSONArray с помощью annotate в django, но, похоже, это не очень хорошо документировано, поэтому я делюсь этим обходным решением для достижения цели:

total = 0
for i in MyModel.objects.values('orders'):
    total += sum([j.get('price',0) for j in i.get('orders') if j is not None])

Одним из способов будет использование jsonb_array_elements для разбиения каждого значения на строки, а затем использование обычной агрегатной функции.

Например:

from django.db import models


Model.objects.annotate(
    # This will break items into multiple rows
    annotate_field_1=models.Func(models.F('array_field__items'), function='jsonb_array_elements'),
).aggregate(
    # Then calculate the total.
    total=models.Count('annotate_field_1'),
)['total']
Вернуться на верх