Simple sample:
from sqlalchemy import Column, Integer, Computed, update, insert, select
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
from sqlalchemy.orm import DeclarativeBase, declared_attr
engine = create_async_engine(url="postgresql+asyncpg://db_user:db_pass@localhost:54320/db_name")
async_session = async_sessionmaker(bind=engine)
class BaseTable(DeclarativeBase):
@declared_attr
def __tablename__(cls) -> str:
return cls.__name__.lower()
class Tablichka(BaseTable):
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
quantity = Column(Integer, nullable=False)
price = Column(Integer, nullable=False)
total_cost = Column(Integer, Computed("quantity * price")) # Attention!
I want to update the row using an orm query. I need to get a result, so I use returning
:
async def run():
async with async_session() as session:
statement = insert(Tablichka).values(quantity=2, price=100).returning(Tablichka)
obj = (await session.execute(statement)).scalar_one()
print(f"total_cost: wait 200, really {obj.total_cost}") # total_cost: wait 200, really 200
id = obj.id
statement = update(Tablichka).filter_by(id=id).values(quantity=4, price=100).returning(Tablichka)
obj = (await session.execute(statement)).scalar_one()
print(f"total_cost: wait 400, really {obj.total_cost}") # total_cost: wait 400, really 200
statement = select(Tablichka).filter_by(id=id)
obj = (await session.execute(statement)).scalar_one()
print(f"total_cost: wait 400, really {obj.total_cost}") # total_cost: wait 400, really 200
await session.commit()
async with async_session() as session:
statement = select(Tablichka).filter_by(id=id)
obj = (await session.execute(statement)).scalar_one()
print(f"ntotal_cost: wait 400, really {obj.total_cost}") # total_cost: wait 400, really 400
if __name__ == "__main__":
import asyncio
loop = asyncio.get_event_loop()
loop.run_until_complete(run())
loop.close()
After the update, I expect to get a recalculated total_cost
, but I get an old total_cost
. I get updated fields except calculated fields. At the same time, the value is recalculated in the database. This confirms the select query in the new session.
How do I solve the problem?
Actually, I have a working example. But I don’t have an understanding of why it works:
async def run_create(session: AsyncSession):
statement = insert(Tablichka).values(quantity=2, price=100).returning(Tablichka)
obj = (await session.execute(statement)).scalar_one()
print(f"total_cost: wait 200, really {obj.total_cost}") # total_cost: wait 200, really 200
return obj.id
async def run_update(session: AsyncSession, id: int):
statement = update(Tablichka).filter_by(id=id).values(quantity=4, price=100).returning(Tablichka)
obj = (await session.execute(statement)).scalar_one()
print(f"total_cost: wait 400, really {obj.total_cost}") # total_cost: wait 400, really 400
async def run():
async with async_session() as session:
id = await run_create(session)
await run_update(session, id)
if __name__ == "__main__":
import asyncio
loop = asyncio.get_event_loop()
loop.run_until_complete(run())
loop.close()
But in my project there is a more complex example in which I cannot use my working example. I need to fix my broken example.
I use the following versions:
python = "^3.10"
sqlalchemy = "^2.0.35"
asyncpg = "^0.29.0"
greenlet = "^3.1.0"