Django JSONField双下划线查询仅PostgreSQL有效,语法为field__key1__key2=value,需确保键存在且类型匹配;数组需用索引或__contains;须手动创建GIN索引提升性能。

JSONField 里怎么用双下划线查嵌套字段
Django 的 JSONField 支持用双下划线(__)语法直接查询 JSON 内部键值,但只对 PostgreSQL 有效,SQLite 和 MySQL 不支持这种写法——哪怕你用了 JSONField,在非 PostgreSQL 下会直接报错或静默忽略条件。
比如你有模型:
class Product(models.Model):
data = models.JSONField()
其中 data 是 {"specs": {"color": "red", "size": "M"}, "tags": ["sale", "new"]},想查 color 是 red 的记录,就得这样写:
Product.objects.filter(data__specs__color="red")
立即学习“Python免费学习笔记(深入)”;
注意:PostgreSQL 要求字段是 jsonb 类型(Django 默认建表就是 jsonb),且路径中的每一级键名都必须存在,否则匹配不到(不会自动跳过缺失层级)。
查数组里的对象要加索引或用 contains
如果 JSON 里有数组,比如 data = {"reviews": [{"user": "alice", "rating": 5}, {"user": "bob", "rating": 4}]},不能直接写 data__reviews__user="alice"——这会报 FieldError,因为 Django 不知道你想查数组哪个元素。
可行方案有两个:
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- 用整数索引定位,如
data__reviews__0__user="alice"(查第一个 review 的 user) - 用
__contains查子结构:data__reviews__contains={"user": "alice"},但注意这个要求整个字典完全匹配,不能只写{"user": "alice"}就匹配{"user": "alice", "rating": 5}(除非你用的是 PostgreSQL 12+ 并开启jsonb_path_exists风格查询,Django 原生不封装)
更灵活的做法是搭配 RawSQL 或 Func 自定义表达式,但日常建议优先用 __contains + 合理数据建模(比如把 reviews 拆成独立模型)。
空值、缺失键、类型不一致时的坑
PostgreSQL 的 jsonb 对 null 和缺失键区分严格:data__specs__color=None 匹配的是 "color": null,不是没这个 key;而 data__specs__color__isnull=True 才能同时捕获 "color": null 和 key 根本不存在的情况。
常见翻车点:
- 存字符串
"123"却用data__id=123查询 → 不匹配(类型不对) - 前端传了
"color": "",后端用__exact=""查,结果漏掉null或缺失情况 - 字段被更新为
null(PythonNone),但查询仍用__isnull=False→ 这个条件会排除掉null,但不会排除缺失 key 的记录(因为缺失 key 时,整个路径取值为 SQL 的NULL)
稳妥做法是显式处理:先用 __has_key 确认键存在,再查值;或统一用 Coalesce 函数兜底。
性能和索引必须手动加
双下划线查询不会自动走索引。PostgreSQL 要加速 data__specs__color 这类查询,得手动建索引:
CREATE INDEX CONCURRENTLY idx_product_data_color ON myapp_product USING GIN ((data -> 'specs' ->> 'color'));
Django 不提供迁移命令自动生成这类索引,得写 RunSQL 迁移。另外注意:-> 返回 json,->> 返回 text,查字符串值一般用后者;若查整数,需配合 ::int 类型转换,但索引无法跨类型生效,所以存数据时尽量统一类型。
没加索引时,深层嵌套查询可能全表扫描,尤其数据量过万后延迟明显——这点容易被忽略,等慢查询报警才想起补。

















