26

SQLiteデータベースエンジンでNumeric列を使用すると、SQLalchemyから次の警告が表示されます。

SA警告:Dialect sqlite+pysqliteはDecimalオブジェクトをネイティブでサポートしていませ

pkgPrice = Column(Numeric(12,2))SQLiteを使用しながらSQLalchemyを使用するための最良の方法を見つけようとしています。

この質問[1] Pythonの10進数をSQLiteの数値に変換する方法は?は、SQLiteがDecimalを受信して​​返すために使用する方法を示していますsqlite3.register_adapter(D, adapt_decimal)が、文字列を格納していますが、SQLAlchemyコアを掘り下げてこれを行う方法はまだわかりません。タイプデコレータは正しいアプローチのように見えますが、私はまだそれらを理解していません。

SQLAlchemyモデルに数値または10進数を含むSQLAlchemyタイプデコレータレシピを持っている人はいますが、それらをSQLiteに文字列として保存しますか?

4

3 に答える 3

20

通貨の値に小数を使用しているように見えるので、安全なことを行い、通貨の値を最低額、たとえば16.10ドルではなく1610セントで保管することをお勧めします。次に、整数列型を使用できます。

それはあなたが期待していた答えではないかもしれませんが、それはあなたの問題を解決し、一般的には正気のデザインと考えられています。

于 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が述べたように、10進数が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 に答える