4

我必须使用 SQLalchemy Core 表达式来获取对象,因为 ORM 不能执行“更新和返回”。(ORM 中的更新没有returning

from sqlalchemy import update
class User(ORMBase): 
    ...
# pure sql expression, the object returned is not ORM object.
# the object is a RowProxy.
object = update(User)  \
    .values({'name': 'Wayne'})  \
    .where(User.id == subquery.as_scalar()) \
    .returning() \
    .fetchone()

什么时候

db_session.add(object)

它报告UnmappedInstanceError: Class 'sqlalchemy.engine.result.RowProxy' is not mapped

如何将该RowProxy对象从 sql 表达式放入 ORM 的身份映射?

4

2 回答 2

2

我不确定是否有一种直接的方法来执行您所描述的操作,这本质上是构建一个直接映射到数据库条目但不通过 ORM 执行查询的 ORM 对象。

我的直觉是,天真的方法(只需使用数据库中的值构建 ORM 对象)只会创建具有相同值的另一行(或由于唯一性约束而失败)。

执行您所要求的更标准的方法是首先通过 ORM 查询行,然后从该 ORM 对象更新数据库。

user = User.query.filter(User.user_attribute == 'foo').one()
user.some_value = 'bar'
session.add(user)
session.commit()

我不确定您是否有一些限制阻止您使用该模式。该文档通过类似的示例工作

于 2017-01-09T21:17:29.740 回答
1

简单案例:

可能的快速解决方案:从kwargs您的构造对象RowProxy,因为它们是类似对象的。

鉴于:

rowproxy = update(User)  \
    .values({'name': 'Wayne'})  \
    .where(User.id == subquery.as_scalar()) \
    .returning() \
    .fetchone()

我们或许可以做到:

user = User(**dict(rowproxy.items()))

rowproxy.items()tuples对的回报key-valuedict(...)将 转换tuples为实际key-value对;并User(...)获取属性名称kwargsmodel

更困难的案例:

但是,如果您有一个与 SQL 不完全相同的modelwhere怎么办?例如:attribute namestable column name

class User(ORMBase):
    # etc...
    user_id = Column(name='id', etc)

当我们尝试将我们解包rowproxyUser类中时,我们可能会收到如下错误:(TypeError: 'id' is an invalid keyword argument for User因为它是期待的user_id)。

现在它变脏了:我们应该围绕mapper如何从table属性获取model属性,反之亦然:

kw_map = {a.key: a.class_attribute.name for a in User.__mapper__.attrs}

这里,a.keymodel attribute(和kwarg),a.class_attribute.nametable attribute。这给了我们类似的东西:

{
    "user_id": "id"
}

好吧,我们实际上想要提供从rowproxy.

kwargs = {a.key: rowproxy[a.class_attribute.name] for a in User.__mapper__.attrs}

现在我们可以这样做:

user = User(**kwargs)

勘误表:

  • 您可能希望session.commit()在调用后update().returning()立即进行,以防止您的更改与当它们永久存储在数据库中时发生长时间的延迟。以后不需要session.add(user)- 您已经updated()并且只需要commit()该交易
  • object是 Python 中的关键字,所以尽量不要踩它;你可能会得到一些非常奇怪的行为;这就是为什么我重命名为rowproxy.
于 2017-11-09T23:55:59.003 回答