Why 2.0, and why async
SQLAlchemy 2.0 unifies the Core SELECT/INSERT API with the ORM, replaces Query with select(), and ships first-class async support across the stack.
Two consequences for backend code:
- One API surface.
session.execute(select(User).where(...))looks identical to a Core query against the same engine. No moresession.query(User)vsengine.execute(text("..."))split. - An honest async path.
AsyncEngine+AsyncSessionwrap the sync ORM with awaitable operations. Pre-2.0 async stories were either ad-hoc wrappers or non-ORM. Now the async session is mainstream and the patterns are settled.
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy import select
engine = create_async_engine("postgresql+asyncpg://...")
async def get_user(uid: int) -> User:
async with AsyncSession(engine) as session:
result = await session.execute(select(User).where(User.id == uid))
return result.scalar_one()
The driver matters: async needs an async driver (asyncpg, aiomysql, aiosqlite). The sync psycopg2 will not work behind create_async_engine โ SQLAlchemy rejects the URL at construction time.
