Consider I have a simple model setup with JSONField
from django.db import models
import random
def simple_json_callable():
return {"amount": random.randint(1, 100)}
def nested_json_callable():
data = {
"l1": {
"l2": {
"l3": {
"amount": random.randint(1, 100)
}
}
}
}
return data
class Foo(models.Model):
simple_json = models.JSONField(default=simple_json_callable)
nested_json = models.JSONField(default=nested_json_callable)
I want to get the sum of amount key from both simple_json and nested_json fields.
I tried the following queries
Case 1: Annotate and then aggregate
result = Foo.objects.annotate(
simple_json_amount=Cast('simple_json__amount', output_field=models.IntegerField()),
nested_json_amount=Cast('nested_json__l1__l2__l3__amount', output_field=models.IntegerField()),
).aggregate(
simple_json_total=models.Sum('simple_json_amount'),
nested_json_total=models.Sum('nested_json_amount'),
)
Case 2: Aggregate
result = Foo.objects.aggregate(
simple_json_total=models.Sum(Cast('simple_json__amount', output_field=models.IntegerField())),
nested_json_total=models.Sum(Cast('nested_json__l1__l2__l3__amount', output_field=models.IntegerField())),
)
In both cases, I got the error
django.db.utils.DataError: cannot cast jsonb object to type integer
Question
What is the proper way to aggregate sum of values from a JSONField in Django?
Version
- Django 3.1.X
- Python 3.9.X
For
Django==3.2.Xand newer versionsYou can use the "double-underscore" to span the relationship and can get the aggregated result (as mentioned in the OP)
For
Django==3.1.Xand older versionsYou can use the
KeyTextTransform(...)to extract the keys and aggregate the values.If you are using
PostgreSQLand don't want to use the "nested usage ofKeyTextTransform", you can create a custom Django DB function equivalent to thejsonb_extract_path_text(...)and it can be used as
References
JSONField