如何使用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
相关产品推荐
相关产品推荐

