26

当我使用带有 SQLite 数据库引擎的 Numeric 列时,SQLalchemy 给了我以下警告。

SAWarning:方言 sqlite+pysqlite 本身支持 Decimal 对象

我正在尝试找出pkgPrice = Column(Numeric(12,2))在仍然使用 SQLite 的同时使用 SQLalchemy 的最佳方法。

这个问题 [1]如何将 Python 十进制转换为 SQLite 数字?展示了一种使用sqlite3.register_adapter(D, adapt_decimal)SQLite 接收和返回小数,但存储字符串的方法,但我不知道如何深入 SQLAlchemy 核心来做到这一点。类型装饰器看起来是正确的方法,但我还没有理解它们。

有没有人有一个 SQLAlchemy 类型装饰器配方,它将在 SQLAlchemy 模型中包含数字或十进制数字,但将它们作为字符串存储在 SQLite 中?

4

3 回答 3

20

由于看起来您使用小数来表示货币价值,我建议您采取安全措施并将货币价值存储在最低面额中,例如 1610 美分而不是 16.10 美元。然后你可以只使用一个整数列类型。

它可能不是您期望的答案,但它解决了您的问题,并且通常被认为是理智的设计。

于 2012-04-29T09:49:30.833 回答
20
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
于 2012-04-30T16:15:16.570 回答
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 Niu 提到的,当十进制作为字符串存储在sqlite 中时,某些查询不会总是按预期运行,例如session.query(T).filter(T.value > 100) 或sqlalchemy.sql 之类的东西。 expression.func.min,甚至 order_by,因为 SQL 比较的是字符串(例如字符串中的“9.2”>“19.2”),而不是我们在这些情况下所期望的数值。

于 2018-09-26T22:28:21.383 回答