sqlmodel
1
总安装量
1
周安装量
#47103
全站排名
安装命令
npx skills add https://github.com/salmanferozkhan/cloud-and-fast-api --skill sqlmodel
Agent 安装分布
cline
1
openclaw
1
trae
1
qoder
1
trae-cn
1
opencode
1
Skill 文档
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 |