sqlalchemy 2.0.51
SQLAlchemy is the Python SQL toolkit and Object-Relational Mapping (ORM) library. Version 2.0 introduced a major API redesign with select(), insert(), update(), delete() as core entry points, first-class async support, and improved type hints using Mapped[].
Overview
SQLAlchemy provides two layers:
- Core — SQL expression language, connection management, schema definition. Works directly with SQL constructs (
select, insert, join, CTEs, etc.) without ORM.
- ORM — Object-relational mapping built on top of Core. Declarative class definitions, relationships, sessions, lazy loading strategies.
Both layers share the same engine, connection pool, and type system. You can mix Core and ORM in the same project.
Supported Databases
SQLAlchemy ships with built-in dialects for these database backends:
| Database |
Dialect Name |
Drivers |
| PostgreSQL |
postgresql |
psycopg, psycopg2, pg8000, asyncpg (async) |
| MySQL |
mysql |
pymysql, mysqldb, mysqlconnector, mariadb, asyncmy (async), aiomysql (async) |
| MariaDB |
mariadb |
mariadb, mariadbconnector, pymysql |
| SQLite |
sqlite |
pysqlite (built-in sqlite3), aiosqlite (async), pysqlcipher (encrypted) |
| Oracle |
oracle |
oracledb, cx_oracle |
| MS SQL Server |
mssql |
pyodbc, pymssql, aioodbc (async) |
Third-party dialects extend support to: CockroachDB, IBM DB2, Firebird, SAP HANA, ClickHouse, Snowflake, BigQuery, SQLite via ODBC, and more. Install them as separate packages (e.g., cockroachdb, ibm_db_sa).
Connection URL format: {dialect}+{driver}://{user}:{password}@host:{port}/{database}
Key Design Principles
- Eagerly execute Core constructs —
engine.execute(select(...)) returns results immediately.
- Sessions manage object state — The ORM
Session tracks identity, changes, and relationships.
- Declarative is the standard — Use
DeclarativeBase with Mapped[] type hints for modern mappings.
- Async is first-class —
AsyncEngine and AsyncSession use asyncpg, asyncmy, or aiosqlite.
Usage
Quick Start: Core (No ORM)
from sqlalchemy import create_engine, select, insert, text
from sqlalchemy.engine import Engine
engine = create_engine("sqlite:///example.db")
# Raw SQL
with engine.connect() as conn:
result = conn.execute(text("SELECT * FROM users WHERE id = :id"), {"id": 1})
row = result.fetchone()
# Expression language
stmt = select(User).where(User.id == 1)
with engine.connect() as conn:
result = conn.execute(stmt)
user = result.scalar_one_or_none()
Quick Start: ORM (Declarative)
from sqlalchemy import create_engine, select, ForeignKey
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session, relationship
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
addresses: Mapped[list["Address"]] = relationship(back_populates="user")
class Address(Base):
__tablename__ = "addresses"
id: Mapped[int] = mapped_column(primary_key=True)
email: Mapped[str]
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
user: Mapped["User"] = relationship(back_populates="addresses")
engine = create_engine("sqlite:///example.db")
Base.metadata.create_all(engine)
with Session(engine) as session:
user = User(name="alice", addresses=[Address(email="a@b.com")])
session.add(user)
session.commit()
stmt = select(User).where(User.name == "alice")
user = session.execute(stmt).scalar_one()
Quick Start: Async ORM
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/dbname")
async_session = sessionmaker(engine, class_=AsyncSession)
async with async_session() as session:
stmt = select(User).where(User.id == 1)
result = await session.execute(stmt)
user = result.scalar_one_or_none()
Gotchas
session.add() does not emit INSERT — It schedules the insert. session.flush() sends SQL to the database; session.commit() flushes and commits the transaction. Call flush() explicitly if you need the INSERT before commit (e.g., to get a generated primary key).
Session.execute(select(...)) vs engine.execute(select(...)) — Session-scoped execution auto-applies expiration rules and participates in the unit of work. Engine-level execution is raw Core with no ORM tracking. Use session for ORM objects, engine for pure Core queries.
- Relationships default to lazy loading — Accessing a relationship attribute triggers a separate SQL query (N+1 problem). Use
selectinload() or joinedload() in the options of select() to eagerly load relationships. In 2.0+, raiseload() is the default for expired attributes, so unexpected lazy loads raise LazyLoadError.
Mapped[list[Child]] vs Mapped[set[Child]] — The collection type in the annotation determines the Python collection used at runtime. Use list for ordered, set for unique, or mapped_collection() for dict-like mappings keyed by attribute.
- Foreign keys need explicit
ForeignKey — SQLAlchemy does not infer relationships from matching column names alone. Always declare ForeignKey("table.column") on the child side and relationship() on both sides with back_populates.
- Connection pooling is enabled by default —
QueuePool is used for most backends. For SQLite, use StaticPool with check_same_thread=False for in-memory databases shared across threads, or NullPool to disable pooling. For production PostgreSQL, tune pool_size and max_overflow.
create_all() vs alembic — Base.metadata.create_all(engine) creates tables that don't exist but does not alter existing ones. Use Alembic for schema migrations in production.
- Async sessions are not thread-safe — Each async task should use its own
AsyncSession. Do not share an AsyncSession across tasks or threads. Use sessionmaker() to create fresh sessions per request.
- Type decorators need
process_bind_param and process_result_value — When writing custom TypeDecorator, implement both methods. process_bind_param transforms Python → database, process_result_value transforms database → Python.
References
- 01-engine-connections — Engine creation, URL format, connection pooling
- 02-core-expressions — Core SQL expression language (SELECT, INSERT, UPDATE, DELETE, JOINs)
- 03-schema-definition — Table, Column, MetaData, constraints, indexes, reflection
- 04-declarative-orm — DeclarativeBase, Mapped[], mapped_column, registry
- 05-session-management — Session lifecycle, transactions, flush/commit, scoped_session
- 06-relationships — One-to-many, many-to-one, many-to-many, back_populates, cascade
- 07-query-loading — joinedload, selectinload, subqueryload, defer, load_only, raiseload
- 08-type-system — Built-in types, TypeDecorator, custom types, dialect-specific types
- 09-async-support — AsyncEngine, AsyncSession, async drivers, streaming results
- 10-events-listeners — Event listeners on engines, sessions, mappers, attributes
- 11-extensions — hybrid properties, automap, baked queries, mutable, ordering_list
- 12-performance-patterns — Bulk operations, connection tuning, query optimization
1---2name: sqlalchemy-2-0-513description: SQLAlchemy 2.0 ORM and Core toolkit for Python database access. Use this skill whenever the user mentions SQLAlchemy, ORM models, database queries, engine creation, session management, declarative mappings, relationships (one-to-many, many-to-many), connection pooling, async database access, SQL expression construction, or any Python database abstraction task. Covers both Core (expression language) and ORM layers. Supports PostgreSQL, MySQL/MariaDB, SQLite, Oracle, Microsoft SQL Server, and third-party dialects (CockroachDB, IBM DB2, Firebird, SAP HANA, etc.).4---56# sqlalchemy 2.0.5178SQLAlchemy is the Python SQL toolkit and Object-Relational Mapping (ORM) library. Version 2.0 introduced a major API redesign with `select()`, `insert()`, `update()`, `delete()` as core entry points, first-class async support, and improved type hints using `Mapped[]`.910## Overview1112SQLAlchemy provides two layers:1314- **Core** — SQL expression language, connection management, schema definition. Works directly with SQL constructs (`select`, `insert`, `join`, CTEs, etc.) without ORM.15- **ORM** — Object-relational mapping built on top of Core. Declarative class definitions, relationships, sessions, lazy loading strategies.1617Both layers share the same engine, connection pool, and type system. You can mix Core and ORM in the same project.1819### Supported Databases2021SQLAlchemy ships with built-in dialects for these database backends:2223| Database | Dialect Name | Drivers |24|---|---|---|25| PostgreSQL | `postgresql` | `psycopg`, `psycopg2`, `pg8000`, `asyncpg` (async) |26| MySQL | `mysql` | `pymysql`, `mysqldb`, `mysqlconnector`, `mariadb`, `asyncmy` (async), `aiomysql` (async) |27| MariaDB | `mariadb` | `mariadb`, `mariadbconnector`, `pymysql` |28| SQLite | `sqlite` | `pysqlite` (built-in `sqlite3`), `aiosqlite` (async), `pysqlcipher` (encrypted) |29| Oracle | `oracle` | `oracledb`, `cx_oracle` |30| MS SQL Server | `mssql` | `pyodbc`, `pymssql`, `aioodbc` (async) |3132Third-party dialects extend support to: CockroachDB, IBM DB2, Firebird, SAP HANA, ClickHouse, Snowflake, BigQuery, SQLite via ODBC, and more. Install them as separate packages (e.g., `cockroachdb`, `ibm_db_sa`).3334Connection URL format: `{dialect}+{driver}://{user}:{password}@host:{port}/{database}`3536### Key Design Principles3738- **Eagerly execute Core constructs** — `engine.execute(select(...))` returns results immediately.39- **Sessions manage object state** — The ORM `Session` tracks identity, changes, and relationships.40- **Declarative is the standard** — Use `DeclarativeBase` with `Mapped[]` type hints for modern mappings.41- **Async is first-class** — `AsyncEngine` and `AsyncSession` use `asyncpg`, `asyncmy`, or `aiosqlite`.4243## Usage4445### Quick Start: Core (No ORM)4647```python48from sqlalchemy import create_engine, select, insert, text49from sqlalchemy.engine import Engine5051engine = create_engine("sqlite:///example.db")5253# Raw SQL54with engine.connect() as conn:55 result = conn.execute(text("SELECT * FROM users WHERE id = :id"), {"id": 1})56 row = result.fetchone()5758# Expression language59stmt = select(User).where(User.id == 1)60with engine.connect() as conn:61 result = conn.execute(stmt)62 user = result.scalar_one_or_none()63```6465### Quick Start: ORM (Declarative)6667```python68from sqlalchemy import create_engine, select, ForeignKey69from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session, relationship7071class Base(DeclarativeBase):72 pass7374class User(Base):75 __tablename__ = "users"76 id: Mapped[int] = mapped_column(primary_key=True)77 name: Mapped[str]78 addresses: Mapped[list["Address"]] = relationship(back_populates="user")7980class Address(Base):81 __tablename__ = "addresses"82 id: Mapped[int] = mapped_column(primary_key=True)83 email: Mapped[str]84 user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))85 user: Mapped["User"] = relationship(back_populates="addresses")8687engine = create_engine("sqlite:///example.db")88Base.metadata.create_all(engine)8990with Session(engine) as session:91 user = User(name="alice", addresses=[Address(email="a@b.com")])92 session.add(user)93 session.commit()9495 stmt = select(User).where(User.name == "alice")96 user = session.execute(stmt).scalar_one()97```9899### Quick Start: Async ORM100101```python102from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession103from sqlalchemy.orm import sessionmaker104105engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/dbname")106async_session = sessionmaker(engine, class_=AsyncSession)107108async with async_session() as session:109 stmt = select(User).where(User.id == 1)110 result = await session.execute(stmt)111 user = result.scalar_one_or_none()112```113114## Gotchas115116- **`session.add()` does not emit INSERT** — It schedules the insert. `session.flush()` sends SQL to the database; `session.commit()` flushes and commits the transaction. Call `flush()` explicitly if you need the INSERT before commit (e.g., to get a generated primary key).117- **`Session.execute(select(...))` vs `engine.execute(select(...))`** — Session-scoped execution auto-applies expiration rules and participates in the unit of work. Engine-level execution is raw Core with no ORM tracking. Use session for ORM objects, engine for pure Core queries.118- **Relationships default to lazy loading** — Accessing a relationship attribute triggers a separate SQL query (N+1 problem). Use `selectinload()` or `joinedload()` in the options of `select()` to eagerly load relationships. In 2.0+, `raiseload()` is the default for expired attributes, so unexpected lazy loads raise `LazyLoadError`.119- **`Mapped[list[Child]]` vs `Mapped[set[Child]]`** — The collection type in the annotation determines the Python collection used at runtime. Use `list` for ordered, `set` for unique, or `mapped_collection()` for dict-like mappings keyed by attribute.120- **Foreign keys need explicit `ForeignKey`** — SQLAlchemy does not infer relationships from matching column names alone. Always declare `ForeignKey("table.column")` on the child side and `relationship()` on both sides with `back_populates`.121- **Connection pooling is enabled by default** — `QueuePool` is used for most backends. For SQLite, use `StaticPool` with `check_same_thread=False` for in-memory databases shared across threads, or `NullPool` to disable pooling. For production PostgreSQL, tune `pool_size` and `max_overflow`.122- **`create_all()` vs `alembic`** — `Base.metadata.create_all(engine)` creates tables that don't exist but does not alter existing ones. Use Alembic for schema migrations in production.123- **Async sessions are not thread-safe** — Each async task should use its own `AsyncSession`. Do not share an `AsyncSession` across tasks or threads. Use `sessionmaker()` to create fresh sessions per request.124- **Type decorators need `process_bind_param` and `process_result_value`** — When writing custom `TypeDecorator`, implement both methods. `process_bind_param` transforms Python → database, `process_result_value` transforms database → Python.125126## References127128- [01-engine-connections](references/01-engine-connections.md) — Engine creation, URL format, connection pooling129- [02-core-expressions](references/02-core-expressions.md) — Core SQL expression language (SELECT, INSERT, UPDATE, DELETE, JOINs)130- [03-schema-definition](references/03-schema-definition.md) — Table, Column, MetaData, constraints, indexes, reflection131- [04-declarative-orm](references/04-declarative-orm.md) — DeclarativeBase, Mapped[], mapped_column, registry132- [05-session-management](references/05-session-management.md) — Session lifecycle, transactions, flush/commit, scoped_session133- [06-relationships](references/06-relationships.md) — One-to-many, many-to-one, many-to-many, back_populates, cascade134- [07-query-loading](references/07-query-loading.md) — joinedload, selectinload, subqueryload, defer, load_only, raiseload135- [08-type-system](references/08-type-system.md) — Built-in types, TypeDecorator, custom types, dialect-specific types136- [09-async-support](references/09-async-support.md) — AsyncEngine, AsyncSession, async drivers, streaming results137- [10-events-listeners](references/10-events-listeners.md) — Event listeners on engines, sessions, mappers, attributes138- [11-extensions](references/11-extensions.md) — hybrid properties, automap, baked queries, mutable, ordering_list139- [12-performance-patterns](references/12-performance-patterns.md) — Bulk operations, connection tuning, query optimization