mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-14 12:47:22 -04:00
a45e2284da
in this patch the asyncio/events.py module, which existed only to raise errors when trying to attach event listeners, is removed, as we were already coding an asyncio-specific workaround in upstream Pool / Session to raise this error, just moved the error out to the target and did the same thing for Engine. We also add an async_sessionmaker class. The initial rationale here is because sessionmaker() is hardcoded to Session subclasses, and there's not a way to get the use case of sessionmaker(class_=AsyncSession) to type correctly without changing the sessionmaker() symbol itself to be a function and not a class, which gets too complicated for what this is. Additionally, _SessionClassMethods has only three methods on it, one of which is not usable with asyncio (close_all()), the others not generally used from the session class. Change-Id: I064a5fa5d91cc8d5bbe9597437536e37b4e801fe
105 lines
2.9 KiB
Python
105 lines
2.9 KiB
Python
"""Illustrates use of the sqlalchemy.ext.asyncio.AsyncSession object
|
|
for asynchronous ORM use.
|
|
|
|
"""
|
|
|
|
import asyncio
|
|
|
|
from sqlalchemy import Column
|
|
from sqlalchemy import DateTime
|
|
from sqlalchemy import ForeignKey
|
|
from sqlalchemy import func
|
|
from sqlalchemy import Integer
|
|
from sqlalchemy import String
|
|
from sqlalchemy.ext.asyncio import async_sessionmaker
|
|
from sqlalchemy.ext.asyncio import create_async_engine
|
|
from sqlalchemy.future import select
|
|
from sqlalchemy.orm import declarative_base
|
|
from sqlalchemy.orm import relationship
|
|
from sqlalchemy.orm import selectinload
|
|
|
|
Base = declarative_base()
|
|
|
|
|
|
class A(Base):
|
|
__tablename__ = "a"
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
data = Column(String)
|
|
create_date = Column(DateTime, server_default=func.now())
|
|
bs = relationship("B")
|
|
|
|
# required in order to access columns with server defaults
|
|
# or SQL expression defaults, subsequent to a flush, without
|
|
# triggering an expired load
|
|
__mapper_args__ = {"eager_defaults": True}
|
|
|
|
|
|
class B(Base):
|
|
__tablename__ = "b"
|
|
id = Column(Integer, primary_key=True)
|
|
a_id = Column(ForeignKey("a.id"))
|
|
data = Column(String)
|
|
|
|
|
|
async def async_main():
|
|
"""Main program function."""
|
|
|
|
engine = create_async_engine(
|
|
"postgresql+asyncpg://scott:tiger@localhost/test",
|
|
echo=True,
|
|
)
|
|
|
|
async with engine.begin() as conn:
|
|
await conn.run_sync(Base.metadata.drop_all)
|
|
async with engine.begin() as conn:
|
|
await conn.run_sync(Base.metadata.create_all)
|
|
|
|
# expire_on_commit=False will prevent attributes from being expired
|
|
# after commit.
|
|
async_session = async_sessionmaker(engine, expire_on_commit=False)
|
|
|
|
async with async_session() as session:
|
|
async with session.begin():
|
|
session.add_all(
|
|
[
|
|
A(bs=[B(), B()], data="a1"),
|
|
A(bs=[B()], data="a2"),
|
|
A(bs=[B(), B()], data="a3"),
|
|
]
|
|
)
|
|
|
|
# for relationship loading, eager loading should be applied.
|
|
stmt = select(A).options(selectinload(A.bs))
|
|
|
|
# AsyncSession.execute() is used for 2.0 style ORM execution
|
|
# (same as the synchronous API).
|
|
result = await session.execute(stmt)
|
|
|
|
# result is a buffered Result object.
|
|
for a1 in result.scalars():
|
|
print(a1)
|
|
print(f"created at: {a1.create_date}")
|
|
for b1 in a1.bs:
|
|
print(b1)
|
|
|
|
# for streaming ORM results, AsyncSession.stream() may be used.
|
|
result = await session.stream(stmt)
|
|
|
|
# result is a streaming AsyncResult object.
|
|
async for a1 in result.scalars():
|
|
print(a1)
|
|
for b1 in a1.bs:
|
|
print(b1)
|
|
|
|
result = await session.execute(select(A).order_by(A.id))
|
|
|
|
a1 = result.scalars().first()
|
|
|
|
a1.data = "new data"
|
|
|
|
await session.commit()
|
|
|
|
|
|
asyncio.run(async_main())
|