AI Skill Library

SQLAlchemy 2.0 ORM

Modern SQLAlchemy 2.x patterns: models, sessions, relationships, queries.

pythondatabaseorm
# SQLAlchemy 2.0 ORM

## Setup
```py
from sqlalchemy import create_engine, ForeignKey, String
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, Session

engine = create_engine('postgresql+psycopg://user:pw@host/db', echo=True)

class Base(DeclarativeBase): pass

class User(Base):
    __tablename__ = 'users'
    id: Mapped[int] = mapped_column(primary_key=True)
    email: Mapped[str] = mapped_column(String(255), unique=True)
    posts: Mapped[list['Post']] = relationship(back_populates='author', cascade='all, delete-orphan')

class Post(Base):
    __tablename__ = 'posts'
    id: Mapped[int] = mapped_column(primary_key=True)
    title: Mapped[str]
    user_id: Mapped[int] = mapped_column(ForeignKey('users.id'))
    author: Mapped[User] = relationship(back_populates='posts')

Base.metadata.create_all(engine)
```

## Sessions
```py
with Session(engine) as s, s.begin():
    s.add(User(email='a@b.c'))
# commit on context exit
```

## Querying (2.0 style)
```py
from sqlalchemy import select
stmt = select(User).where(User.email == 'a@b.c')
user = s.scalars(stmt).first()

stmt = select(User).join(User.posts).where(Post.title.ilike('%hello%'))
users = s.scalars(stmt).all()
```

## Eager loading (avoid N+1)
```py
from sqlalchemy.orm import selectinload, joinedload
select(User).options(selectinload(User.posts))
```

## Update / delete
```py
from sqlalchemy import update, delete
s.execute(update(User).where(User.id==1).values(email='x@y.z'))
s.execute(delete(Post).where(Post.user_id==1))
```

## Migrations
Use Alembic: `alembic init migrations`, `alembic revision --autogenerate -m "msg"`, `alembic upgrade head`.

API: /api/skills/sqlalchemy-orm