Django+psycopg2全局序列化UUID至JSONField失败求助
问题根源
Django的JSONField在ORM写入操作时,会先通过自身的JSON编码器序列化数据,再将结果传给psycopg2。这意味着你注册的psycopg2自定义适配器根本没机会处理UUID对象——Django在序列化阶段就会抛出TypeError。
解决方法
以下是三种可行方案,按适配场景排序:
方案一:自定义Django JSONField(兼顾ORM与原生查询,最推荐)
创建继承自models.JSONField的自定义字段,重写序列化逻辑处理UUID,同时保留你之前的psycopg2适配器支持原生查询。
1. 定义自定义字段与编码器
# 可放在app的fields.py或models.py中 import json from uuid import UUID from django.db import models from django.core.serializers.json import DjangoJSONEncoder class UUIDAwareJSONEncoder(DjangoJSONEncoder): def default(self, obj): if isinstance(obj, UUID): return str(obj) # 可扩展添加其他需要序列化的类型(如datetime、Decimal等) return super().default(obj) class CustomJSONField(models.JSONField): def pre_save(self, model_instance, add): value = super().pre_save(model_instance, add) if value is not None: # 用自定义编码器完成序列化,绕过Django默认逻辑 return json.dumps(value, cls=UUIDAwareJSONEncoder) return value def from_db_value(self, value, expression, connection): # 从数据库读取时反序列化 if value is None: return value try: return json.loads(value) except (TypeError, ValueError): return value
2. 在模型中使用自定义字段
class SomeModel(models.Model): data = CustomJSONField()
3. 保留psycopg2适配器(支持原生查询)
在项目初始化文件(如settings.py)中注册你之前的自定义适配器:
import psycopg2 from psycopg2.extras import Json import json from uuid import UUID class CustomJson(Json): def dumps(self, obj): def default(o): if isinstance(o, UUID): return str(o) raise TypeError(f"Object of type {o.__class__.__name__} is not JSON serializable") return json.dumps(obj, default=default) psycopg2.extensions.register_adapter(dict, CustomJson)
这样ORM写入时由自定义字段处理序列化,原生查询由psycopg2适配器处理,两边都能正常工作。
方案二:在Pydantic Schema层面提前转换UUID
如果数据全部来自Django Ninja/Pydantic Schema,可以直接在Schema中将UUID转为字符串,避免传到Django时出现UUID对象。
from pydantic import BaseModel, field_serializer from uuid import UUID class NestedDataSchema(BaseModel): item_id: UUID # 其他字段... @field_serializer('item_id') def serialize_uuid(self, value: UUID) -> str: return str(value) class MainSchema(BaseModel): nested_data: NestedDataSchema # 其他字段...
调用schema.dict()时,UUID已经被转为字符串,直接传给Django ORM不会报错,同时原生查询的psycopg2适配器依然生效。
方案三:全局替换Django的JSON编码器
修改Django全局JSON编码器,让所有JSONField默认支持UUID序列化。在settings.py中添加:
import json from uuid import UUID from django.core.serializers.json import DjangoJSONEncoder class UUIDAwareJSONEncoder(DjangoJSONEncoder): def default(self, obj): if isinstance(obj, UUID): return str(obj) return super().default(obj) # 替换全局JSON编码器 JSON_ENCODER = 'your_project.settings.UUIDAwareJSONEncoder'
注意:此方法会影响所有使用Django默认JSONEncoder的场景(如admin后台、序列化器等),适合需要全局生效的场景,否则优先选择方案一。
内容的提问来源于stack exchange,提问作者nschagen
相关产品推荐
相关产品推荐

