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

SqlAlchemy基于货币类型的金额精度适配与查询实现咨询

解决方案

核心思路是在SQL表达式层用CASE语句实现和Python层一致的货币小数位查找逻辑,再通过精确数值运算还原出实际金额,避免浮点精度丢失。

完整实现代码

首先导入所需的SQLAlchemy工具:

from sqlalchemy import case, cast, Numeric, func

补全hybrid_property的expression实现:

decimal_places: dict[str, int] = {
    "BHD": 3,
    # 后续新增多小数位货币直接在此处添加即可
}

class Table(declarative_base()):
    __tablename__ = "test"

    def __init__(self, **kwargs):
        amount = kwargs.pop("amount", None)
        super().__init__(**kwargs)
        if amount is not None:
            self.amount = amount

    id = sa.Column(sa.Integer, primary_key=True)
    _amount = sa.Column(sa.Integer, nullable=False, default=0)
    currency = sa.Column(sa.String(3), nullable=False, default="")

    @hybrid_property
    def amount(self):
        places = decimal_places.get(self.currency, 2)
        return Decimal(self._amount).scaleb(-places)

    @amount.setter
    def amount(self, value):
        if not isinstance(value, Decimal):
            value = Decimal(value)
        places = decimal_places.get(self.currency, 2)
        integer = value.scaleb(places)
        self._amount = int(integer)

    @amount.expression
    def amount(cls):
        # 构建和Python层逻辑一致的小数位查找CASE语句
        places = case(
            [(cls.currency == code, places) for code, places in decimal_places.items()],
            else_=2
        )
        # 取所有支持货币的最大小数位,避免数值截断
        max_supported_scale = max(decimal_places.values(), default=2)
        # 用Numeric类型做精确运算,避免浮点误差
        return cast(
            cls._amount / func.pow(cast(10, Numeric), places),
            Numeric(precision=19, scale=max_supported_scale)
        )

注意事项

  • 精度适配:Numeric的scale参数会自动适配你支持的最大小数位,后续新增如0位小数的日元、4位小数的突尼斯第纳尔等币种时,只需更新decimal_places字典即可。
  • 赋值顺序:因为金额赋值逻辑依赖当前记录的currency值,赋值时要保证currency先于amount设置,你现有__init__的传参逻辑已经符合该要求,直接使用Table(currency="BHD", amount=Decimal("12.345"))即可正确存储。
  • 数据库兼容:该写法是跨数据库兼容的,如果你使用PostgreSQL这类支持原生数值移位函数的数据库,可以把pow运算替换为对应的原生函数,进一步提升性能。

实现后可以直接对amount属性做查询、过滤、排序操作,比如session.query(Table).filter(Table.amount > Decimal("10.5"))会自动转换为符合业务逻辑的SQL语句执行。

内容的提问来源于stack exchange,提问作者mdomke

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 16:39:02