SQLAlchemy:在内部格式和数据库格式之间来回转换列值

SQLAlchemy: Convert column value back and forth between internal and database format

在我的数据库中,我有一些列以某种奇怪的格式存储数据。由于数据库也被其他代码使用,我无法更改数据格式。

例如,一种奇怪的格式是将时间值表示为字符串,如 23:42:30。我想要一些魔法让我总是在 python 侧使用 datetime.time 对象。

一个非常简单的解决方案如下:

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')

但是,这只是解决了读写数据的问题。这样的事情是不可能的:

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

另一种方法是使用 hybrid extension。那样的话,查询是可能的,但如果我看对了,就不会写了。此外,它需要将所有代码编写两次,一次在 python 代码中,一次在 SQL 代码中。 (附加问题:我不确定是否可以仅使用 SQL 代码编写转换。)

真的没有办法把两者结合起来吗?比如我定义了两个函数,比方说 python2sqlsql2python,SQLAlchemy 使用它来透明地将值从 python 对象转换为字符串,然后返回?我知道这会使某些查询变得不可能,例如 betweenlike 或求和等,但没关系。

请记住,时间只是我需要转换数据的例子之一;所以请尽量保持通用的回复。

使用 type decorator 处理与自定义格式之间的转换。在定义列时使用此类型而不是 String

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))