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

Val*_*yal 9 python mysql sqlalchemy

使用MySQL,我想生成这个SQL:

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

这是我的SQLAlchemy查询:

session.query(tableA).join(tableB, tableA.some_id == tableB.some_id) \
                     .filter(tableB.bar.in_(['baz','baaz']))\
                     .update({tableA.foo: 1})
Run Code Online (Sandbox Code Playgroud)

但它生成的SQL是这样的(多表更新,没有连接条件,这不是我想要的):

UPDATE tableA, tableB 
SET tableA.foo = 1
WHERE tableB.bar IN ('baz','baaz')
Run Code Online (Sandbox Code Playgroud)

我已经尝试将.join更改为另一个.filter来指定连接条件,但没有解决问题.如何强制使用这个简单的更新语句来进行正确的连接?

Fux*_*uxi 8

我想我也有同样的问题。这是我的解决方案:

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)
Run Code Online (Sandbox Code Playgroud)


Myl*_*les 7

随着版本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()
        )
Run Code Online (Sandbox Code Playgroud)

(例如直接来自上面的链接)

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()
Run Code Online (Sandbox Code Playgroud)

在我们的示例中,我们选择将更新转换为对一个表的选择和更新.