Tortoise ORM中DecimalField触发decimal.InvalidOperation错误排查
### 报错信息
(.venv) n1qro@n1qro-Latitude-5480:~/Рабочий стол/TortoiseORM$ "/home/n1qro/Рабочий стол/TortoiseORM/.venv/bin/python" "/home/n1qro/Рабочий стол/TortoiseORM/main.py"
Traceback (most recent call last):
File "/home/n1qro/Рабочий стол/TortoiseORM/main.py", line 19, in
asyncio.run(main())
File "/usr/local/lib/python3.10/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/usr/local/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
return future.result()
File "/home/n1qro/Рабочий стол/TortoiseORM/main.py", line 15, in main
await test()
File "/home/n1qro/Рабочий стол/TortoiseORM/main.py", line 7, in test
user = await User.create(decimal=1234567890.3)
File "/home/n1qro/Рабочий стол/TortoiseORM/.venv/lib/python3.10/site-packages/tortoise/models.py", line 1140, in create
instance = cls(**kwargs)
File "/home/n1qro/Рабочий стол/TortoiseORM/.venv/lib/python3.10/site-packages/tortoise/models.py", line 660, in init
for key in meta.fields.difference(self._set_kwargs(kwargs)):
File "/home/n1qro/Рабочий стол/TortoiseORM/.venv/lib/python3.10/site-packages/tortoise/models.py", line 696, in _set_kwargs
setattr(self, key, field_object.to_python_value(value))
File "/home/n1qro/Рабочий стол/TortoiseORM/.venv/lib/python3.10/site-packages/tortoise/fields/data.py", line 288, in to_python_value
value = Decimal(value).quantize(self.quant).normalize()
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]
我推测Tortoise ORM底层用Decimal库管理该字段,但不太了解这个库。按文档说只需指定小数位数和最大位数就能用,请问哪里操作错了? --- # 解决方法 ### 错误根源 1. **字段参数设置矛盾**:`max_digits=27`指的是数值的总位数(整数位+小数位),你设置`decimal_places=27`意味着小数位占满所有位数,但传入的数值整数部分已有10位,总位数达到37,超出了`max_digits`的限制。 2. **浮点数精度问题**:直接传入`1234567890.3`这个浮点数,其底层存储的是近似值,转成Decimal时会生成远超27位的小数位,导致`quantize`操作失败,触发`InvalidOperation`。 ### 具体修正方案 - **调整字段参数**:根据实际业务需求设置合理的位数。比如要存储整数位10位、小数位1位,应设置: ```python decimal = DecimalField(max_digits=11, decimal_places=1)
- 避免传入浮点数:优先使用字符串或Decimal对象传入数值,确保精度:
# 方式1:传入字符串 async def test(): user = await User.create(decimal="1234567890.3") ... # 方式2:直接传入Decimal对象 from decimal import Decimal async def test(): user = await User.create(decimal=Decimal("1234567890.3")) ...
内容的提问来源于stack exchange,提问作者Andrew

