Reputation: 11
I have been testing sqlalchemy for a few days and have some problems using the update method in ORM mode.
What I basically want to do is to update a table from the result of a subquery performed on a second table.
Here is a simplified example:
from sqlalchemy import create_engine, Column, Integer, Float, Boolean
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session
Base = declarative_base()
engine = create_engine('postgresql+psycopg2://urldb', echo=True)
session = Session(engine)
class User1(Base):
__tablename__ = 'user1'
index = Column(Integer, primary_key=True)
value1 = Column(Float)
value2 = Column(Boolean)
class User2(Base):
__tablename__ = 'user2'
index = Column(Integer, primary_key=True)
value1 = Column(Float)
value2 = Column(Boolean)
try:
User1.__table__.drop(engine)
User2.__table__.drop(engine)
Base.metadata.create_all(engine)
except:
Base.metadata.create_all(engine)
user1 = User1(value1=12, value2=True)
user2 = User1(value1=15, value2=True)
user3 = User1(value1=7, value2=True)
user4 = User2(value1=12, value2=True)
user5 = User2(value1=15, value2=False)
session.add(user1)
session.add(user2)
session.add(user3)
session.add(user4)
session.add(user5)
session.flush()
session.commit()
stmt = session.query(User2).filter(User2.value1 > 12).subquery()
session.query(User1).filter(User1.value1 == stmt.c.value1).update({User1.value2 : stmt.c.value2})
session.commit()
I expect value2
from user2
to be updated to False
, which that code does correctly. However, I get the following warning message. As far as I understand, update would expect some ORM like expression instead of stmt.c. But I do not see how to convert my stmt object into an ORM one (tried aliased to User1 but failed, I understood why afterwards...).
Any idea to circumvent that warning msg? Thanks in advance!
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/evaluator.py:71: SAWarning: Evaluating non-mapped column expression '%(4357770376 anon)s.value1' onto ORM instances; this is a deprecated use case. Please make use of the actual mapped columns in ORM-evaluated UPDATE / DELETE expressions. "UPDATE / DELETE expressions." % clause) /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/evaluator.py:71: SAWarning: Evaluating non-mapped column expression '%(4357770376 anon)s.value2' onto ORM instances; this is a deprecated use case. Please make use of the actual mapped columns in ORM-evaluated UPDATE / DELETE expressions. "UPDATE / DELETE expressions." % clause)
Upvotes: 1
Views: 1737
Reputation: 2977
The subquery declaration is not necessary. You can directly update from
on version 1.4:
from sqlalchemy import update
session.execute(
update(User1).values(
value2=User2.value2
)
.where(User1.value1==User2.value1, User2.value1 > 12)
.execution_options(synchronize_session="fetch") # won't work with 'evaluate'
)
session.commit()
# UPDATE user1 SET value2=(SELECT user2.value2
# FROM user2
# WHERE user1.value1 = user2.value1) WHERE user1.value1 > %(value1_1)s
# RETURNING user1.index
You have to set the execution_options
to not raise an error.
Another option involves a correlated subquery for the values only:
from sqlalchemy import select, update
session.execute(
update(User1).values(
value2=select(User2.value2).\
where(User1.value1 == User2.value1).\
scalar_subquery()
)
.where(User1.value1 > 12)
)
session.commit()
# UPDATE user1 SET value2=user2.value2
# FROM user2 WHERE user1.value1 = user2.value1 AND user2.value1 >= %(value1_1)s
# RETURNING user1.index
But then the update where
condition has to be applied on User1
.
Upvotes: 1
Reputation: 11
Found the following solution: 1. perform a session.query(stmt).all()
, 2. build a mydict
dictionnary based on result of 1., 3. create a new User1
instance with new_user = User1(**mydict)
, 4. Perform the update
using new_user
instead of stmt.c
. No more SAWarning msg.
Anyone having a more elegant/efficient solution?
Upvotes: 0