SQLAlchemy: Convierte el valor de la columna de ida y vuelta entre el formato interno y el de la base de datos

En mi base de datos, tengo algunas columnas donde los datos se almacenan en algún formato extraño. Como la base de datos también es utilizada por otro código, no puedo cambiar el formato de los datos.

Por ejemplo, uno de los formatos raros es que un valor de tiempo se representa como una cadena como 23:42:30 . Me gustaría tener algo de magia que me permita usar siempre los objetos datetime.time en el lado de Python.

Una solución muy simple sería algo como:

 col_raw = Column('col', String(7)) @property def col(self): return datetime.strptime(self.col_raw, '%H:%M:%S').time() @col.setter def colself, t): self.col_raw = t.strftime('%H:%M:%S') 

Sin embargo, esto solo resuelve el problema para leer y escribir datos. Cosas como esta no serían posibles:

 Table.query.filter(Table.col == time(23,42,30)) 

Otra forma sería utilizar la extensión híbrida . De esa manera, la consulta sería posible, pero si la veo correctamente, la escritura no. Además, requiere escribir la encoding de todo dos veces, una vez en el código Python y una vez en el código SQL. (Problema adicional: no estoy seguro de que la conversión se pueda escribir utilizando solo el código SQL).

¿Realmente no hay forma de que combine ambos? Tal como defino dos funciones, digamos python2sql y sql2python , que SQLAlchemy usa para convertir de forma transparente los valores de objeto python a cadena y la parte posterior. Sé que esto haría que algunas consultas sean imposibles, como between o like o sums y similares, pero eso está bien.

Por favor, tenga en cuenta que el momento es solo uno de los casos en los que necesito convertir datos; así que por favor trata de mantener las respuestas genéricas.

Use un decorador de tipo que maneje la conversión hacia y desde el formato personalizado. Utilice este tipo en lugar de String al definir su columna.

 class MyTime(TypeDecorator): impl = String def __init__(self, length=None, format='%H:%M:%S', **kwargs) super().__init__(length, **kwargs) self.format = format def process_literal_param(self, value, dialect): # allow passing string or time to column if isinstance(value, basestring): # use str instead on py3 value = datetime.strptime(value, self.format).time() # convert python time to sql string return value.strftime(self.format) if value is not None else None process_bind_param = process_literal_param def process_result_value(self, value, dialect): # convert sql string to python time return datetime.strptime(value, self.format).time() if value is not None else None # in your model class MyModel(Base): time = Column(MyTime(length=7))