sqlmodel
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSQLModel Development Guide
SQLModel 开发指南
SQLModel combines SQLAlchemy and Pydantic into a single library - one model class serves as both ORM model and Pydantic schema.
SQLModel 将 SQLAlchemy 和 Pydantic 整合到一个库中——一个模型类可同时作为 ORM 模型和 Pydantic 模式使用。
Quick Start
快速开始
Installation
安装
bash
pip install sqlmodelbash
pip install sqlmodelMinimal Example
最简示例
python
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)python
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)
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()
undefinedwith Session(engine) as session:
heroes = session.exec(select(Hero)).all()
undefinedCore Concepts
核心概念
| Concept | Description |
|---|---|
| Makes class a database table (without it, it's just Pydantic) |
| Define column attributes: |
| Database session for CRUD operations |
| Type-safe query builder |
| Define relationships between models |
| 概念 | 描述 |
|---|---|
| 将类设为数据库表(不添加该参数时,仅作为 Pydantic 模式) |
| 定义列属性: |
| 用于 CRUD 操作的数据库会话 |
| 类型安全的查询构建器 |
| 定义模型之间的关系 |
Model Patterns
模型模式
Base Model (API Schema Only)
基础模型(仅 API 模式)
python
class HeroBase(SQLModel):
name: str
age: int | None = Nonepython
class HeroBase(SQLModel):
name: str
age: int | None = NoneTable Model (Database)
表模型(数据库)
python
class Hero(HeroBase, table=True):
id: int | None = Field(default=None, primary_key=True)python
class Hero(HeroBase, table=True):
id: int | None = Field(default=None, primary_key=True)Request/Response Models
请求/响应模型
python
class HeroCreate(HeroBase):
secret_name: str
class HeroPublic(HeroBase):
id: int
class HeroUpdate(SQLModel):
name: str | None = None
age: int | None = Nonepython
class HeroCreate(HeroBase):
secret_name: str
class HeroPublic(HeroBase):
id: int
class HeroUpdate(SQLModel):
name: str | None = None
age: int | None = NoneCRUD Operations
CRUD 操作
Create
创建
python
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_heropython
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_heroRead
读取
python
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()python
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
更新
python
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_heropython
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_heroDelete
删除
python
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 Truepython
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 TrueFastAPI Integration
FastAPI 集成
Database Setup
数据库设置
python
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 sessionpython
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 sessionDependency Injection
依赖注入
python
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_heropython
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_heroLifespan Events
生命周期事件
python
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
create_db_and_tables()
yield
app = FastAPI(lifespan=lifespan)python
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 |
根据任务需求加载以下文档:
| 主题 | 文件 | 使用场景 |
|---|---|---|
| 模型 | models.md | 字段选项、验证器、计算字段、继承、混合类 |
| 关系 | relationships.md | 一对多、多对多、自引用、延迟加载 |
| 异步 | async.md | 异步会话、异步引擎、后台任务 |
| 迁移 | migrations.md | Alembic 配置、自动生成、迁移模式 |
Querying
查询
Basic Queries
基础查询
python
undefinedpython
undefinedAll heroes
所有英雄
heroes = session.exec(select(Hero)).all()
heroes = session.exec(select(Hero)).all()
Single result (first or None)
单个结果(第一个或空)
hero = session.exec(select(Hero).where(Hero.name == "Spider-Boy")).first()
hero = session.exec(select(Hero).where(Hero.name == "Spider-Boy")).first()
Get by primary key
通过主键获取
hero = session.get(Hero, 1)
undefinedhero = session.get(Hero, 1)
undefinedFiltering
过滤
python
from sqlmodel import select, or_, and_python
from sqlmodel import select, or_, and_Single condition
单个条件
select(Hero).where(Hero.age >= 18)
select(Hero).where(Hero.age >= 18)
Multiple conditions (AND)
多个条件(AND)
select(Hero).where(Hero.age >= 18, Hero.name == "Spider-Boy")
select(Hero).where(Hero.age >= 18, Hero.name == "Spider-Boy")
OR conditions
OR 条件
select(Hero).where(or_(Hero.age < 18, Hero.age > 60))
select(Hero).where(or_(Hero.age < 18, Hero.age > 60))
LIKE/contains
LIKE/包含
select(Hero).where(Hero.name.contains("Spider"))
undefinedselect(Hero).where(Hero.name.contains("Spider"))
undefinedOrdering and Pagination
排序和分页
python
select(Hero).order_by(Hero.name)
select(Hero).order_by(Hero.age.desc())
select(Hero).offset(10).limit(5)python
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 only for actual DB tables
table=True - Use for conversion - Convert between schemas and table models
model_validate() - Use for partial updates - Pass
sqlmodel_update()toexclude_unset=Truemodel_dump() - Always use for constraints - Primary keys, indexes, foreign keys, defaults
Field() - Use dependencies - Clean, reusable session injection
Annotated - Use lifespan for table creation - Not deprecated
@app.on_event - Index frequently queried columns -
Field(index=True) - Use during development - See generated SQL queries
echo=True
- 分离表模型与 API 模式 - 仅为实际数据库表添加
table=True - 使用 进行转换 - 实现模式与表模型之间的转换
model_validate() - 使用 进行部分更新 - 传递
sqlmodel_update()给exclude_unset=Truemodel_dump() - 始终使用 定义约束 - 主键、索引、外键、默认值
Field() - 使用 依赖项 - 简洁、可复用的会话注入
Annotated - 使用生命周期事件创建表 - 替代已弃用的
@app.on_event - 为频繁查询的列添加索引 - 使用
Field(index=True) - 开发阶段启用 - 查看生成的 SQL 查询
echo=True
Common Issues
常见问题
| Issue | Solution |
|---|---|
Missing | Add |
| Circular imports | Use |
| Session already closed | Ensure session is still open when accessing lazy-loaded relationships |
| Migration not detecting changes | Use |
| 问题 | 解决方案 |
|---|---|
缺少 | 为需要数据库表的模型添加 |
| 循环导入 | 对于关系,使用 |
| 会话已关闭 | 访问延迟加载的关系时确保会话仍处于打开状态 |
| 迁移未检测到变更 | 在 Alembic 的 env.py 中设置 |