SQLAlchemy hybrid_property 和表达式

dnf*_*ren 5 python sqlalchemy

我正在使用 sqlalchemy 将外部进程生成的一些数据存储在 postgres 数据库中。外部数据有几个存储为字符串的日期,我希望将这些日期用作日期时间对象进行比较和持续时间计算,并且我希望在数据模型中进行转换以保持一致性。我正在尝试使用 hybrid_property 但我遇到了基于 SQLAlchemy 使用 hybrid_property 作为实例或类的不同方式的问题。

一个(简化的)案例看起来像这样......

class Contact(Base):
    id = Column(String(100), primary_key=True)
    status_date = Column(String(100))

    @hybrid_property
    def real_status_date(self):
        return convert_from_outside_date(self.status_date)
Run Code Online (Sandbox Code Playgroud)

使用类似这样的转换函数(该函数可以返回一个日期,转换失败时返回 False 或传递 None 时返回 None)...

def convert_from_outside_date(in_str):
    out_date = None

    if in_str != None:
        try:
            out_date = datetime.datetime.strptime(in_str,"%Y-%m-%d")
        except ValueError:
            out_date = False
    return out_date
Run Code Online (Sandbox Code Playgroud)

当我使用 Contact 实例时,contact.real_status_date 作为日期时间正常工作。问题是在查询过滤器中使用 Contact.real_status_date 时。

db_session.query(Contact).filter(
    Contact.real_status_date > datetime.datetime.now())
Run Code Online (Sandbox Code Playgroud)

给我一个“TypeError:此子句的布尔值未定义”异常,带有

in_str != None
Run Code Online (Sandbox Code Playgroud)

转换函数的行作为堆栈跟踪的最后一部分。

一些答案 ( /sf/answers/1015328681/ ) 显示了 setter 函数的使用和数据模型中新列的添加。其他答案 ( /sf/answers/954989591/ ) 显示添加了 @property.expression 函数,该函数返回 sqlalchemy 可以解释为 sql 表达式的内容。

向 Contact 类添加一个 setter 是可行的,但添加新列似乎没有必要,这会使稍后解析某些表元数据变得更加困难,如果可以的话,我想避免它。

_real_status_date = Column(DateTime())

@hybrid_property
def real_status_date(self):
    return self._real_status_date

@real_status_date.setter
def value(self):
    self._real_status_date = convert_from_outside_date(self.status_date)
Run Code Online (Sandbox Code Playgroud)

如果我使用 @.expression 装饰器,我是否必须实现一个更兼容 sql 的 strptime 函数?那会是什么样子?在这里引起麻烦的转换函数有什么问题吗?

Efr*_*ren 0

正如zzzeek提到的,您可以将以下内容添加到您的班级中

根据您的数据库,它可能已经解释了 pythondatetime对象

因此,只需将转换函数修改为:

def convert_from_outside_date(in_str):
if in_str:
    try:
        return datetime.datetime.strptime(in_str,"%Y-%m-%d")
# Return None for a Null date
return None
Run Code Online (Sandbox Code Playgroud)

否则你需要添加一个表达式函数:

@real_status_date.expression
def real_status_date(self):
    return sqlalchemy.Date(self.real_status_date)
Run Code Online (Sandbox Code Playgroud)