我正在使用django 1.9及其内置的JSONField和postgres 9.4.在我的模型的JSONFieldjson字段中,我使用一些值存储对象,包括数字.我需要聚合它们来找到最小/最大值.像这样的东西:
Model.objects.aggregate(min=Min('attrs__my_key'))
Run Code Online (Sandbox Code Playgroud)
提取特定密钥也很有用:
Model.objects.values_list('attrs__my_key', flat=True)
Run Code Online (Sandbox Code Playgroud)
上述查询失败attrs:"无法将关键字'my_key'解析为字段.不允许加入'attrs'."
有可能吗?
注意:1)我知道如何进行简单的postgres查询来完成这项工作,所以我专门搜索ORM解决方案,以便能够过滤等.2)我想这可以用(相对)新的查询表达式/查找api完成但是我还没有研究过它.
din*_*ltz 33
从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')
Run Code Online (Sandbox Code Playgroud)
您甚至可以KeyTextTransform在SearchVectors中包含s以进行全文搜索
Model.objects.annotate(
search=SearchVector(
KeyTextTransform('jsonb_text_field_key', 'json_field'))
)
).filter(search='stuff I am searching for')
Run Code Online (Sandbox Code Playgroud)
请记住,您也可以在jsonb字段中编制索引,因此您应该根据您的特定工作负载来考虑这一点.
alT*_*Tus 18
对于那些感兴趣的人,我找到了解决方案(或至少解决方法).
from django.db.models.expressions import RawSQL
Model.objects.annotate(
val=RawSQL("((attrs->>%s)::numeric)", (json_field_key,))
).aggregate(min=Min('val')
Run Code Online (Sandbox Code Playgroud)
请注意,attrs->>%s表达式将attrs->>'width'在处理后变为smth (我的意思是单引号).因此,如果您对此名称进行硬编码,则应记住插入它们,否则您将收到错误消息.
///有点offtopic ///
还有一个棘手的问题与django本身无关,但需要以某种方式处理.和attrsjson字段一样,它的键和值没有限制,你可以(取决于你的应用程序逻辑)获得一些非数字值,例如widthkey.在这种情况下,您将从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;
Run Code Online (Sandbox Code Playgroud)
然后使用它将文本转换为数字:
Model.objects.annotate(
val=RawSQL("safe_cast_to_numeric(attrs->>%s)", (json_field_key,))
).aggregate(min=Min('val')
Run Code Online (Sandbox Code Playgroud)
因此,我们为像json这样的动态事物得到了非常可靠的解决方案.
小智 8
我知道这有点晚了(几个月),但是我在尝试执行此操作时遇到了这个帖子。设法做到这一点:
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)
Run Code Online (Sandbox Code Playgroud)
其中“数据”是jsonb属性,“数字_val”是我通过注释创建的变量的名称。
希望这对某人有帮助!
可以使用 Postgres 函数来做到这一点
https://www.postgresql.org/docs/9.5/functions-json.html
from django.db.models import Func, F, FloatField
from django.db.models.expressions import Value
from django.db.models.functions import Cast
text = Func(F(json_field), Value(json_key), function='jsonb_extract_path_text')
floatfield = Cast(text, FloatField())
Model.objects.aggregate(min=Min(floatfield))
Run Code Online (Sandbox Code Playgroud)
这比使用 好得多,RawQuery因为如果您执行更复杂的查询,它不会中断,其中 Django 使用别名并且存在字段名称冲突。ORM 有很多事情可以用手写的实现来咬你。
| 归档时间: |
|
| 查看次数: |
5965 次 |
| 最近记录: |