Saltar al contenido

¿Cómo debo manejar el decimal en SQLalchemy y SQLite?

La guía o código que hallarás en este post es la solución más fácil y efectiva que hallamos a tus dudas o problema.

Solución:

Dado que parece que está utilizando decimales para valores de moneda, le sugiero que haga lo seguro y almacene el valor de la moneda en su denominación más baja, por ejemplo, 1610 centavos en lugar de 16,10 dólares. Entonces puede usar un tipo de columna Integer.

Puede que no sea la respuesta que esperaba, pero resuelve su problema y generalmente se considera un diseño sensato.

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

Aquí hay una solución inspirada tanto en @van como en @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

Como mencionó @Jinghui Niu, cuando el decimal se almacena como cadenas en sqlite, algunas consultas no siempre funcionarán como se espera, como session.query(T).filter(T.value > 100), o cosas como sqlalchemy.sql. expression.func.min, o incluso order_by, porque SQL compara cadenas (por ejemplo, “9.2” > “19.2” en cadenas) en lugar de valores numéricos como esperábamos en estos casos.

Te mostramos las reseñas y valoraciones de los lectores

Agradecemos que desees añadir valor a nuestra información asistiendo con tu veteranía en las críticas.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *