sqlmodel
SQLModel Development Guide
SQLModel combines SQLAlchemy and Pydantic into a single library - one model class serves as both ORM model and Pydantic schema.
Quick Start
Installation
pip install sqlmodel
Minimal Example
from sqlmodel import Field, SQLModel, Session, create_engine, select
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
age: int | None = None
engine = create_engine("sqlite:///database.db")
SQLModel.metadata.create_all(engine)
# Create
with Session(engine) as session:
hero = Hero(name="Spider-Boy", age=18)
session.add(hero)
session.commit()
session.refresh(hero)
# Read
with Session(engine) as session:
heroes = session.exec(select(Hero)).all()
Core Concepts
| Concept | Description |
|---|---|
table=True |
Makes class a database table (without it, it's just Pydantic) |
Field() |
Define column attributes: primary_key, index, unique, foreign_key |
Session |
Database session for CRUD operations |
select() |
Type-safe query builder |
Relationship |
Define relationships between models |
Model Patterns
Base Model (API Schema Only)
class HeroBase(SQLModel):
name: str
age: int | None = None
Table Model (Database)
class Hero(HeroBase, table=True):
id: int | None = Field(default=None, primary_key=True)
Request/Response Models
class HeroCreate(HeroBase):
secret_name: str
class HeroPublic(HeroBase):
id: int
class HeroUpdate(SQLModel):
name: str | None = None
age: int | None = None
CRUD Operations
Create
def create_hero(session: Session, hero: HeroCreate) -> Hero:
db_hero = Hero.model_validate(hero)
session.add(db_hero)
session.commit()
session.refresh(db_hero)
return db_hero
Read
def get_hero(session: Session, hero_id: int) -> Hero | None:
return session.get(Hero, hero_id)
def get_heroes(session: Session, skip: int = 0, limit: int = 100) -> list[Hero]:
return session.exec(select(Hero).offset(skip).limit(limit)).all()
Update
def update_hero(session: Session, hero_id: int, hero_update: HeroUpdate) -> Hero | None:
db_hero = session.get(Hero, hero_id)
if not db_hero:
return None
hero_data = hero_update.model_dump(exclude_unset=True)
db_hero.sqlmodel_update(hero_data)
session.add(db_hero)
session.commit()
session.refresh(db_hero)
return db_hero
Delete
def delete_hero(session: Session, hero_id: int) -> bool:
hero = session.get(Hero, hero_id)
if not hero:
return False
session.delete(hero)
session.commit()
return True
FastAPI Integration
Database Setup
from sqlmodel import SQLModel, Session, create_engine
DATABASE_URL = "sqlite:///./database.db"
engine = create_engine(DATABASE_URL, echo=True)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
def get_session():
with Session(engine) as session:
yield session
Dependency Injection
from typing import Annotated
from fastapi import Depends
SessionDep = Annotated[Session, Depends(get_session)]
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate, session: SessionDep):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
session.commit()
session.refresh(db_hero)
return db_hero
Lifespan Events
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
create_db_and_tables()
yield
app = FastAPI(lifespan=lifespan)
Reference Files
Load these based on the task at hand:
| Topic | File | When to Use |
|---|---|---|
| Models | models.md | Field options, validators, computed fields, inheritance, mixins |
| Relationships | relationships.md | One-to-many, many-to-many, self-referential, lazy loading |
| Async | async.md | Async sessions, async engine, background tasks |
| Migrations | migrations.md | Alembic setup, auto-generation, migration patterns |
Querying
Basic Queries
# All heroes
heroes = session.exec(select(Hero)).all()
# Single result (first or None)
hero = session.exec(select(Hero).where(Hero.name == "Spider-Boy")).first()
# Get by primary key
hero = session.get(Hero, 1)
Filtering
from sqlmodel import select, or_, and_
# Single condition
select(Hero).where(Hero.age >= 18)
# Multiple conditions (AND)
select(Hero).where(Hero.age >= 18, Hero.name == "Spider-Boy")
# OR conditions
select(Hero).where(or_(Hero.age < 18, Hero.age > 60))
# LIKE/contains
select(Hero).where(Hero.name.contains("Spider"))
Ordering and Pagination
select(Hero).order_by(Hero.name)
select(Hero).order_by(Hero.age.desc())
select(Hero).offset(10).limit(5)
Best Practices
- Separate table models from API schemas - Use
table=Trueonly for actual DB tables - Use
model_validate()for conversion - Convert between schemas and table models - Use
sqlmodel_update()for partial updates - Passexclude_unset=Truetomodel_dump() - Always use
Field()for constraints - Primary keys, indexes, foreign keys, defaults - Use
Annotateddependencies - Clean, reusable session injection - Use lifespan for table creation - Not deprecated
@app.on_event - Index frequently queried columns -
Field(index=True) - Use
echo=Trueduring development - See generated SQL queries
Common Issues
| Issue | Solution |
|---|---|
Missing table=True |
Add table=True to models that need DB tables |
| Circular imports | Use TYPE_CHECKING and string annotations for relationships |
| Session already closed | Ensure session is still open when accessing lazy-loaded relationships |
| Migration not detecting changes | Use compare_type=True in Alembic env.py |
More from salmanferozkhan/cloud-and-fast-api
chainlit
Expert guidance for building conversational AI applications with Chainlit framework in Python. Use when (1) creating chat interfaces for LLM applications, (2) building apps with OpenAI, LangChain, LlamaIndex, or Mistral AI, (3) implementing streaming responses, (4) adding UI elements like images, files, charts, (5) handling user file uploads, (6) implementing authentication (OAuth, password), (7) creating multi-step workflows with visible steps, (8) building RAG applications with document upload, or (9) deploying chat apps to web, Slack, Discord, or Teams.
87fastapi
Expert guidance for building REST APIs with FastAPI framework in Python. Use when (1) creating new FastAPI projects from scratch, (2) implementing API endpoints with routing, (3) working with Pydantic models for validation, (4) setting up dependency injection, (5) implementing authentication (OAuth2, JWT, API keys), (6) integrating databases (SQLAlchemy sync/async), (7) writing tests for FastAPI apps, (8) deploying FastAPI to production (Docker, Gunicorn), or (9) implementing advanced features like WebSockets, middleware, background tasks.
5microsoft-agent-framework
Expert guidance for building AI agents and multi-agent workflows using Microsoft Agent Framework for .NET. Use when (1) creating AI agents with OpenAI or Azure OpenAI, (2) implementing function tools and structured outputs, (3) building multi-turn conversations, (4) designing graph-based workflows with streaming/checkpointing, (5) implementing middleware pipelines, (6) orchestrating multi-agent systems with fan-out/fan-in patterns, (7) adding human-in-the-loop interactions, (8) integrating OpenTelemetry observability, or (9) exposing agents as MCP tools.
3docx
Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction. When Claude needs to work with professional documents (.docx files) for: (1) Creating new documents, (2) Modifying or editing content, (3) Working with tracked changes, (4) Adding comments, or any other document tasks
3xlsx
Comprehensive spreadsheet creation, editing, and analysis with support for formulas, formatting, data analysis, and visualization. When Claude needs to work with spreadsheets (.xlsx, .xlsm, .csv, .tsv, etc) for: (1) Creating new spreadsheets with formulas and formatting, (2) Reading or analyzing data, (3) Modify existing spreadsheets while preserving formulas, (4) Data analysis and visualization in spreadsheets, or (5) Recalculating formulas
3skill-validator
|
3