Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update joined table via SQLAlchemy ORM using session.query

Working with MySQL, I'd like to generate this SQL:

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

This is my SQLAlchemy query:

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

But the SQL it generates is this (a multi-table update, with no join condition, which is not what I want):

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

I've tried changing the .join into another .filter to specify the join condition, that didn't solve the problem. How do I force this simple update statement to do the proper join?

like image 459
ValAyal Avatar asked Oct 15 '15 16:10

ValAyal


People also ask

How do I update data in SQLAlchemy?

Update table elements in SQLAlchemy. Get the books to table from the Metadata object initialized while connecting to the database. Pass the update query to the execute() function and get all the results using fetchall() function. Use a for loop to iterate through the results.

How do I join a Table in SQLAlchemy ORM?

I've found that the following works to join two tables: result = session. query(User, Document). select_from(join(User, Document)). filter(User.

What does all () do in SQLAlchemy?

all() method. The Query object, when asked to return full entities, will deduplicate entries based on primary key, meaning if the same primary key value would appear in the results more than once, only one object of that primary key would be present.

What are the query join methods supported by SQLAlchemy?

The Query.join()method currently supports several usage patterns and arguments that are considered to be legacy as of SQLAlchemy 1.3. A deprecation path will follow in the 1.4 series for the following features: Joining on relationship names rather than attributes: session.query(User).join("addresses")

What's new in SQLAlchemy?

New in version 1.4. SQLAlchemy 2.0 will standardize the production of SELECT statements across both Core and ORM by making direct use of the Select object within the ORM, removing the need for there to be a separate Query object. This mode of operation is available in SQLAlchemy 1.4 right now to support applications that will be migrating to 2.0.

What is the SQLAlchemy Orm?

The SQLAlchemy ORM is based around the concept of an identity map such that when an object is “loaded” from a SQL query, there will be a unique Python object instance maintained corresponding to a particular database identity.

What is method SQLAlchemy query from_statement?

methodsqlalchemy.orm.Query.from_statement(statement)¶ Execute the given SELECT statement and return results. This method bypasses all internal statement compilation, and the statement is executed without modification.


1 Answers

As of version 0.7.4 sqlalchemy.sql.expression.update does allow you to refer to multiple tables in the WHERE clause. With this, you could build and execute an expression like:

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

(example straight from the link above)

The problem ValAyal is having is actually because Query.join() is not supported with Query.update(). Unfortunately, until 0.9.1 this was silently generating queries like the one ValAyal shared above. The changelog notes for 0.9.1 notes that the behavior was modified to emit a warning:

[orm] [bug] Query doesn’t support joins, subselects, or special FROM clauses when using the Query.update() or Query.delete() methods; instead of silently ignoring these fields if methods like Query.join() or Query.select_from() has been called, a warning is emitted. As of 1.0.0b5 this will raise an error.

References: #3349

We actually ran into this where I work just this evening and found that our code is, in fact, emitting the following warning (which says it will an error in 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()

In our case, we opted to convert the update into a select and an update to one table.

like image 115
Myles Avatar answered Sep 19 '22 08:09

Myles