skills/sqlalchemy/SKILL.md
Use when editing SQLAlchemy code, sqlalchemy imports, mapped_column, DeclarativeBase, ORM models, relationships, select() queries, async sessions, engines, events, or migrations.
npx skillsauth add cofin/flow sqlalchemyInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
SQLAlchemy 2.0 uses Mapped[] type annotations, mapped_column(), and select() statements throughout. Legacy patterns (Column(), session.query()) are never used.
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from sqlalchemy import String, Text, DateTime, func
from datetime import datetime
from typing import Optional
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
# Required column -- Mapped[type] (non-optional = NOT NULL)
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(100))
# Nullable column -- use Optional
bio: Mapped[Optional[str]] = mapped_column(Text)
# Server default
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now()
)
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
engine = create_engine("postgresql+psycopg://user:pass@localhost/db")
SessionFactory = sessionmaker(engine, expire_on_commit=False)
with SessionFactory() as session:
# auto-closed on exit
pass
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
async_engine = create_async_engine(
"postgresql+asyncpg://user:pass@localhost/db",
pool_size=5, max_overflow=10, pool_pre_ping=True,
)
AsyncSessionFactory = async_sessionmaker(
async_engine, class_=AsyncSession, expire_on_commit=False,
)
async with AsyncSessionFactory() as session:
result = await session.execute(select(User))
users = result.scalars().all()
from sqlalchemy import select, and_, or_, func
from sqlalchemy.orm import selectinload
# Basic select
stmt = select(User).where(User.name == "alice")
# Multiple conditions (AND)
stmt = select(User).where(and_(User.active == True, User.age > 18))
# IN clause
stmt = select(User).where(User.id.in_([1, 2, 3]))
# Join with eager loading
stmt = select(User).options(selectinload(User.posts)).where(User.active == True)
# Aggregation
stmt = select(func.count()).select_from(User).where(User.active == True)
<workflow>
| Need | Pattern | Key Import |
| --- | --- | --- |
| Define a model | DeclarativeBase + mapped_column() | sqlalchemy.orm |
| Sync database access | sessionmaker factory | sqlalchemy.orm |
| Async database access | async_sessionmaker factory | sqlalchemy.ext.asyncio |
| Query data | select() + where() chain | sqlalchemy |
| Eager load relations | selectinload() / joinedload() | sqlalchemy.orm |
| Schema migration | Alembic autogenerate | alembic |
Mapped[] + mapped_column() -- never Column()with / async with) for session lifecycleselect() -- never session.query()selectinload() or raiseload("*") in async contextsRun through the validation checkpoint below before considering the work complete.
</workflow> <guardrails>select(User).where(...) not session.query(User).filter(...)mapped_column(): never Column() for ORM modelsMapped[int], Mapped[Optional[str]] -- no untyped columnssessionmaker / async_sessionmaker: never raw Session() calls in applicationsexpire_on_commit=False in async session factories to avoid lazy-load errorsselectinload() in async contexts -- lazy loading triggers MissingGreenlet errorsback_populates over backref for explicit bidirectional relationshipsBefore delivering SQLAlchemy code, verify:
Mapped[] + mapped_column() (no legacy Column())select() style (no session.query())expire_on_commit=False for asyncselectinload, joinedload)String(100), not bare Stringasyncpg not psycopg for async)Task: "Create a User model with posts relationship, async session setup, and a query to fetch active users with their posts."
from __future__ import annotations
from datetime import datetime
from typing import Optional
from sqlalchemy import ForeignKey, String, Text, DateTime, func, select
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, selectinload
# --- Models ---
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(100))
email: Mapped[str] = mapped_column(String(255), unique=True)
active: Mapped[bool] = mapped_column(default=True)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now()
)
posts: Mapped[list[Post]] = relationship(back_populates="author")
class Post(Base):
__tablename__ = "posts"
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String(200))
body: Mapped[Optional[str]] = mapped_column(Text)
author_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
author: Mapped[User] = relationship(back_populates="posts")
# --- Async Engine & Session ---
async_engine = create_async_engine(
"postgresql+asyncpg://user:pass@localhost:5432/mydb",
pool_size=5, max_overflow=10, pool_pre_ping=True,
)
AsyncSessionFactory = async_sessionmaker(
async_engine, class_=AsyncSession, expire_on_commit=False,
)
# --- Query ---
async def get_active_users_with_posts() -> list[User]:
async with AsyncSessionFactory() as session:
stmt = (
select(User)
.options(selectinload(User.posts))
.where(User.active == True)
.order_by(User.name)
)
result = await session.execute(stmt)
return list(result.scalars().all())
</example>
For detailed guides and code examples, refer to the following documents in references/:
Mapped[] annotations, mapped_column(), mixins, inheritance, hybrid properties.relationship() typing, one-to-many, many-to-many, loading strategies, cascades, self-referential.select() statements, where clauses, joins, aggregations, subqueries, CTEs, bulk operations, result handling.create_engine(), connection URLs, pooling, events, async engines, multi-engine patterns.Session, AsyncSession, lifecycle, scoped sessions, merge, refresh, savepoints.AsyncSession patterns, driver notes, lazy-loading pitfalls.development
Use when tracing execution paths, mapping dependencies, understanding unfamiliar code, following data flow, investigating end-to-end behavior, debugging call chains, or deciding which files to read next.
development
Use when reviewing authentication, authorization, user input, secrets, API keys, database queries, file uploads, session management, external API calls, OWASP risks, or data handling attack surface.
testing
Use when analyzing tradeoffs, comparing approaches, weighing options, assessing risks, stress-testing conclusions, identifying blind spots, or applying multiple viewpoints to a decision.
development
Use when reviewing hot paths, slow code, database queries, N+1 risks, memory usage, loops, I/O, caching strategy, concurrency, latency-sensitive paths, or resource efficiency.