在SQLAlchemy Core表中创建一个计算列

4

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

翻译结果:

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

我在搜索中发现了一些使用column_property或hybrid_property的SQLAlchemy ORM解决方案。而我这种情况只能使用SQLAlchemy Core。"Original Answer"翻译成"最初的回答"。
1个回答

2

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

1
如果我直接在表实例上创建派生列,可能会出现名称冲突,full_name只是一个示例,实际上可以是任何单词。我的项目严重依赖于table.c来进行许多与列相关的检查,所以根据您的建议,我可以将full_name移动到table.c中的ColumnCollection实例中。但是我检查了源代码,发现ColumnCollection只接受Column实例。如果有一种方法可以绕过并将其注入到ColumnCollection中,那对我来说是可接受的。 - undefined
我已经编辑了我的答案,通过将SQL表达式存储在一个新的集合中对其进行命名空间处理。可以很容易地通过猴子补丁(columns collection)来接受这些,但这可能会破坏其他期望只在那里找到列的代码。我建议将这些集合保持分开,因为它们包含不同的内容。如果你真的想要一起访问它们,你可以将self.d = Properties({})更改为self.d = Properties(ChainMap({}, self.c._data)),然后通过user.d来访问所有内容。 - undefined
谢谢@EAW,虽然你的想法不能解决我所有的问题,但这对我来说仍然是一个非常好的方向。如果使用扩展属性d,所有原始的Table功能都不会被破坏。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接