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

如何使用Flask-SQLAlchemy将SQLAlchemy JSON反序列化为Decimal

解决Flask-SQLAlchemy从PostgreSQL JSON字段反序列化Decimal的问题

嘿,我之前也碰到过一模一样的坑!序列化Decimal到JSON字段没问题,但读出来全变成float了,精度直接炸锅。给你几个亲测有效的解决方案:

方案一:直接用simplejson带参数反序列化

既然你已经用simplejson处理了序列化,那反序列化直接沿用它的use_decimal=True参数就好,只需要在自定义的apply_driver_hacks里加上反序列化的配置:

首先导入依赖:

import simplejson
from flask_sqlalchemy import SQLAlchemy

然后替换默认的驱动钩子:

db = SQLAlchemy()

def custom_driver_hacks(db, app, connection, **kwargs):
    # 你的原有序列化代码(已经正常工作的部分)
    connection.driver_options['json_serializer'] = lambda x: simplejson.dumps(x, use_decimal=True)
    # 新增反序列化配置,指定use_decimal=True
    connection.driver_options['json_deserializer'] = lambda x: simplejson.loads(x, use_decimal=True)

# 把自定义钩子绑定到db实例
db.apply_driver_hacks = custom_driver_hacks

这样一来,从JSON字段读取数据时,simplejson会自动把数字转回Decimal类型,完美保留精度。

方案二:自定义反序列化钩子(不用simplejson)

如果不想依赖simplejson,也可以用Python标准库的json模块,自己写一个object_hook来把float转换成Decimal:

import json
from decimal import Decimal
from flask_sqlalchemy import SQLAlchemy

def decimal_converter(obj):
    # 遍历所有值,把float转成Decimal
    if isinstance(obj, float):
        return Decimal(str(obj))
    # 处理嵌套的字典和列表
    elif isinstance(obj, dict):
        return {k: decimal_converter(v) for k, v in obj.items()}
    elif isinstance(obj, list):
        return [decimal_converter(i) for i in obj]
    # 其他类型直接返回
    else:
        return obj

db = SQLAlchemy()

def custom_driver_hacks(db, app, connection, **kwargs):
    connection.driver_options['json_serializer'] = lambda x: json.dumps(x, default=lambda o: str(o) if isinstance(o, Decimal) else o)
    connection.driver_options['json_deserializer'] = lambda x: json.loads(x, object_hook=decimal_converter)

db.apply_driver_hacks = custom_driver_hacks

这里要注意:序列化时把Decimal转成字符串存(避免标准json转成float),反序列化时再把字符串/浮点数转回Decimal,用字符串转Decimal更安全,不会有精度损失。

方案三:自定义SQLAlchemy JSON类型(最规范)

如果希望更贴合SQLAlchemy的扩展方式,推荐自定义一个支持Decimal的JSON字段类型,这样可以针对特定字段生效,更灵活:

from sqlalchemy.types import JSON
from sqlalchemy.ext.mutable import MutableDict
import simplejson
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class DecimalJSON(JSON):
    def bind_processor(self, dialect):
        # 序列化逻辑:把对象转成JSON字符串
        def process(value):
            if value is None:
                return None
            return simplejson.dumps(value, use_decimal=True)
        return process

    def result_processor(self, dialect, coltype):
        # 反序列化逻辑:把JSON字符串转回带Decimal的对象
        def process(value):
            if value is None:
                return None
            return simplejson.loads(value, use_decimal=True)
        return process

# 在模型里使用这个自定义类型(用MutableDict支持自动更新)
class YourModel(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    decimal_data = db.Column(MutableDict.as_mutable(DecimalJSON))

这个方案的好处是不用全局修改驱动钩子,只给需要的字段用这个类型,代码结构更清晰,也更符合SQLAlchemy的最佳实践。

试试上面的方法,应该就能把JSON字段里的数字重新转成Decimal啦!

内容的提问来源于stack exchange,提问作者W. Enzo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 06:43:47