SQLModel 中的 Decimal 类型:精确处理金融数据的最佳实践
为什么需要 Decimal 类型
在编程中处理金融数据时,浮点数的精度问题常常让人头疼。例如在 Python 中执行 1.1 + 2.2
时,期望得到 3.3,实际却得到 3.3000000000000003。这种精度问题源于计算机使用二进制存储浮点数的本质。
对于以下场景,这种精度损失是不可接受的:
- 金融交易系统
- 会计软件
- 电子商务平台的价格计算
- 任何需要精确计算的货币操作
SQLModel 中的 Decimal 解决方案
SQLModel 基于 Pydantic 和 SQLAlchemy,提供了完善的 Decimal 类型支持,可以完美解决上述精度问题。
基本用法
在 SQLModel 模型中定义 Decimal 字段:
from decimal import Decimal
from sqlmodel import SQLModel, Field
from sqlmodel.sql.sqltypes import AutoString
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
money: Decimal = Field(max_digits=5, decimal_places=3)
这里我们定义了一个 money
字段,参数说明:
max_digits=5
:总位数(整数部分+小数部分)decimal_places=3
:小数位数
有效值示例
根据上述定义,以下数值是有效的:
- 12.345
- 12.3
- 12
- 1.2
- 0.123
- 0
无效值示例
以下数值将引发验证错误:
- 1.2345(小数位数超过3位)
- 123.234(总位数超过5位)
- 123(整数部分超过2位)
实际应用示例
创建包含 Decimal 的记录
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson", money=1.1)
hero_2 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48, money=2.2)
虽然我们传入的是浮点数,但 Pydantic 会自动将其转换为 Decimal 类型。
查询和计算
session.add(hero_1)
session.add(hero_2)
session.commit()
heroes = session.exec(select(Hero)).all()
total = sum(hero.money for hero in heroes)
print(f"Total money: {total}")
输出结果将是精确的 3.300,而不是浮点数计算常见的 3.3000000000000003。
数据库支持说明
需要注意的是,虽然 SQLModel 在 Python 层面完全支持 Decimal 类型,但底层数据库的支持情况有所不同:
- PostgreSQL、MySQL 等主流数据库原生支持 Decimal 类型
- SQLite 会将 Decimal 转换为它支持的 NUMERIC 类型(本质仍是浮点数)
对于生产环境的金融应用,建议使用 PostgreSQL 等专业数据库系统。
最佳实践建议
-
根据业务需求合理设置精度:
- 货币交易:通常需要 2-4 位小数
- 科学计算:可能需要更高精度
-
避免在应用层进行浮点数计算,始终使用 Decimal 类型
-
在数据库迁移脚本中明确指定 Decimal 字段的精度
-
对于金融系统,考虑使用专门的货币处理库(如 python-money)
通过合理使用 SQLModel 的 Decimal 支持,开发者可以轻松构建出精确可靠的金融应用系统,避免因浮点数精度问题导致的业务逻辑错误。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考