我正在使用django 1.9及其内置的JSONField和postgres 9.4.在我的模型的JSONField
json字段中,我使用一些值存储对象,包括数字.我需要聚合它们来找到最小/最大值.像这样的东西:
Model.objects.aggregate(min=Min('attrs__my_key'))
提取特定密钥也很有用:
Model.objects.values_list('attrs__my_key', flat=True)
上述查询失败attrs
:"无法将关键字'my_key'解析为字段.不允许加入'attrs'."
有可能吗?
注意:1)我知道如何进行简单的postgres查询来完成这项工作,所以我专门搜索ORM解决方案,以便能够过滤等.2)我想这可以用(相对)新的查询表达式/查找api完成但是我还没有研究过它.
从django 1.11(尚未出现,所以这可能会改变)你可以使用django.contrib.postgres.fields.jsonb.KeyTextTransform
而不是RawSQL
.
在Django 1.10,你必须复制/粘贴KeyTransform
到你自己KeyTextTransform
和更换->
与运营商->>
,并#>
与#>>
因此它返回文本,而不是JSON对象.
Model.objects.annotate( val=KeyTextTransform('json_field_key', 'blah__json_field')) ).aggregate(min=Min('val')
您甚至可以KeyTextTransform
在SearchVector
s中包含s以进行全文搜索
Model.objects.annotate( search=SearchVector( KeyTextTransform('jsonb_text_field_key', 'json_field')) ) ).filter(search='stuff I am searching for')
请记住,您也可以在jsonb字段中编制索引,因此您应该根据您的特定工作负载来考虑这一点.
对于那些感兴趣的人,我找到了解决方案(或至少解决方法).
from django.db.models.expressions import RawSQL Model.objects.annotate( val=RawSQL("((attrs->>%s)::numeric)", (json_field_key,)) ).aggregate(min=Min('val')
请注意,attrs->>%s
表达式将attrs->>'width'
在处理后变为smth (我的意思是单引号).因此,如果您对此名称进行硬编码,则应记住插入它们,否则您将收到错误消息.
///有点offtopic ///
还有一个棘手的问题与django本身无关,但需要以某种方式处理.和attrs
json字段一样,它的键和值没有限制,你可以(取决于你的应用程序逻辑)获得一些非数字值,例如width
key.在这种情况下,您将从DataError
执行上述查询后获得postgres.同时会忽略NULL值,所以没关系.如果你能抓住错误那么没问题,你很幸运.在我的情况下,我需要忽略错误的值,这里唯一的方法是编写自定义的postgres函数来抑制错误的输出错误.
create or replace function safe_cast_to_numeric(text) returns numeric as $$ begin return cast($1 as numeric); exception when invalid_text_representation then return null; end; $$ language plpgsql immutable;
然后使用它将文本转换为数字:
Model.objects.annotate( val=RawSQL("safe_cast_to_numeric(attrs->>%s)", (json_field_key,)) ).aggregate(min=Min('val')
因此,我们为像json这样的动态事物得到了非常可靠的解决方案.
我知道这有点晚了(几个月),但是我在尝试执行此操作时遇到了这个帖子。设法做到这一点:
1)使用KeyTextTransform将jsonb值转换为文本
2)使用Cast将其转换为整数,以便SUM起作用:
q = myModel.objects.filter(type=9) \ .annotate(numeric_val=Cast(KeyTextTransform(sum_field, 'data'), IntegerField())) \ .aggregate(Sum('numeric_val')) print(q)
其中“数据”是jsonb属性,“数字_val”是我通过注释创建的变量的名称。
希望这对某人有帮助!