Claude Code for SQLModel: SQLAlchemy and Pydantic ORM for FastAPI — Claude Skills 360 Blog
Blog / AI / Claude Code for SQLModel: SQLAlchemy and Pydantic ORM for FastAPI
AI

Claude Code for SQLModel: SQLAlchemy and Pydantic ORM for FastAPI

Published: May 9, 2028
Read time: 5 min read
By: Claude Skills 360

SQLModel combines SQLAlchemy and Pydantic for type-safe ORM models. pip install sqlmodel. Model: from sqlmodel import SQLModel, Field; class User(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True); name: str. Engine: from sqlmodel import create_engine; engine = create_engine("sqlite:///db.sqlite3"). Create tables: SQLModel.metadata.create_all(engine). Session: from sqlmodel import Session; with Session(engine) as s: s.add(user); s.commit(). Select: from sqlmodel import select; s.exec(select(User).where(User.name == "Alice")).first(). All: s.exec(select(User)).all(). Get by PK: s.get(User, 1). Update: user.name = "Bob"; s.add(user); s.commit(); s.refresh(user). Delete: s.delete(user); s.commit(). Field options: Field(default=None, primary_key=True), Field(index=True), Field(unique=True), Field(nullable=False). Relationship: from sqlmodel import Relationship; class Hero(SQLModel, table=True): team_id: int | None = Field(default=None, foreign_key="team.id"); team: "Team" = Relationship(back_populates="heroes"). Validator: Pydantic validators work on SQLModel. Async: from sqlmodel.ext.asyncio.session import AsyncSession; from sqlalchemy.ext.asyncio import create_async_engine. engine = create_async_engine("sqlite+aiosqlite:///db.sqlite3"). FastAPI: inject SessionDep = Annotated[Session, Depends(get_session)]. Claude Code generates SQLModel CRUD services, FastAPI database dependencies, and async session patterns.

CLAUDE.md for SQLModel

## SQLModel Stack
- Version: sqlmodel >= 0.0.19 | pip install sqlmodel sqlalchemy
- Model: class User(SQLModel, table=True): id: int|None = Field(primary_key=True)
- Engine: create_engine("sqlite:///db.sqlite3") | postgres: postgresql://...
- CRUD: with Session(engine) as s: s.add(obj); s.commit(); s.refresh(obj)
- Query: s.exec(select(Model).where(Model.col == val)).all()
- Async: create_async_engine + AsyncSession for FastAPI async endpoints

SQLModel CRUD Pipeline

# app/db_sqlmodel.py — SQLModel models, CRUD, relationships, and FastAPI integration
from __future__ import annotations

from datetime import datetime
from typing import Optional, Generator, AsyncGenerator

from sqlalchemy import event
from sqlmodel import (
    Field,
    Relationship,
    Session,
    SQLModel,
    create_engine,
    select,
)


# ─────────────────────────────────────────────────────────────────────────────
# 1. Models
# ─────────────────────────────────────────────────────────────────────────────

class TeamBase(SQLModel):
    name:     str = Field(index=True)
    division: str | None = None


class Team(TeamBase, table=True):
    id: int | None = Field(default=None, primary_key=True)
    heroes: list["Hero"] = Relationship(back_populates="team")


class TeamCreate(TeamBase):
    pass


class TeamRead(TeamBase):
    id: int


class HeroBase(SQLModel):
    name:    str   = Field(index=True)
    secret_name: str
    age:     int | None = None
    active:  bool  = Field(default=True)


class Hero(HeroBase, table=True):
    id:      int | None = Field(default=None, primary_key=True)
    team_id: int | None = Field(default=None, foreign_key="team.id", index=True)
    created_at: datetime = Field(default_factory=datetime.utcnow)
    team:    Optional[Team] = Relationship(back_populates="heroes")


class HeroCreate(HeroBase):
    team_id: int | None = None


class HeroRead(HeroBase):
    id:         int
    team_id:    int | None
    created_at: datetime


class HeroUpdate(SQLModel):
    name:        str | None = None
    secret_name: str | None = None
    age:         int | None = None
    active:      bool | None = None
    team_id:     int | None = None


# ─────────────────────────────────────────────────────────────────────────────
# 2. Engine and session factory
# ─────────────────────────────────────────────────────────────────────────────

