SQLAlchemy,如何将混合表达式设置为“理解”日期时间?

Posted

技术标签:

【中文标题】SQLAlchemy,如何将混合表达式设置为“理解”日期时间?【英文标题】:SQLAlchemy, how to set hybrid expression to 'understand' datetime? 【发布时间】:2022-01-15 04:39:05 【问题描述】:

我正在尝试为 Person 的声明性映射类设置 SQLAlchemy 混合属性,该类具有一个名为 birth_dateDateTime 字段,它表示此人的出生日期。

我想设置一个 @hybrid_property 来代表这个人的年龄,如下所示:

class Person(Base):
    __tablename__ = 'person'
    name: str = Column(String)
    date_of_birth: DateTime = Column(DateTime)

    #works fine after the objects are loaded
    @hybrid_property
    def age(self):
        today = date.today()
        if self.date_of_birth:
            return today.year - self.date_of_birth.year - (
                    (today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day))
    
    @age.expression
    def age(cls):   #Don't know how to set this up
        pass

我在为混合属性设置 expression 时遇到问题。据我了解,该表达式应返回一个 SQL 语句,该语句将有助于过滤/查询数据库中的 preson 年龄。

为此,以下 SQL 起作用

SELECT (strftime('%Y', 'now') - strftime('%Y', person.date_of_birth)) - (strftime('%m-%d', 'now') < strftime('%m-%d', person.date_of_birth)) as age from person

但我不知道如何“告诉”表达式使用此 SQL(或者即使它是正确的方法。)我尝试像这样使用 text:

@age.expression
def age(cls):
    current_time = datetime.utcnow()   
    return text(f"current_time.year - strftime('%Y', 'cls.date_of_birth')")

但它没有用。我不知道如何告诉表达式使用 SQL 语句作为虚拟列的选择。 (那是年龄列)

目标是能够像这样过滤和查询age 属性:

session.query(Person).filter(Person.age > 25)

请提供帮助。

【问题讨论】:

【参考方案1】:

混合属性的那部分需要返回一个可执行的 SQLAlchemy 子句。而且由于 Postgres 已经为此提供了合适的功能,您可以直接使用它:

import sqlalchemy as sa

@age.expression
def age(cls):
    return sa.func.age(cls.date_of_birth)

函数:docs,查找age(timestamp)

或者在mysql:

@age.expression
def age(cls):
    return sa.func.timestampdiff('year', cls.date_of_birth, sa.func.curdate())

或者在 SQLite 中:

@age.expression
def age(cls):
  strftime = sa.func.strftime

  year_diff = strftime('%Y', 'now') - strftime('%Y', cls.date_of_birth)
  # If the month and date of the current year are before the
  # month and date of birth, then we need to subtract one year
  # because the "birthday" hasn't happened yet.
  is_partial_year = strftime('%m-%d', 'now') < strftime('%m-%d', cls.date_of_birth)

  return year_diff - is_partial_year

【讨论】:

感谢您的帮助!您能否详细说明&lt; 运算符在这种情况下的含义?我的意思是,如果is_partial_year 的计算结果为true,那么它会继承is_partial_year?那么is_partial_year 是一个布尔值,计算结果为10,然后进行减法运算? 是的,布尔值本质上是一个 1/0 整数,在这里被这样对待。这适用于 SQL 和 Python 代码

以上是关于SQLAlchemy,如何将混合表达式设置为“理解”日期时间?的主要内容,如果未能解决你的问题,请参考以下文章

如何将sqlalchemy中列的默认值设置为关系中列的值?

如何在 SQLAlchemy 中设置 M2M 混合计数属性?

SQLAlchemy - 将日期时间设置为字符串并获取日期?

如何使用 SQLAlchemy 将列默认设置为 PostgreSQL 函数?

SQLAlchemy:如何在删除时禁用 ORM 级外键处理?

sqlalchemy 的 ORM 与 Core 混合方式使用示例