简体   繁体   中英

How to limit the number of decimal places while writing floats using SQLAlchemy?

I am using SQLAlchemy as ORM in Python with underlying Sybase DB. I want to limit the number of decimal places (scale) in floating-point numbers which will be written from Python. The underlying column in the Sybase table is also of type FLOAT. So even if the input float is 0.12345678 , I want to be able to write only 0.1234 to the table. I can't change the Sybase schema to use DECIMAL with defined precision and scale, that has to remain FLOAT. This has to be handled at the ORM level only.

So far, I have tried to model the FLOAT column as below, both of them don't seem to work; ie, if I try to write to the table using session.add({'my_floats': 0.12345678}) , it gets persisted to the DB as 0.12345678 and not 0.1234 or 0.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))

Nice question. Seems to only work on select , but not on insert or update :

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))

Let's see the logs:

# 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'>

Looks like you need to process manually if you want to save only rounded values(just an example):

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

Or you can use before_insert / before_update events

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM