我应该如何处理小数中的SQLAlchemy和SQLite的(How should I handle

2019-06-24 00:30发布

SQLAlchemy的给我下面的警告,当我使用数字列和SQLite数据库引擎。

SAWarning:方言源码+ pysqlite 支持十进制对象本身

我试图找出拥有最好的办法pkgPrice = Column(Numeric(12,2))同时还使用SQLite在SQLAlchemy的。

这个问题[1] 如何Python的十进制转换成数字的SQLite? 显示方式使用sqlite3.register_adapter(D, adapt_decimal)有SQLite的接收和返回小数,但存储字符串,但我不知道如何挖掘到SQLAlchemy的核心要做到这一点呢。 类型装饰看起来像正确的做法,但我不神交他们没有。

是否有人在SQLAlchemy的装饰型配方,将有数字或十进制数在SQLAlchemy的模式,而是将它们存储在SQLite的字符串?

Answer 1:

from decimal import Decimal as D
import sqlalchemy.types as types

class SqliteNumeric(types.TypeDecorator):
    impl = types.String
    def load_dialect_impl(self, dialect):
        return dialect.type_descriptor(types.VARCHAR(100))
    def process_bind_param(self, value, dialect):
        return str(value)
    def process_result_value(self, value, dialect):
        return D(value)

# can overwrite the imported type name
# @note: the TypeDecorator does not guarantie the scale and precision.
# you can do this with separate checks
Numeric = SqliteNumeric
class T(Base):
    __tablename__ = 't'
    id = Column(Integer, primary_key=True, nullable=False, unique=True)
    value = Column(Numeric(12, 2), nullable=False)
    #value = Column(SqliteNumeric(12, 2), nullable=False)

    def __init__(self, value):
        self.value = value


Answer 2:

因为它看起来像您使用的货币值小数,我建议你做安全的事情和货币的值存储在它的最小单位,如1610美分,而不是16.10美元。 然后,你可以使用一个整数列类型。

它可能不是您想要的答案,但它解决您的问题,被普遍认为是稳健的设计。



Answer 3:

下面是两个@van和@JosefAssad灵感的解决方案。

class SqliteDecimal(TypeDecorator):
    # This TypeDecorator use Sqlalchemy Integer as impl. It converts Decimals
    # from Python to Integers which is later stored in Sqlite database.
    impl = Integer

    def __init__(self, scale):
        # It takes a 'scale' parameter, which specifies the number of digits
        # to the right of the decimal point of the number in the column.
        TypeDecorator.__init__(self)
        self.scale = scale
        self.multiplier_int = 10 ** self.scale

    def process_bind_param(self, value, dialect):
        # e.g. value = Column(SqliteDecimal(2)) means a value such as
        # Decimal('12.34') will be converted to 1234 in Sqlite
        if value is not None:
            value = int(Decimal(value) * self.multiplier_int)
        return value

    def process_result_value(self, value, dialect):
        # e.g. Integer 1234 in Sqlite will be converted to Decimal('12.34'),
        # when query takes place.
        if value is not None:
            value = Decimal(value) / self.multiplier_int
        return value

像@Jinghui钮提到的,当小数被存储为在源码字符串,某些查询不会总是正常操作,如session.query(T).filter(T.value> 100),或类似的东西sqlalchemy.sql。 expression.func.min,甚至ORDER_BY,因为SQL比较字符串(如“9.2”>中的字符串“19.2”),如我们预期在这些情况下,而不是数值。



文章来源: How should I handle decimal in SQLalchemy & SQLite