如何在 SQLAlchemy ORM 中表达多表更新(UPDATE FROM)?

boa*_*der 4 python sqlalchemy

CREATE TABLE foo (
    name text NOT NULL,
    deleted_at timestamp without time zone
);

CREATE TABLE bar (
    name text NOT NULL,
    status_id int
);

UPDATE bar set status_id=1
FROM foo
WHERE status_id <> 1 AND foo.name = bar.name AND foo.deleted_at is null;
Run Code Online (Sandbox Code Playgroud)

当我尝试使用 ORM 执行此操作时,最终出现此错误

InvalidRequestError: Can't call Query.update() or Query.delete() when join(), outerjoin(), select_from(), or from_self() has been called
Run Code Online (Sandbox Code Playgroud)

我想使用 ORM,以便在更新命令完成之前会话将根据更改进行更新。

Ilj*_*ilä 6

以下是使用 ORM发出多表更新的方法:

session.query(Bar).\
    filter(Bar.status_id != 1,
           Bar.name == Foo.name,
           Foo.deleted_at.is_(None)).\
    update({Bar.status_id: 1}, synchronize_session=False)
Run Code Online (Sandbox Code Playgroud)

查询 API文档中更新链接的脚注指向核心多表更新文档:

SQLAlchemyupdate()构造通过在 WHERE 子句中指定多个表来隐式支持这两种模式

它也扩展到 ORM 查询 API,这并不奇怪,因为 ORM 是构建在 Core 之上的。结果查询是:

UPDATE bar SET status_id=%(status_id)s
FROM foo
WHERE bar.status_id != %(status_id_1)s
  AND bar.name = foo.name
  AND foo.deleted_at IS NULL
Run Code Online (Sandbox Code Playgroud)

从你的错误中我怀疑你有类似的事情

session.query(Bar).select_from(Foo)...update(...)
Run Code Online (Sandbox Code Playgroud)

正如错误所述,这是不被接受的。您必须在 WHERE 子句中隐式传递 FROM 表,例如filter()在查询 API 中。

实现

我想使用 ORM,以便在更新命令完成之前会话将根据更改进行更新。

您必须synchronize_session相应地将 更改为'fetch''evaluate'