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.