使用SQLAlchemy ORM通过session.query更新连接表

19

我正在使用MySQL,希望生成以下SQL:

UPDATE tableA
INNER JOIN tableB
ON tableA.some_id = tableB.some_id
SET tableA.foo = 1
WHERE tableB.bar IN ('baz','baaz')

这是我的SQLAlchemy查询:

session.query(tableA).join(tableB, tableA.some_id == tableB.some_id) \
                     .filter(tableB.bar.in_(['baz','baaz']))\
                     .update({tableA.foo: 1})
但它生成的SQL是这样的(一个多表更新,没有连接条件,这不是我想要的):
UPDATE tableA, tableB 
SET tableA.foo = 1
WHERE tableB.bar IN ('baz','baaz')

我尝试将 .join 更改为另一个 .filter 以指定连接条件,但这并没有解决问题。我应该如何强制执行此简单的更新语句来执行正确的连接?


你解决了这个问题吗?我有一个几乎相同的问题,正在苦苦挣扎。 - Fuxi
不得不采用一种变通方法(两个查询而不是一个)。如果你也遇到了同样的问题,请点赞这个问题,也许会有人看到它。 - ValAyal
2个回答

13

从0.7.4版本开始,sqlalchemy.sql.expression.update 允许你在 WHERE 子句中引用多个表。这样,你就可以构建并执行如下表达式:

users.update().values(name='ed').where(
        users.c.name==select([addresses.c.email_address]).\
                    where(addresses.c.user_id==users.c.id).\
                    as_scalar()
        )

(以上内容直接从链接中提取的示例)

ValAyal遇到的问题实际上是因为Query.join()Query.update()不兼容。不幸的是,直到0.9.1版本,这仍然会静默生成像ValAyal分享的查询。 0.9.1版更新日志指出修改了该行为以发出警告:

[orm] [bug] 当使用Query.update()或Query.delete()方法时,Query不支持连接、子选择或特殊的FROM子句;如果已调用Query.join()或Query.select_from()等方法,则不再静默忽略这些字段,而是发出警告。从1.0.0b5开始,这将引发错误。

参考资料:#3349

我们公司今晚遇到了这个问题,并发现我们的代码实际上会发出以下警告(说明将在1.0版本中报错):

SAWarning: Can't call Query.update() or Query.delete() when join(), outerjoin(), select_from(), or from_self() has been called.  This will be an exception in 1.0
  self._validate_query_state()

在我们的情况下,我们选择将更新转换为一个对单表的查询和更新。

请注意,这里的示例不是多表更新,而是使用子查询引用多个表的标准SQL方式。 - Ilja Everilä

6

我认为我遇到了完全一样的问题。这是我的解决方案:

query = update(Model).values(field=123)
query = query.where(Model.parent_model_id == ParentModel.id)
query = query.where(ParentModel.grand_parent_id == GrandParentModel.id)
query = query.where(GrandParentModel.name == 'foobar')
session.execute(query)

这对于sqlite不起作用,但可能适用于其他数据库后端。 - Charles L.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接