Python2.7操作PostgreSQL时Decimal类型数据被截断问题咨询
精度丢失的根本原因
- PostgreSQL驱动默认类型转换问题:Python 2.7环境下操作PostgreSQL常用的psycopg2驱动,默认会将数据库的
numeric类型字段值自动转换为Python原生float类型。float是双精度浮点数,仅能保存约15~17位有效数字,你存储的高精度小数位数远超这个范围,转换时就会发生精度截断。 - 隐式类型转换问题:代码中赋值时的兜底默认值为
1.0(float类型),即便前面查询到Decimal类型的值,也会因为和float混合使用被隐式转换为float,进一步加重精度丢失。 - Python 2 float输出截断问题:Python 2中float类型转字符串输出时,会默认截断小数位,导致你看到的导出值比实际float存储的位数还要短。
修复方案
- 修改psycopg2的类型映射规则,指定
numeric类型返回Decimal而非float,在执行查询前添加类型注册代码即可。 - 将兜底默认值替换为
Decimal('1.0'),避免隐式类型转换。 - 后续需要输出值时,直接操作Decimal类型做格式化,不要转成float后再处理。
修改后代码参考:
from decimal import Decimal import psycopg2.extensions def _compute_current_rate(self): date = self._context.get('date') or fields.Datetime.now() company_id = self._context.get('company_id') or self.env['res.users']._get_company().id # 注册numeric类型映射为Decimal psycopg2.extensions.register_type(psycopg2.extensions.DECIMAL, self._cr.connection) query = """SELECT c.id, (SELECT r.rate FROM res_currency_rate r WHERE r.currency_id = c.id AND r.name <= %s AND (r.company_id IS NULL OR r.company_id = %s) ORDER BY r.company_id, r.name DESC LIMIT 1) AS rate FROM res_currency c WHERE c.id IN %s""" self._cr.execute(query, (date, company_id, tuple(self.ids))) currency_rates = dict(self._cr.fetchall()) for currency in self: # 兜底值改为Decimal类型 currency.rate = currency_rates.get(currency.id) or Decimal('1.0')
内容的提问来源于stack exchange,提问作者Kyoungseo Park
相关产品推荐
相关产品推荐