def make_engine(url: str = "sqlite:///./heroes.db", echo: bool = False):
    """
    Create a SQLAlchemy engine.
    url: SQLAlchemy connection string.
         SQLite:     "sqlite:///./app.db"
         PostgreSQL: "postgresql+psycopg2://user:pass@host/db"
         MySQL:      "mysql+pymysql://user:pass@host/db"
    """
    engine = create_engine(url, echo=echo)
    # Enable WAL mode for SQLite
    if url.startswith("sqlite"):
        @event.listens_for(engine, "connect")
        def set_sqlite_pragma(dbapi_connection, connection_record):
            cursor = dbapi_connection.cursor()
            cursor.execute("PRAGMA journal_mode=WAL")
            cursor.execute("PRAGMA foreign_keys=ON")
            cursor.close()
    return engine


def init_db(engine) -> None:
    """Create all tables."""
    SQLModel.metadata.create_all(engine)


def get_session(engine) -> Generator[Session, None, None]:
    """
    FastAPI dependency: yields a Session for the request lifecycle.

    Usage:
        @app.get("/heroes")
        def list_heroes(session: Session = Depends(get_session(engine))):
            ...
    """
    with Session(engine) as session:
        yield session


# ─────────────────────────────────────────────────────────────────────────────
# 3. CRUD operations
# ─────────────────────────────────────────────────────────────────────────────

class HeroService:
    """
    Service layer for Hero CRUD using SQLModel.

    Usage:
        svc = HeroService(session)
        hero = svc.create(HeroCreate(name="Spider-Man", secret_name="Peter Parker"))
        heroes = svc.list_active(limit=10)
    """

    def __init__(self, session: Session) -> None:
        self._s = session

    # --- Create ---

    def create(self, data: HeroCreate) -> Hero:
        hero = Hero.model_validate(data)
        self._s.add(hero)
        self._s.commit()
        self._s.refresh(hero)
        return hero

    def create_many(self, items: list[HeroCreate]) -> list[Hero]:
        heroes = [Hero.model_validate(d) for d in items]
        self._s.add_all(heroes)
        self._s.commit()
        for h in heroes:
            self._s.refresh(h)
        return heroes

    # --- Read ---

    def get(self, hero_id: int) -> Hero | None:
        return self._s.get(Hero, hero_id)

    def get_or_404(self, hero_id: int) -> Hero:
        hero = self.get(hero_id)
        if hero is None:
            raise KeyError(f"Hero {hero_id} not found")
        return hero

    def list_all(self, limit: int = 100, offset: int = 0) -> list[Hero]:
        return self._s.exec(
            select(Hero).offset(offset).limit(limit)
        ).all()

    def list_active(self, limit: int = 100) -> list[Hero]:
        return self._s.exec(
            select(Hero).where(Hero.active == True).limit(limit)
        ).all()

    def find_by_name(self, name: str) -> Hero | None:
        return self._s.exec(
            select(Hero).where(Hero.name == name)
        ).first()

    def by_team(self, team_id: int) -> list[Hero]:
        return self._s.exec(
            select(Hero).where(Hero.team_id == team_id)
        ).all()

    def count(self, active_only: bool = False) -> int:
        from sqlmodel import func
        stmt = select(func.count()).select_from(Hero)
        if active_only:
            stmt = stmt.where(Hero.active == True)
        return self._s.exec(stmt).one()

    # --- Update ---

    def update(self, hero_id: int, data: HeroUpdate) -> Hero:
        hero = self.get_or_404(hero_id)
        update_data = data.model_dump(exclude_unset=True)
        for field, value in update_data.items():
            setattr(hero, field, value)
        self._s.add(hero)
        self._s.commit()
        self._s.refresh(hero)
        return hero

    # --- Delete ---

    def delete(self, hero_id: int) -> bool:
        hero = self.get(hero_id)
        if hero is None:
            return False
        self._s.delete(hero)
        self._s.commit()
        return True


class TeamService:
    def __init__(self, session: Session) -> None:
        self._s = session

    def create(self, data: TeamCreate) -> Team:
        team = Team.model_validate(data)
        self._s.add(team)
        self._s.commit()
        self._s.refresh(team)
        return team

    def get(self, team_id: int) -> Team | None:
        return self._s.get(Team, team_id)

    def list_all(self) -> list[Team]:
        return self._s.exec(select(Team)).all()


# ─────────────────────────────────────────────────────────────────────────────
# 4. FastAPI integration
# ─────────────────────────────────────────────────────────────────────────────

