SQLModel 使用 Decimal 精确处理金额与财务数据:从 Field 配置到数据库存储的完整实践 ORM数据库后端【免费下载链接】sqlmodelSQL databases in Python, designed for simplicity, compatibility, and robustness.项目地址https://gitcode.com/gh_mirrors/sq/sqlmodel点击查看免费下载本指南以 SQLModel 官方文档 Decimal Numbers 为核心系统讲解如何在 SQLModel 模型中使用 Python 标准库decimal.Decimal类型来存储货币、价格、账户余额等对精度有严格要求的财务数据。读完本文你将掌握Field()中max_digits与decimal_places参数的精确配置规则、合法的数值范围判断方法以及 SQLModel 底层如何将Decimal映射为 SQLAlchemy 的DECIMAL/Numeric数据库列类型。为什么财务数据需要 Decimal在二进制计算机中浮点数float的存储方式决定了它无法精确表达所有十进制小数。以最常见的例子来说在 Python 中执行1.1 2.2直觉上应该得到3.3实际却得到 1.1 2.2 3.3000000000000003这是因为1.1和2.2在二进制中都是无限循环小数只能近似存储累加后误差便暴露出来。Python 提供了decimal标准库模块和Decimal类型来解决这类问题它可以以十进制的形式严格保存数值从而保证运算结果的确定性。数据库在底层同样以二进制存储数据因此也存在相同的问题这也正是主流数据库都提供专用decimal类型的原因。对多数场景如统计视频播放量、游戏角色血条来说浮点误差通常无关紧要但对于货币、价格、账户余额这类涉及金钱与财务计算的业务四舍五入误差是绝对不能接受的此时就必须使用 Decimal。SQLModel 中 Decimal 的类型基础Pydantic 对Decimal类型有专门支持。当你在 SQLModel 模型字段上使用Decimal时可以通过Field()函数指定该数值允许的总位数digits和小数位数decimal placesmax_digits数值允许的最大总位数同时包含整数部分和小数部分decimal_places小数点右侧允许的小数位数。这两个参数一方面由 Pydantic 在校验阶段例如配合 FastAPI 做请求体校验时强制执行另一方面会被 SQLModel 原样传递给数据库列定义让数据库层面的精度约束与模型层面的校验保持一致。从源码看SQLModel 的Field()签名完整接收max_digits与decimal_places两个参数见 sqlmodel/main.py并在构造字段信息时将其原样转发给底层 Pydantic 元数据见 sqlmodel/main.py。在内部兼容层中SQLModel 通过FakeMetadata类持有这两个属性供后续类型映射读取见 sqlmodel/_compat.py。底层数据库类型映射SQLModel 在将模型字段转换为数据库列时会通过get_sqlalchemy_type()函数完成 Python 类型到 SQLAlchemy 类型的映射。当字段类型是Decimal时映射逻辑如下见 sqlmodel/main.pyif issubclass(type_, Decimal): return Numeric( precisiongetattr(metadata, max_digits, None), scalegetattr(metadata, decimal_places, None), )也就是说SQLModel 使用 SQLAlchemy 的DECIMAL类型即Numeric来表示 Decimal 字段并将max_digits映射为 SQL 层面的precision精度将decimal_places映射为scale小数位。这意味着你在 Python 模型中声明的精度约束会直接体现在数据库表结构中形成端到端的精度保证。在模型中使用 Decimal 字段假设数据库中的每个 hero英雄都有一笔钱我们可以将该字段声明为Decimal并用Field()参数配置最大位数与小数位。完整示例代码如下见 docs_src/advanced/decimal/tutorial001_py310.pyfrom decimal import Decimal from sqlmodel import Field, Session, SQLModel, create_engine, select class Hero(SQLModel, tableTrue): id: int | None Field(defaultNone, primary_keyTrue) name: str Field(indexTrue) secret_name: str age: int | None Field(defaultNone, indexTrue) money: Decimal Field(default0, max_digits5, decimal_places3) sqlite_file_name database.db sqlite_url fsqlite:///{sqlite_file_name} engine create_engine(sqlite_url, echoTrue) def create_db_and_tables(): SQLModel.metadata.create_all(engine) def create_heroes(): hero_1 Hero(nameDeadpond, secret_nameDive Wilson, money1.1) hero_2 Hero(nameSpider-Boy, secret_namePedro Parqueador, money0.001) hero_3 Hero(nameRusty-Man, secret_nameTommy Sharp, age48, money2.2) with Session(engine) as session: session.add(hero_1) session.add(hero_2) session.add(hero_3) session.commit()其中money: Decimal Field(default0, max_digits5, decimal_places3)的含义是max_digits5money字段最多允许5 位数字这 5 位同时包括整数部分小数点左侧和小数部分小数点右侧decimal_places3小数点右侧最多3 位小数。因此该字段的整数部分最多只能是5 - 3 2位即数值范围为0到99.999之间考虑符号则为-99.999到99.999。合法的数值示例✅ 以下数值对money字段都是合法的12.345—— 5 位数字其中整数 2 位、小数 3 位12.3—— 不足 3 位小数时按 3 位小数存储即12.30012—— 无小数部分整数 2 位1.2—— 整数 1 位、小数 1 位0.123—— 整数 0 位、小数 3 位0—— 全零值。非法的数值示例 以下数值对money字段都是非法的1.2345—— 小数位数超过 3 位4 位小数123.234—— 总位数超过 5 位整数部分 3 位 小数部分 3 位123—— 虽然没有任何小数位但字段仍为小数保留了 3 位因此整数部分只能使用max_digits - decimal_places 2位而123有 3 位整数数字超出限制。提示请务必根据自己应用的实际业务需求调整位数和小数位配置。例如货币场景通常需要decimal_places2分而科学计算或高精度统计可能需要更多小数位。创建带 Decimal 字段的模型数据创建模型实例时你完全可以直接传入普通的float数字Pydantic 会自动将其转换为Decimal类型SQLModel 再通过 SQLAlchemy 以Decimal类型存入数据库。例如上面的示例代码中hero_1 Hero(nameDeadpond, secret_nameDive Wilson, money1.1) hero_2 Hero(nameSpider-Boy, secret_namePedro Parqueador, money0.001) hero_3 Hero(nameRusty-Man, secret_nameTommy Sharp, age48, money2.2)这里传入的1.1、0.001、2.2都是 Pythonfloat经过 Pydantic 校验后被规范化为Decimal(1.100)、Decimal(0.001)、Decimal(2.200)存储。注意1.1被自动补零为1.100以满足decimal_places3的精度约定。查询 Decimal 数据并验证精度写入数据之后再读取 Decimal 字段并参与运算即可验证它确实规避了浮点数的舍入误差def select_heroes(): with Session(engine) as session: statement select(Hero).where(Hero.name Deadpond) results session.exec(statement) hero_1 results.one() print(Hero 1:, hero_1) statement select(Hero).where(Hero.name Rusty-Man) results session.exec(statement) hero_2 results.one() print(Hero 2:, hero_2) total_money hero_1.money hero_2.money print(fTotal money: {total_money}) def main(): create_db_and_tables() create_heroes() select_heroes() if __name__ __main__: main()注意这里的hero_1.money hero_2.money是两个Decimal对象直接相加其结果依然是Decimal因此不会产生浮点累加误差。运行程序示例中使用了 uv 作为包管理器若你的环境不同可替换为python app.py$ uv run python app.py // 部分样板输出已省略 // The type of money is Decimal(1.100) Hero 1: id1 secret_nameDive Wilson ageNone nameDeadpond moneyDecimal(1.100) // 更多输出已省略 // The type of money is Decimal(1.100) Hero 2: id3 secret_nameTommy Sharp age48 nameRusty-Man moneyDecimal(2.200) // 没有舍入误差就是 3.3 Total money: 3.300可以看到最终输出是精确的3.300而不是浮点运算时出现的3.3000000000000003。这正是 Decimal 类型在财务计算中的核心价值。测试用例佐证仓库中的测试文件 tests/test_advanced/test_decimal/test_tutorial001.py 对上述行为做了完整验证。测试用内存型 SQLite 引擎sqlite://替换示例中的文件数据库然后运行示例的main()并断言Hero 1的money等于Decimal(1.100)Hero 2的money等于Decimal(2.200)两笔钱相加的结果打印为Total money: 3.300。这从自动化测试层面确认了从 Python 类型转换、数据库存取到算术运算Decimal 的精度在整个链路上都得到了保持。重要警告SQLite 不支持 Decimal虽然 Decimal 类型在 Python 侧得到完整支持但并非所有数据库都支持 Decimal 类型。需要特别注意的是SQLite 不支持 Decimal。在 SQLite 中Decimal 会被转换为它支持的浮点型NUMERIC类型这意味着精度保证在 SQLite 下无法落实。这一点在示例代码中也能得到印证——示例默认使用sqlite:///database.db作为数据库引擎因此实际的精度行为取决于底层数据库。好消息是绝大多数其他 SQL 数据库如 PostgreSQL、MySQL 等都原生支持 Decimal 类型在这些数据库上SQLModel 会以真正的DECIMAL(precision, scale)列来存储精度约束由数据库引擎强制执行。因此如果你的应用涉及金额等财务数据建议在生产环境选择支持 Decimal 的数据库并在部署前用真实数据库验证精度行为。实战要点总结何时使用 Decimal涉及货币、价格、账户余额、税率等对舍入误差敏感的财务场景一律使用Decimal普通计数、度量等场景使用float即可。字段声明方式money: Decimal Field(default0, max_digits5, decimal_places3)其中max_digits包含整数与小数全部位数整数部分上限为max_digits - decimal_places。写入无需手动转换创建模型时可以传floatPydantic 自动转换为Decimal并补足小数位。运算保持精确Decimal之间的加、减、乘、除不会引入浮点舍入误差适合直接用于财务计算。底层映射SQLModel 将Decimal映射为 SQLAlchemy 的Numeric/DECIMAL类型max_digits对应precision、decimal_places对应scale见 sqlmodel/main.py。数据库选型SQLite 会把 Decimal 降级为浮点NUMERIC精度无法保证生产环境请选用支持 Decimal 的 SQL 数据库。自动化验证可以参考 tests/test_advanced/test_decimal/test_tutorial001.py 的写法为财务字段编写精度断言测试防止回归。赞分享ORM数据库后端【免费下载链接】sqlmodelSQL databases in Python, designed for simplicity, compatibility, and robustness.项目地址https://gitcode.com/gh_mirrors/sq/sqlmodel点击查看免费下载相关推荐数据处理与存储从文件到数据库的完整流程数据处理与存储从文件到数据库的完整流程 本文全面探讨了数据处理与存储的完整技术流程涵盖了从序列号生成算法、MySQL关系型数据库操作、Redis非关系型数据SQLModel事务处理最佳实践确保数据一致性的完整方案SQLModel事务处理最佳实践确保数据一致性的完整方案 SQLModel作为Python中强大的ORM工具提供了完善的事务处理机制来保证数据库操作的数据一ORM数据库后端OpenPAI 数据管理完全指南从存储配置到任务使用OpenPAI 数据管理完全指南从存储配置到任务使用 前言 在OpenPAI深度学习平台中高效的数据管理是机器学习工作流的关键环节。本文将全面介绍如何在Op上一篇Android 12精确位置权限革命AndPermission新特性深度解析下一篇k-skill 的 lotto-results 技能与 k-lotto 包韩国 로또 6/45 开奖结果查询与号码大对照实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考