我必须使用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
.
如何将 sql 表达式中的RowProxy
对象放入 ORM 的身份映射中 ?
我不确定是否有一种直接的方法可以完成您所描述的操作,这本质上是构建一个直接映射到数据库条目但不通过 ORM 执行查询的 ORM 对象。
我的直觉是,天真的方法(只需使用数据库中的值构建 init ORM 对象)只会创建具有相同值的另一行(或者由于唯一性约束而失败)。
执行所要求操作的更标准方法是首先通过ORM查询行,然后从该ORM对象更新数据库。
user = User.query.filter(User.user_attribute == 'foo').one()
user.some_value = 'bar'
session.add(user)
session.commit()
我不确定你是否有一些限制,阻止你使用该模式。该文档通过类似的示例工作
简单案例:
可能的快速解决方案:从kwargs
RowProxy
构造对象,因为它们是类似对象的。
鉴于:
rowproxy = update(User)
.values({'name': 'Wayne'})
.where(User.id == subquery.as_scalar())
.returning()
.fetchone()
我们也许能够做到:
user = User(**dict(rowproxy.items()))
rowproxy.items()
返回key-value
对的tuples
;dict(...)
将tuples
转换为实际的key-value
对;User(...)
model
属性名称需要kwargs
。
更困难的情况:
但是,如果你有一个model
,其中一个attribute names
与SQLtable
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
)。
现在它变得很脏:我们应该有一个关于如何从table
属性到model
属性的mapper
,反之亦然:
kw_map = {a.key: a.class_attribute.name for a in User.__mapper__.attrs}
在这里,a.key
是model attribute
(和kwarg
),a.class_attribute.name
是table attribute
。这给了我们类似的东西:
{
"user_id": "id"
}
好吧,我们希望实际提供我们从rowproxy
中获取的值,除了允许类似对象的访问外,还允许类似字典的访问:
kwargs = {a.key: rowproxy[a.class_attribute.name] for a in User.__mapper__.attrs}
现在我们可以做到:
user = User(**kwargs)
勘误表:
- 您可能希望在调用
update().returning()
后立即session.commit()
,以防止更改与永久存储在数据库中时相比出现长时间延迟。无需稍后session.add(user)
- 您已经updated()
,只需要commit()
该交易 object
是Python中的一个关键字,所以尽量不要踩它;这样做可能会得到一些非常奇怪的行为;这就是为什么我重命名为rowproxy
。