FASTAPI_EXAMPLE = '''
from typing import Annotated
from fastapi import FastAPI, Depends, HTTPException
from sqlmodel import Session, create_engine, SQLModel
from app.db_sqlmodel import (
    Hero, HeroCreate, HeroRead, HeroUpdate,
    HeroService, init_db, get_session,
)

engine = create_engine("sqlite:///./heroes.db")
init_db(engine)

app = FastAPI()
SessionDep = Annotated[Session, Depends(lambda: get_session(engine))]

@app.post("/heroes/", response_model=HeroRead)
def create_hero(hero: HeroCreate, session: SessionDep):
    return HeroService(session).create(hero)

@app.get("/heroes/", response_model=list[HeroRead])
def list_heroes(session: SessionDep, limit: int = 20, offset: int = 0):
    return HeroService(session).list_all(limit=limit, offset=offset)

@app.get("/heroes/{hero_id}", response_model=HeroRead)
def get_hero(hero_id: int, session: SessionDep):
    hero = HeroService(session).get(hero_id)
    if not hero:
        raise HTTPException(status_code=404, detail="Hero not found")
    return hero

@app.patch("/heroes/{hero_id}", response_model=HeroRead)
def update_hero(hero_id: int, data: HeroUpdate, session: SessionDep):
    return HeroService(session).update(hero_id, data)

@app.delete("/heroes/{hero_id}")
def delete_hero(hero_id: int, session: SessionDep):
    if not HeroService(session).delete(hero_id):
        raise HTTPException(status_code=404, detail="Hero not found")
    return {"ok": True}
'''


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    engine = make_engine("sqlite:///./demo_heroes.db")
    init_db(engine)

    with Session(engine) as s:
        team_svc = TeamService(s)
        hero_svc = HeroService(s)

        print("=== Create teams ===")
        avengers = team_svc.create(TeamCreate(name="Avengers", division="Alpha"))
        xmen     = team_svc.create(TeamCreate(name="X-Men",    division="Beta"))
        print(f"  {avengers.id}: {avengers.name}")

        print("\n=== Create heroes ===")
        hero_svc.create_many([
            HeroCreate(name="Spider-Man",  secret_name="Peter Parker", age=28, team_id=avengers.id),
            HeroCreate(name="Iron Man",    secret_name="Tony Stark",   age=45, team_id=avengers.id),
            HeroCreate(name="Wolverine",   secret_name="Logan",        age=200,team_id=xmen.id),
            HeroCreate(name="Storm",       secret_name="Ororo Munroe", age=35, team_id=xmen.id),
        ])

        print("\n=== List all ===")
        for h in hero_svc.list_all():
            print(f"  [{h.id}] {h.name} (team_id={h.team_id})")

        print("\n=== Find by name ===")
        sm = hero_svc.find_by_name("Spider-Man")
        print(f"  Found: {sm.name}, age={sm.age}")

        print("\n=== Update ===")
        hero_svc.update(sm.id, HeroUpdate(age=29))
        sm_updated = hero_svc.get(sm.id)
        print(f"  Updated age: {sm_updated.age}")

        print("\n=== By team ===")
        avengers_heroes = hero_svc.by_team(avengers.id)
        print(f"  Avengers: {[h.name for h in avengers_heroes]}")

        print(f"\n=== Count: {hero_svc.count()} heroes ===")

For the SQLAlchemy + Pydantic separately alternative — using SQLAlchemy Core/ORM for models and Pydantic separately requires maintaining two parallel class hierarchies (one for DB, one for API validation); SQLModel unifies them with table=True models that are both SQLAlchemy models and Pydantic schemas, eliminating the need for .from_orm() or manual field duplication. For the tortoise-orm alternative — tortoise-orm uses async with transactions and is built for async-first designs with Model.create() / Model.filter(); SQLModel wraps SQLAlchemy giving access to the full SQLAlchemy ecosystem (Alembic migrations, Core expressions, connection pooling) — SQLModel is the recommended ORM when building FastAPI applications where Alembic migrations and Pydantic validation are important. The Claude Skills 360 bundle includes SQLModel skill sets covering Hero/Team models with SQLModel base classes, Field() with primary_key/index/foreign_key/default, HeroCreate/HeroRead/HeroUpdate split schemas, make_engine()/init_db()/get_session() setup, HeroService/TeamService CRUD classes, FastAPI Depends(get_session) dependency injection, select().where().offset().limit() queries, and async engine + AsyncSession configuration. Start with the free tier to try SQLModel ORM code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free