【问题标题】:connect SQLAlchemy ORM with the objects from sql core expression?将 SQLAlchemy ORM 与 sql 核心表达式中的对象连接起来?
【发布时间】:2017-05-21 22:18:01
【问题描述】:

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

【问题讨论】:

  • 这似乎是SQLAlchemy 的疏忽,update-returning 之类的东西(通常)返回正在更新的整个表,没有简单的方法来映射回对象。不幸的是,没有比这更令人满意的答案了。
  • 也许可以从obj = User(**dict(object.items())) 获得一些好处,但它似乎并非在所有情况下都有效。

标签: python orm sqlalchemy object-relational-model


【解决方案1】:

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

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

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

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

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

【讨论】:

    【解决方案2】:

    简单案例:

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

    给定:

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

    我们或许可以做到:

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

    rowproxy.items() 返回 tupleskey-value 对; dict(...)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 中返回的值,除了允许类似对象的访问之外,还允许类似字典的访问:

    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的原因。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-10
      • 1970-01-01
      • 2016-06-24
      • 1970-01-01
      • 2021-09-09
      • 2021-02-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多