【问题标题】:Create a Calculated Column in SQLAlchemy Core Table在 SQLAlchemy 核心表中创建计算列
【发布时间】:2019-09-03 03:09:23
【问题描述】:

我有一个 SQLAlchemy 表定义如下:

user = Table('user', MetaData(),
             Column('id', Integer),
             Column('first_name', String),
             Column('last_name', String))

查询时经常需要引用用户的全名,例如:

sql = (select([
          user.c.id,
          (user.c.first_name + user.c.last_name).label('full_name')
       ]).where(user.c.id == 123))

由于full_name在很多地方使用,所以这种代码有很多副本。

我想知道在SQLAlchemy中是否有一种方法可以创建一个计算列,这样我就可以像其他普通列一样方便地使用它,每当我引用user.c.full_name时,SQLAlchemy会自动将其转换为(user.c.first_name + user.c.last_name).label('full_name')

sql = (select([
          user.c.id,
          user.c.full_name
       ]).where(user.c.id == 123))

我在 SQLAlchemy ORM 中使用 column_property 或 hybrid_property 搜索并找到了一些解决方案。我的不同之处在于我只能使用 SQLAlchemy Core

【问题讨论】:

    标签: python database orm sqlalchemy


    【解决方案1】:

    无法在 sqlalchemy 核心中创建计算列。但是,您不需要这样做,只需将您的表达式保存在一个变量中,然后在您的选择语句中使用它。如果您有很多这些要存储,那么您可以通过将它们全部存储在一个集合中来命名它们。在下面的示例中,我为此使用了一个 SQLAlchemy Properties 对象,因此它的行为方式与列集合类似。

    class Table(sqlalchemy.Table):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.d = sqlalchemy.util._collections.Properties({})
    
    user = Table('user', MetaData(),
                 Column('id', Integer),
                 Column('first_name', String),
                 Column('last_name', String))
    user.d.full_name = (user.c.first_name + user.c.last_name).label('full_name')
    user.d.backwards_name = (user.c.last_name + user.c.first_name).label('backwards_name')
    
    sql = (select([
              user.c.id,
              user.d.full_name
           ]).where(user.c.id == 123))
    

    【讨论】:

    • 如果我直接在表实例上创建派生列,可能会出现名称冲突,full_name只是一个例子,实际上它可以是任何单词。我的项目严重依赖table.c 来做很多与列相关的检查,所以按照你的建议,我可以将full_name 移动到table.c,ColumnCollection 实例。但我检查了源代码,ColumnCollection 只接受 Column 实例。如果有一种方法可以绕过并将其注入 ColumnCollection,我可以接受。
    • 我已经编辑了我的答案,通过将 sql 表达式存储在一个新集合中来命名它们。对列集合进行monkeypatch 以接受这些是很简单的,但这可能会破坏所有其他期望只找到列的代码。我建议将这些集合分开是有意义的,因为它们包含不同的东西。如果您真的想一起访问它们,您可以将self.d = Properties({}) 更改为self.d = Properties(ChainMap({}, self.c._data))。并通过user.d 访问所有内容。
    • 感谢@EAW,虽然你的想法并不能解决我所有的顾虑,但这对我来说仍然是一个很好的方向。如果使用扩展属性 d,则所有原始 Table 功能都不会被破坏
    猜你喜欢
    • 2015-04-28
    • 1970-01-01
    • 1970-01-01
    • 2013-06-23
    • 2021-09-09
    • 1970-01-01
    • 2013-04-30
    • 1970-01-01
    • 2017-12-09
    相关资源
    最近更新 更多