我必须使用 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 的标识映射中
?

最佳答案

简单案例:

可能的快速解决方案:从 kwargsRowProxy 构造对象,因为它们类似于对象。

鉴于:

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(...)kwargs 作为 model 属性名称。

更难的情况:

但是,如果您有一个 model ,其中 attribute names 之一与 SQL table column name 不完全相同呢?例如。就像是:
class User(ORMBase):
    # etc...
    user_id = Column(name='id', etc)

当我们尝试将 rowproxy 解包到 User 类中时,我们可能会得到如下错误: 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 返回的值,它除了允许类对象访问还允许类 dict 访问:
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 的原因。
  • 关于python - 将 SQLAlchemy ORM 与 sql 核心表达式中的对象连接起来?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41517779/

    10-11 13:32