【问题标题】:使用 SQLAlchemy 编写浮点数时如何限制小数位数?
【发布时间】:2022-01-23 16:53:42
【问题描述】:

我在 Python 中使用 SQLAlchemy 作为 ORM 和底层 Sybase DB。我想限制将从 Python 编写的浮点数中的小数位数(比例)。 Sybase 表中的基础列也是 FLOAT 类型。所以即使输入浮点数是0.12345678,我也希望能够只将0.1234 写入表中。我无法更改 Sybase 模式以使用具有已定义精度和比例的 DECIMAL,它必须保持 FLOAT。这只能在 ORM 级别处理。

到目前为止,我已经尝试对 FLOAT 列进行如下建模,但它们似乎都不起作用;即,如果我尝试使用session.add({'my_floats': 0.12345678}) 写入表,它会以0.12345678 而不是0.12340.1235 的形式保存到数据库中。

# mapping Sybase my_floats to NUMERIC in ORM
my_floats = Column(sqlalchemy.dialects.sybase.NUMERIC(precision=32, scale=4))
# using SQLAlchemy's internal coercion from FLOAT to DECIMAL
my_floats = Column(sqlalchemy.dialects.sybase.FLOAT(precision=32, scale=4, asdecimal=True, decimal_return_scale=4))

【问题讨论】:

  • float 数据类型以机器/cpu 依赖格式存储;虽然 一些 您的插入可能会完全按照您插入它们的方式存储,但这不能保证 float 数据类型的行为;如果您无法更改列的数据类型(例如,更改为 numeric(x,y)decimal(x,y)),或者可能添加一个新的 numeric(x,y)/decimal(x,y) 列,那么您将“卡住”当前的行为......最好的你可以做的是希望你能写一个select,将float值重新格式化为你最初尝试插入的格式

标签: python sqlalchemy floating-point


【解决方案1】:

好问题。似乎只适用于select,但不适用于insertupdate

import decimal
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, Column, Integer
from sqlalchemy.dialects.sybase import FLOAT
from sqlalchemy.orm import sessionmaker

engine = create_engine('blablabla...', echo=True)
Base = declarative_base(engine)
Session = sessionmaker(bind=engine)
session = Session()


class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    money = Column(FLOAT(precision=32, asdecimal=True, decimal_return_scale=4))


Base.metadata.create_all()
user = User(money=0.99312321)
print('type before insert %s' % type(user.money))
session.add(user)
session.commit()

for u in session.query(User):
    print(u.money)
    print('type after select %s' % type(u.money))

让我们看看日志:

# float but not decimal.Decimal...
type before insert <class 'float'>

# VALUES (%(money)s) but not something like VALUES (ROUND(%(money)s, 4))...
[2021-12-22 16:38:20,572] {log.py:110} INFO - INSERT INTO users (money) VALUES (%(money)s) RETURNING users.id

# not rounded value....
2021-12-22 16:38:20,572 INFO sqlalchemy.engine.base.Engine {'money': 0.99312321}

# but rounded after select and not float:
0.9931
type after select <class 'decimal.Decimal'>

如果您只想保存四舍五入的值,您似乎需要手动处理(仅作为示例):

def __init__(self, **kwargs) -> None:
    super().__init__(**kwargs)
    if kwargs.get('money'):
        self.money = round(decimal.Decimal(self.money), 4)

或者你可以使用before_insert / before_update events

【讨论】:

    猜你喜欢
    • 2021-04-23
    • 2023-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多