You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从PostgreSQL提取Django模型JSONField的唯一(foodid, fieldname)对?

解决Django中JSONField嵌套结构的唯一(foodid, fieldname)对查询问题

问题背景

我们有一个包含JSONField字段food的Django模型,对应PostgreSQL表里存了约1000条数据,JSON结构固定为food->foodid->'field'->fieldname->{....}(其中foodid和fieldname是动态变化的值)。现在需要从数据库中提取所有唯一的(foodid, fieldname)对。

之前尝试的代码如下:

Model.objects.annotate(
 foodid=Func(
 F('food'),
 function='jsonb_object_keys',
 output_field=models.CharField()
 )
 ).annotate(
 field_name=Func(
 Value('food__') + F('foodid') + Value('__field'), 
 function='jsonb_object_keys',
 output_field=models.CharField()
 )
 ).values_list('food_id', 'field_names')

这段代码里第一个annotate能正常提取出所有foodid,但第二个annotate想用生成的foodid去获取对应的fieldname时,不管是拼接字符串路径还是用F('foodid__field'),都会让PostgreSQL误以为要去表中查找名为foodid的字段,根本实现不了需求。

解决方案

要实现这个需求,得利用PostgreSQL的JSONB函数逐层解析嵌套结构:先展开food的顶层键(也就是foodid),再根据每个foodid定位到对应的field对象,最后提取该对象的所有键(fieldname),最后去重即可。

完整代码实现

from django.db.models import Func, F, Value
from django.db.models.fields import CharField

unique_pairs = Model.objects.annotate(
    # 第一步:提取food字段的所有顶层键,即foodid
    foodid=Func(
        F('food'),
        function='jsonb_object_keys',
        output_field=CharField()
    )
).annotate(
    # 第二步:根据foodid定位到food->foodid->field,再提取该对象的所有键(fieldname)
    fieldname=Func(
        Func(
            F('food'),
            F('foodid'),
            Value('field'),
            function='jsonb_extract_path',
            output_field=CharField()
        ),
        function='jsonb_object_keys',
        output_field=CharField()
    )
).values_list('foodid', 'fieldname').distinct()  # 去重获取唯一对

为什么之前的代码不行?

之前尝试拼接字符串路径的思路完全错误:Django的F表达式在annotate中生成的是数据库层面的字段别名,不能直接当作JSON路径的一部分拼接。而jsonb_object_keys需要接收一个实际的JSONB对象作为参数,不是字符串格式的路径。用jsonb_extract_path可以动态根据foodid的值定位到嵌套的field对象,再提取其键值就没问题了。

内容的提问来源于stack exchange,提问作者Салават Абдудин

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 06:55:05