SQLAlchemy
raw JSON → 2.0.48 verified Tue May 12 auth: no python install: verified quickstart: verified
The most widely used Python SQL toolkit and ORM. v2.0 released January 2023 — massive breaking change from v1.x. The entire ORM query API shifted from session.query() to select(). engine.execute() removed. Passing raw strings to execute() removed. Typed mapped_column() replaces Column(). Current version: 2.0.48 (Mar 2026). Flask-SQLAlchemy's Model.query pattern is also legacy in v2.
pip install sqlalchemy Common errors
error AttributeError: 'Session' object has no attribute 'query' ↓
cause In SQLAlchemy 2.0, the `session.query()` method has been removed; ORM queries now use the `select()` construct.
fix
Replace
session.query(MyModel).filter(...).all() with session.execute(select(MyModel).filter(...)).scalars().all(). error ArgumentError: Textual SQL not allowed directly on Connection.execute(). Please use text() or bind parameters. ↓
cause SQLAlchemy 2.0 requires explicit `text()` constructs for executing raw SQL strings with `connection.execute()` or `session.execute()`.
fix
Wrap raw SQL strings in
sqlalchemy.text(): connection.execute(text('SELECT * FROM my_table')). error TypeError: Mapped expects a type annotation argument ↓
cause When defining declarative models in SQLAlchemy 2.0 using `Mapped`, column definitions should typically use `mapped_column()` or just type annotations, not the `Column()` construct without an explicit type hint.
fix
Use
mapped_column() instead of Column() for mapped attributes: id: Mapped[int] = mapped_column(primary_key=True). error AttributeError: type object 'User' has no attribute 'query' ↓
cause In Flask-SQLAlchemy when using SQLAlchemy 2.0, the `Model.query` attribute is deprecated in favor of direct SQLAlchemy 2.0-style `session.execute(select(...))` queries.
fix
Use
db.session.execute(db.select(User).filter_by(username='test')) instead of User.query.filter_by(username='test').first(). Warnings
breaking engine.execute() removed in v2.0. Raises AttributeError. Most LLM-generated SQLAlchemy code uses engine.execute(). ↓
fix Use 'with engine.connect() as conn: conn.execute(text(...))'
breaking Passing raw strings to execute() removed. conn.execute('SELECT 1') raises ArgumentError in v2. ↓
fix from sqlalchemy import text; conn.execute(text('SELECT 1'))
breaking declarative_base() from sqlalchemy.ext.declarative is legacy in v2. Still works but generates RemovedIn20Warning. ↓
fix from sqlalchemy.orm import DeclarativeBase; class Base(DeclarativeBase): pass
gotcha session.query() is legacy in v2 — still works but not recommended. Flask-SQLAlchemy's Model.query is also legacy. LLMs trained pre-2023 generate session.query() patterns. ↓
fix Replace session.query(Model).filter(...) with session.scalars(select(Model).where(...))
gotcha session.execute() now returns a Result object. For ORM models use session.scalars() to get model instances directly, not session.execute(). ↓
fix users = session.scalars(select(User)).all() — not session.execute(select(User)).all()
gotcha Autocommit behavior changed in v2. Connections no longer autocommit. Must explicitly use with engine.begin() for autocommit or session.commit(). ↓
fix Use 'with engine.begin() as conn:' for autocommit. Or call session.commit() explicitly.
gotcha Column() without type annotations still works but loses IDE/mypy type inference. mapped_column() with Mapped[] is the v2 typed approach. ↓
fix id: Mapped[int] = mapped_column(primary_key=True)
Install
pip install 'sqlalchemy[asyncio]' Install compatibility verified last tested: 2026-05-12
python os / libc variant status wheel install import disk
3.10 alpine (musl) asyncio - - 0.54s 42.4M
3.10 alpine (musl) sqlalchemy - - 0.52s 42.4M
3.10 slim (glibc) asyncio - - 0.36s 41M
3.10 slim (glibc) sqlalchemy - - 0.36s 41M
3.11 alpine (musl) asyncio - - 0.79s 47.5M
3.11 alpine (musl) sqlalchemy - - 0.79s 47.5M
3.11 slim (glibc) asyncio - - 0.66s 46M
3.11 slim (glibc) sqlalchemy - - 0.65s 46M
3.12 alpine (musl) asyncio - - 0.86s 38.8M
3.12 alpine (musl) sqlalchemy - - 0.84s 38.8M
3.12 slim (glibc) asyncio - - 0.85s 37M
3.12 slim (glibc) sqlalchemy - - 0.83s 37M
3.13 alpine (musl) asyncio - - 0.81s 38.2M
3.13 alpine (musl) sqlalchemy - - 0.81s 38.2M
3.13 slim (glibc) asyncio - - 0.79s 37M
3.13 slim (glibc) sqlalchemy - - 0.80s 37M
3.9 alpine (musl) asyncio - - 0.45s 41.1M
3.9 alpine (musl) sqlalchemy - - 0.46s 41.1M
3.9 slim (glibc) asyncio - - 0.44s 40M
3.9 slim (glibc) sqlalchemy - - 0.43s 40M
Imports
- select (v2 ORM query) wrong
# v1 style — legacy with Session(engine) as session: user = session.query(User).filter(User.username == 'alice').first()correctfrom sqlalchemy import select from sqlalchemy.orm import Session with Session(engine) as session: # v2 style — use select() stmt = select(User).where(User.username == 'alice') user = session.scalars(stmt).first() # Insert new_user = User(username='bob', email='bob@example.com') session.add(new_user) session.commit() - mapped_column (v2 ORM models) wrong
from sqlalchemy import Column, Integer, String from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) username = Column(String(50))correctfrom sqlalchemy import String from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column class Base(DeclarativeBase): pass class User(Base): __tablename__ = 'users' id: Mapped[int] = mapped_column(primary_key=True) username: Mapped[str] = mapped_column(String(50), unique=True) email: Mapped[str] = mapped_column(String(120)) - engine.connect / text() wrong
engine = create_engine('...') engine.execute('SELECT 1') # removed in v2 # also wrong: conn.execute('SELECT 1') # raw strings removed in v2correctfrom sqlalchemy import create_engine, text engine = create_engine('postgresql://user:pass@localhost/db') with engine.connect() as conn: result = conn.execute(text('SELECT 1')) print(result.fetchall())
Quickstart verified last tested: 2026-04-23
# pip install sqlalchemy
from sqlalchemy import create_engine, String, select, text
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = 'users'
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(50))
engine = create_engine('sqlite:///test.db')
Base.metadata.create_all(engine)
with Session(engine) as session:
# Insert
session.add(User(name='Alice'))
session.commit()
# Query — v2 style
stmt = select(User).where(User.name == 'Alice')
user = session.scalars(stmt).first()
print(user.name) # 'Alice'