You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 15:15:10