使用SQLModel和FastAPI构建RESTful API教程
还在为构建RESTful API时需要在数据库模型和API数据模型之间反复转换而烦恼吗?SQLModel和FastAPI的完美结合将彻底改变你的开发体验!本文将带你从零开始,构建一个功能完整的英雄管理API,涵盖CRUD操作、数据验证、依赖注入等核心功能。
🎯 你将学到什么
- SQLModel和FastAPI的集成原理
- 完整的CRUD(增删改查)API实现
- 数据库会话管理和依赖注入
- 数据验证和错误处理
- 分页查询和过滤功能
- 自动化API文档生成
📦 环境准备和安装
首先确保你已安装Python 3.7+,然后创建虚拟环境并安装必要的依赖:
# 创建虚拟环境
python -m venv venv
# 启用虚拟环境(Windows)
venv\Scripts\activate
# 启用虚拟环境(macOS/Linux)
source venv/bin/activate
# 安装核心依赖
pip install fastapi "uvicorn[standard]" sqlmodel
🏗️ 项目结构设计
让我们先规划一个清晰的项目结构:
heroes_api/
├── main.py # FastAPI应用入口
├── models.py # 数据模型定义
├── database.py # 数据库配置
├── dependencies.py # 依赖注入
└── crud.py # 业务逻辑
🎭 数据模型定义
SQLModel的强大之处在于它结合了SQLAlchemy的ORM功能和Pydantic的数据验证能力。让我们定义一个英雄模型:
# models.py
from sqlmodel import Field, SQLModel
from typing import Optional
from datetime import datetime
class HeroBase(SQLModel):
name: str = Field(index=True, max_length=100)
secret_name: str = Field(max_length=100)
age: Optional[int] = Field(default=None, index=True)
created_at: datetime = Field(default_factory=datetime.utcnow)
class Hero(HeroBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
class HeroCreate(HeroBase):
pass
class HeroRead(HeroBase):
id: int
created_at: datetime
class HeroUpdate(SQLModel):
name: Optional[str] = None
secret_name: Optional[str] = None
age: Optional[int] = None
这个模型设计体现了SQLModel的核心优势:
Hero是数据库表模型,包含所有ORM功能HeroCreate用于创建操作的输入验证HeroRead用于读取操作的输出序列化HeroUpdate用于更新操作的部分更新支持
🗄️ 数据库配置
# database.py
from sqlmodel import create_engine, SQLModel, Session
from typing import Generator
# SQLite数据库配置
sqlite_url = "sqlite:///./heroes.db"
connect_args = {"check_same_thread": False}
engine = create_engine(sqlite_url, echo=True, connect_args=connect_args)
def create_db_and_tables():
"""创建数据库和表结构"""
SQLModel.metadata.create_all(engine)
def get_session() -> Generator[Session, None, None]:
"""获取数据库会话的依赖函数"""
with Session(engine) as session:
yield session
🔧 依赖注入配置
# dependencies.py
from fastapi import Depends
from sqlmodel import Session
from database import get_session
def get_db_session(session: Session = Depends(get_session)):
"""数据库会话依赖注入"""
return session
🚀 FastAPI应用搭建
现在让我们创建主应用文件:
# main.py
from fastapi import FastAPI, Depends, HTTPException, Query
from sqlmodel import Session, select
from typing import List, Optional
import models
import database
from dependencies import get_db_session
# 创建FastAPI应用实例
app = FastAPI(
title="Heroes API",
description="一个基于SQLModel和FastAPI的英雄管理API",
version="1.0.0"
)
@app.on_event("startup")
def on_startup():
"""应用启动时创建数据库表"""
database.create_db_and_tables()
@app.post("/heroes/", response_model=models.HeroRead)
def create_hero(
hero: models.HeroCreate,
session: Session = Depends(get_db_session)
):
"""创建新英雄"""
db_hero = models.Hero.model_validate(hero)
session.add(db_hero)
session.commit()
session.refresh(db_hero)
return db_hero
@app.get("/heroes/", response_model=List[models.HeroRead])
def read_heroes(
offset: int = 0,
limit: int = Query(default=100, le=100),
session: Session = Depends(get_db_session)
):
"""获取英雄列表(支持分页)"""
heroes = session.exec(
select(models.Hero).offset(offset).limit(limit)
).all()
return heroes
@app.get("/heroes/{hero_id}", response_model=models.HeroRead)
def read_hero(
hero_id: int,
session: Session = Depends(get_db_session)
):
"""根据ID获取单个英雄"""
hero = session.get(models.Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero
@app.patch("/heroes/{hero_id}", response_model=models.HeroRead)
def update_hero(
hero_id: int,
hero: models.HeroUpdate,
session: Session = Depends(get_db_session)
):
"""更新英雄信息"""
db_hero = session.get(models.Hero, hero_id)
if not db_hero:
raise HTTPException(status_code=404, detail="Hero not found")
hero_data = hero.model_dump(exclude_unset=True)
for key, value in hero_data.items():
setattr(db_hero, key, value)
session.add(db_hero)
session.commit()
session.refresh(db_hero)
return db_hero
@app.delete("/heroes/{hero_id}")
def delete_hero(
hero_id: int,
session: Session = Depends(get_db_session)
):
"""删除英雄"""
hero = session.get(models.Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
session.delete(hero)
session.commit()
return {"message": "Hero deleted successfully"}
@app.get("/heroes/search/", response_model=List[models.HeroRead])
def search_heroes(
name: Optional[str] = None,
min_age: Optional[int] = None,
max_age: Optional[int] = None,
session: Session = Depends(get_db_session)
):
"""搜索英雄(支持名称和年龄范围查询)"""
query = select(models.Hero)
if name:
query = query.where(models.Hero.name.contains(name))
if min_age is not None:
query = query.where(models.Hero.age >= min_age)
if max_age is not None:
query = query.where(models.Hero.age <= max_age)
heroes = session.exec(query).all()
return heroes
📊 API功能特性说明
让我们通过表格来详细了解这个API的功能特性:
| 功能 | HTTP方法 | 路径 | 描述 | 参数 |
|---|---|---|---|---|
| 创建英雄 | POST | /heroes/ | 创建新英雄 | HeroCreate模型 |
| 获取列表 | GET | /heroes/ | 获取英雄列表 | offset, limit |
| 获取详情 | GET | /heroes/{id} | 根据ID获取英雄 | hero_id |
| 更新英雄 | PATCH | /heroes/{id} | 部分更新英雄 | hero_id, HeroUpdate |
| 删除英雄 | DELETE | /heroes/{id} | 删除英雄 | hero_id |
| 搜索英雄 | GET | /heroes/search/ | 条件搜索英雄 | name, min_age, max_age |
🧪 测试API接口
启动开发服务器:
fastapi dev main.py
访问 http://127.0.0.1:8000/docs 查看自动生成的交互式API文档。
创建英雄示例
curl -X POST "http://127.0.0.1:8000/heroes/" \
-H "Content-Type: application/json" \
-d '{
"name": "钢铁侠",
"secret_name": "托尼·斯塔克",
"age": 48
}'
获取英雄列表示例
curl -X GET "http://127.0.0.1:8000/heroes/?offset=0&limit=10"
搜索英雄示例
curl -X GET "http://127.0.0.1:8000/heroes/search/?name=钢铁&min_age=30"
🔍 高级功能扩展
1. 关系模型支持
SQLModel支持复杂的关系模型,让我们添加团队功能:
# models.py - 添加团队模型
class Team(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class HeroWithTeam(Hero):
team: Optional[Team] = None
# 在Hero模型中添加团队关系
class Hero(HeroBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
team_id: Optional[int] = Field(default=None, foreign_key="team.id")
2. 异步支持
SQLModel也支持异步操作:
# database.py - 异步数据库配置
from sqlmodel.ext.asyncio.session import AsyncSession
from sqlmodel import create_engine
from sqlalchemy.ext.asyncio import create_async_engine
async_engine = create_async_engine(
"sqlite+aiosqlite:///./heroes.db",
echo=True,
connect_args={"check_same_thread": False}
)
async def get_async_session() -> AsyncSession:
async with AsyncSession(async_engine) as session:
yield session
3. 错误处理中间件
# main.py - 添加全局错误处理
from fastapi import Request
from fastapi.responses import JSONResponse
@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
return JSONResponse(
status_code=exc.status_code,
content={"detail": exc.detail}
)
@app.exception_handler(Exception)
async def general_exception_handler(request: Request, exc: Exception):
return JSONResponse(
status_code=500,
content={"detail": "Internal server error"}
)
📈 性能优化建议
- 数据库连接池:配置合适的连接池大小
- 索引优化:为常用查询字段添加索引
- 查询优化:使用selectinload避免N+1查询问题
- 缓存策略:对频繁读取的数据添加缓存层
🎯 总结
通过本教程,你已经学会了:
✅ 如何使用SQLModel定义兼具ORM和数据验证功能的模型 ✅ 如何配置FastAPI与SQLModel的集成 ✅ 实现完整的CRUD RESTful API ✅ 使用依赖注入管理数据库会话 ✅ 实现分页、搜索等高级功能 ✅ 生成自动化API文档
SQLModel和FastAPI的组合为Python开发者提供了极佳的开发体验,让你能够快速构建高性能、类型安全的Web API。这种组合特别适合需要快速原型开发和保持代码质量的场景。
🚀 下一步行动
- 尝试添加身份验证和授权功能
- 实现文件上传和下载功能
- 添加单元测试和集成测试
- 部署到生产环境(Docker、Kubernetes等)
- 实现监控和日志记录
现在就开始使用SQLModel和FastAPI构建你的下一个API项目吧!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



