获取json字段中的数据

Posted by Shallow Dreameron March 4, 2025

如果你已经知道 JSON 字段中的 key 路径,并且需要使用 Django ORM 获取指定数据并自定义别名,可以使用 annotateF 来实现。


示例模型

假设你的 data 字段如下:

{
    "name": "Alice",
    "age": 25,
    "details": {
        "email": "alice@example.com",
        "phone": "123456789"
    }
}

对应的 Django 模型:

from django.db import models

class MyModel(models.Model):
    data = models.JSONField()

1. 获取 JSON 字段中的单个 key

如果你想获取 data 中的 name 并自定义别名:

from django.db.models import F

queryset = MyModel.objects.annotate(user_name=F("data__name")).values("user_name")
print(queryset)

等价 SQL:

SELECT data->>'name' AS user_name FROM mymodel;

2. 获取嵌套 JSON 数据

如果你想获取 details 里面的 email 并命名为 user_email

queryset = MyModel.objects.annotate(user_email=F("data__details__email")).values("user_email")
print(queryset)

等价 SQL:

SELECT data->'details'->>'email' AS user_email FROM mymodel;

3. 获取 JSON 数组中的某个 key

假设你的 data 结构如下:

{
    "contacts": [
        {"type": "email", "value": "alice@example.com"},
        {"type": "phone", "value": "123456789"}
    ]
}

如果你想获取 contacts 数组中 第一个元素的 value

queryset = MyModel.objects.annotate(first_contact=F("data__contacts__0__value")).values("first_contact")
print(queryset)

等价 SQL:

SELECT data->'contacts'->0->>'value' AS first_contact FROM mymodel;

总结

需求 Django ORM PostgreSQL SQL
获取 data.name F("data__name") data->>'name'
获取 data.details.email F("data__details__email") data->'details'->>'email'
获取 data.contacts[0].value F("data__contacts__0__value") data->'contacts'->0->>'value'

这样就能直接使用 ORM 获取 JSON 数据,并且可以自定义字段别名,无需额外使用 RawSQL

如果你需要获取 JSON 数组 contacts 中所有 value 值,可以使用 PostgreSQL 的 jsonb_array_elements,并结合 Django ORM 的 RawSQL 实现。


1. 使用 jsonb_path_query_array 获取所有 value

from django.db.models.expressions import RawSQL

queryset = MyModel.objects.annotate(
    all_values=RawSQL("jsonb_path_query_array(data, '$.contacts[*].value')", [])
).values("all_values")

print(queryset)

等价 SQL:

SELECT jsonb_path_query_array(data, '$.contacts[*].value') AS all_values FROM mymodel;

返回示例:

[{'all_values': ['alice@example.com', '123456789']}]

这会返回一个 JSON 数组,包含 contacts 数组中所有 value 值。


2. 使用 jsonb_array_elements 拆分数组(返回多个行)

如果你希望每个 value 作为单独的行返回:

queryset = MyModel.objects.raw("""
    SELECT id, jsonb_array_elements(data->'contacts')->>'value' AS value
    FROM mymodel
""")

for row in queryset:
    print(row.value)

等价 SQL:

SELECT jsonb_array_elements(data->'contacts')->>'value' AS value FROM mymodel;

返回示例(每个 value 一行):

alice@example.com
123456789

3. 使用 ArrayAgg 聚合

如果你希望仍然使用 ORM 而不使用 RawSQL

from django.contrib.postgres.aggregates import ArrayAgg

queryset = MyModel.objects.annotate(
    all_values=ArrayAgg("data__contacts__value")
).values("all_values")

print(queryset)

等价 SQL:

SELECT array_agg(data->'contacts'->>'value') FROM mymodel;

返回示例:

[{'all_values': ['alice@example.com', '123456789']}]

总结

需求 Django ORM SQL 语句
获取所有 value(数组) RawSQL("jsonb_path_query_array(data, '$.contacts[*].value')") jsonb_path_query_array(data, '$.contacts[*].value')
拆分数组,每个 value 一行 RawSQL("SELECT jsonb_array_elements(data->'contacts')->>'value'") jsonb_array_elements(data->'contacts')->>'value'
使用 ArrayAgg 聚合 ArrayAgg("data__contacts__value") array_agg(data->'contacts'->>'value')

如果你希望所有值返回在一个 JSON 数组里,推荐 方法 1jsonb_path_query_array)。如果你希望每个 value 作为一行返回,推荐 方法 2jsonb_array_elements)。如果你想用纯 ORM 实现,推荐 方法 3ArrayAgg)。