使用Django ORM查询含JSON字段的表报TypeError如何解决
问题原因
- 你之前用来更新JSON字段的脚本存在明显逻辑错误:第一行对
jsonstring做反斜杠替换得到的new_clear,被第二行直接用原始jsonstring替换NaN的结果覆盖,反斜杠清理逻辑完全没有生效。 - 不要全局删除JSON字符串中的所有反斜杠,会破坏JSON中合法的转义字符(比如转义引号、转义特殊字符),导致JSON格式损坏。
- 报错本质是
json.loads()方法接收到了dict类型的参数:大概率是你使用的PostgreSQL驱动(psycopg2)会自动将JSON/JSONB类型的字段值解析为Python dict,但Django的JSONField会默认对数据库返回的值再做一次反序列化,两次反序列化冲突导致报错;也有可能是部分记录存在双重序列化的问题(手动dumps后赋值给JSONField,导致JSONField又做了一次序列化)。
修复步骤
1. 临时获取目标记录的方法
如果你需要先快速拿到id=301的记录,可以用原生SQL强制将JSON字段转成字符串返回,避免自动解析冲突:
import json from .models import House # 只查询你需要的字段,result强制转text类型 test_object = House.objects.raw("SELECT id, result::text FROM house WHERE id = 301")[0] # 手动反序列化result result_data = json.loads(test_object.result)
2. 修复字段解析冲突
如果是psycopg2自动解析JSONB和Django JSONField反序列化逻辑冲突,在数据库配置中新增配置关闭psycopg2的自动解析即可:
# settings.py DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': '你的库名', 'USER': '你的用户名', 'PASSWORD': '你的密码', 'HOST': '你的地址', 'PORT': '你的端口', 'OPTIONS': { # 禁止psycopg2自动解析jsonb为dict 'jsonb_loads': lambda s: s, }, } }
3. 批量修复已损坏的存量数据
import json from django.db import connection, transaction from .models import House @transaction.atomic def fix_json_data(): with connection.cursor() as cursor: # 先获取所有记录的原始值 cursor.execute("SELECT id, result FROM house") for obj_id, raw_result in cursor.fetchall(): try: if isinstance(raw_result, str): # 是字符串说明是未正确解析的JSON,转成dict后更新 parsed_val = json.loads(raw_result) House.objects.filter(id=obj_id).update(result=parsed_val) elif isinstance(raw_result, dict): # 已经是dict的直接更新,触发Django JSONField的正确序列化逻辑 House.objects.filter(id=obj_id).update(result=raw_result) except json.JSONDecodeError: print(f"记录ID {obj_id} 的JSON格式异常,需手动处理,原始值:{raw_result}") # 执行修复 fix_json_data()
4. 修正之前的JSON格式化脚本
import json from .models import House data = House.objects.all() # 按需修改查询范围 for i in data: # 如果i.result本身是dict直接处理即可,不需要先dumps if isinstance(i.result, dict): # 处理NaN的问题,不需要全局删反斜杠 jsonstring = json.dumps(i.result) new_clear = jsonstring.replace("NaN", "null") # 只处理双重转义的反斜杠,不要全删 new_clear = new_clear.replace("\\\\", "\\") i.result = json.loads(new_clear) else: # 如果是字符串直接loads try: parsed = json.loads(i.result.replace("NaN", "null")) i.result = parsed except json.JSONDecodeError: print(f"记录ID {i.id} 格式错误跳过") i.save()
内容的提问来源于stack exchange,提问作者Alex Cortes
相关产品推荐
相关产品推荐

