You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何通过Django JSONField更新PostgreSQL JSON字段中对象数组内指定对象的特定值

解决Django JSONField中嵌套数组元素的更新问题

你遇到的是PostgreSQL JSONB嵌套数组中特定元素的更新问题,JSON_SET(或jsonb_set)确实无法直接定位数组里的元素,需要结合数组拆解、索引匹配或者重新构建数组的方式来实现更新。下面针对你的需求给出两种可行方案:

方案一:更新数组中第一个匹配的元素

如果你的user数组中只有一个名为"Devang"的条目,或者只需要更新第一个匹配的元素,可以通过定位元素索引的方式实现:

PostgreSQL原生SQL

UPDATE user_table
SET json_field = jsonb_set(
    json_field,
    -- 构建JSON路径:user数组 -> 目标元素索引 -> user_name字段
    ARRAY['user', (SELECT idx - 1 FROM jsonb_array_elements(json_field->'user') WITH ORDINALITY arr(elem, idx) WHERE elem->>'user_name' = 'Devang')::int, 'user_name'],
    '"Dev"' -- JSON字符串需要用双引号包裹
)
WHERE json_field @> '{"user": [{"user_name": "Devang"}]}';
  • 解释:
    • jsonb_array_elements(json_field->'user') WITH ORDINALITY:将user数组拆分为单个元素,并带上1-based的索引idx
    • idx - 1:转换为JSON路径使用的0-based索引
    • jsonb_set:通过构建的路径精准更新目标字段

Django中使用该逻辑

利用RawSQL结合Django的查询API实现:

from django.db.models import RawSQL
from yourapp.models import UserTable

# 更新第一个匹配"Devang"的条目
UserTable.objects.filter(
    json_field__contains={"user": [{"user_name": "Devang"}]}
).update(
    json_field=RawSQL(
        """
        jsonb_set(
            json_field,
            ARRAY['user', (SELECT idx - 1 FROM jsonb_array_elements(json_field->'user') WITH ORDINALITY arr(elem, idx) WHERE elem->>'user_name' = %s)::int, 'user_name'],
            %s
        )
        """,
        ('Devang', '"Dev"',)
    )
)

方案二:更新数组中所有匹配的元素

如果user数组中可能存在多个名为"Devang"的条目,需要全部更新,可以通过重新构建数组的方式实现:

PostgreSQL原生SQL

UPDATE user_table
SET json_field = jsonb_set(
    json_field,
    '{user}', -- 指定要替换的顶层数组路径
    -- 遍历数组元素,匹配到目标条目则更新,否则保留原元素
    (
        SELECT json_agg(
            CASE 
                WHEN elem->>'user_name' = 'Devang' THEN jsonb_set(elem, '{user_name}', '"Dev"')
                ELSE elem 
            END
        )
        FROM jsonb_array_elements(json_field->'user') elem
    )
)
WHERE json_field @> '{"user": [{"user_name": "Devang"}]}';
  • 解释:
    • json_agg:将遍历处理后的元素重新组合为数组
    • CASE语句:逐个判断元素是否匹配目标,实现精准更新

Django中使用该逻辑

from django.db.models import RawSQL
from yourapp.models import UserTable

# 更新所有匹配"Devang"的条目
UserTable.objects.filter(
    json_field__contains={"user": [{"user_name": "Devang"}]}
).update(
    json_field=RawSQL(
        """
        jsonb_set(
            json_field,
            '{user}',
            (
                SELECT json_agg(
                    CASE 
                        WHEN elem->>'user_name' = %s THEN jsonb_set(elem, '{user_name}', %s)
                        ELSE elem 
                    END
                )
                FROM jsonb_array_elements(json_field->'user') elem
            )
        )
        """,
        ('Devang', '"Dev"',)
    )
)

注意事项

  • 确保你的Django模型中json_field使用的是JSONField(PostgreSQL下默认映射为jsonb类型,支持高效的JSON操作)
  • 如果你的字段是JSONField而非JSONBField,只需将上述代码中的jsonb_set替换为JSON_SETjsonb_array_elements替换为json_array_elements即可

内容的提问来源于stack exchange,提问作者l.b.vasoya

火山引擎 最新活动