如何在Django中聚合JSONField里amount字段的总和?
Django JSONField 数值聚合问题
我定义了如下Django模型,包含simple_json和nested_json两个JSONField字段,分别存储含amount键的简单JSON和嵌套JSON:
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)
我想要计算这两个字段中amount键对应值的总和,尝试了两种查询方式:
尝试的查询方式
方式1:先注解再聚合
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'), )
方式2:直接聚合
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())), )
但两种方式都报错:
django.db.utils.DataError: cannot cast jsonb object to type integer
请问在Django中,正确聚合JSONField中数值总和的方法是什么?
版本信息
- Django 3.1.X
- Python 3.9.X
正确解法
报错原因是直接使用simple_json__amount这类点式访问在Cast中时,Django ORM没有正确解析为JSON字段的键提取,反而错误地将整个JSON对象尝试转换为整数,触发数据库报错。
在Django 3.1中,需要使用KeyTransform函数明确提取JSON字段中的指定键值,再进行类型转换和聚合:
from django.db import models from django.db.models.functions import KeyTransform # 计算两个JSON字段中amount的总和 result = Foo.objects.aggregate( # 提取simple_json中的amount,转整数后求和 simple_json_total=models.Sum( models.Cast(KeyTransform('amount', 'simple_json'), output_field=models.IntegerField()) ), # 逐层提取嵌套JSON中的amount,转整数后求和 nested_json_total=models.Sum( models.Cast( KeyTransform('amount', KeyTransform('l3', KeyTransform('l2', KeyTransform('l1', 'nested_json') ) ) ), output_field=models.IntegerField() ) ) )
补充说明
KeyTransform用于从JSONField中提取指定键对应的值,嵌套结构需要逐层调用,每层对应一个JSON层级的键。- JSON存储的数值在数据库中是jsonb类型,必须通过
Cast明确转换为IntegerField后,才能使用Sum进行聚合计算。 - 该方案适配PostgreSQL数据库(报错信息显示使用的是jsonb类型,对应PostgreSQL)。
内容的提问来源于stack exchange,提问作者JPG
相关产品推荐
相关产品推荐

