简介:本文详细讲解如何使用 FastAPI 快速开发一个完整的待办事项 Web API 项目,涵盖路由设计、CRUD 操作实现、数据验证及项目结构优化,助力开发者高效构建 RESTful 服务。
FastAPI 作为现代 Python Web 框架的代表,凭借其高性能、自动生成文档和异步支持等特性,已成为开发 RESTful API 的首选工具。本文将以待办事项(Todo)管理为例,深入讲解如何使用 FastAPI 实现完整的增删改查(CRUD)路由,覆盖从项目初始化到功能实现的完整流程。
开发 FastAPI 项目前,需确保 Python 版本 ≥3.7(推荐 3.9+),并通过 pip 安装核心依赖:
pip install fastapi uvicorn[standard] pydantic sqlalchemy
fastapi:核心框架uvicorn:ASGI 服务器pydantic:数据验证与序列化sqlalchemy:ORM 工具(可选,用于数据库操作)合理的项目结构能提升代码可维护性。推荐采用以下分层架构:
todo_api/├── main.py # 应用入口├── models/ # 数据模型│ └── todo.py # Todo 模型定义├── schemas/ # 数据验证 schema│ └── todo.py # Todo 请求/响应模型├── routers/ # 路由模块│ └── todos.py # Todo 相关路由├── database.py # 数据库连接(可选)└── requirements.txt # 依赖清单
from sqlalchemy import Column, Integer, String, Booleanfrom sqlalchemy.ext.declarative import declarative_baseBase = declarative_base()class Todo(Base):__tablename__ = "todos"id = Column(Integer, primary_key=True, index=True)title = Column(String, index=True)description = Column(String)completed = Column(Boolean, default=False)
from pydantic import BaseModelclass TodoBase(BaseModel):title: strdescription: str = Nonecompleted: bool = Falseclass TodoCreate(TodoBase):passclass TodoUpdate(TodoBase):passclass Todo(TodoBase):id: intclass Config:orm_mode = True # 允许从 ORM 对象转换
from fastapi import APIRouter, Depends, HTTPExceptionfrom sqlalchemy.orm import Sessionfrom typing import Listfrom ..database import get_db # 假设已实现数据库会话工厂from ..models.todo import Todo as TodoModelfrom ..schemas.todo import TodoCreate, TodoUpdate, Todo as TodoSchemarouter = APIRouter()
@router.post("/", response_model=TodoSchema)def create_todo(todo: TodoCreate,db: Session = Depends(get_db)):db_todo = TodoModel(**todo.dict())db.add(db_todo)db.commit()db.refresh(db_todo)return db_todo
关键点:
TodoCreate Schema 验证输入数据TodoSchema 对象获取所有待办事项:
@router.get("/", response_model=List[TodoSchema])def read_todos(db: Session = Depends(get_db),skip: int = 0,limit: int = 100):return db.query(TodoModel).offset(skip).limit(limit).all()
获取单个待办事项:
@router.get("/{todo_id}", response_model=TodoSchema)def read_todo(todo_id: int,db: Session = Depends(get_db)):db_todo = db.query(TodoModel).filter(TodoModel.id == todo_id).first()if db_todo is None:raise HTTPException(status_code=404, detail="Todo not found")return db_todo
@router.put("/{todo_id}", response_model=TodoSchema)def update_todo(todo_id: int,todo_update: TodoUpdate,db: Session = Depends(get_db)):db_todo = db.query(TodoModel).filter(TodoModel.id == todo_id).first()if db_todo is None:raise HTTPException(status_code=404, detail="Todo not found")update_data = todo_update.dict(exclude_unset=True)for key, value in update_data.items():setattr(db_todo, key, value)db.commit()db.refresh(db_todo)return db_todo
优化点:
exclude_unset=True 忽略未提供的字段setattr 动态更新属性
@router.delete("/{todo_id}")def delete_todo(todo_id: int,db: Session = Depends(get_db)):db_todo = db.query(TodoModel).filter(TodoModel.id == todo_id).first()if db_todo is None:raise HTTPException(status_code=404, detail="Todo not found")db.delete(db_todo)db.commit()return {"message": "Todo deleted successfully"}
from fastapi import FastAPIfrom .routers import todosapp = FastAPI()# 包含子路由app.include_router(todos.router, prefix="/todos", tags=["todos"])@app.get("/")def read_root():return {"message": "Welcome to the Todo API"}
FastAPI 原生支持异步路由,可提升 I/O 密集型操作性能:
from sqlalchemy.ext.asyncio import AsyncSessionfrom .database import get_async_db # 异步数据库会话@router.post("/async/", response_model=TodoSchema)async def create_todo_async(todo: TodoCreate,db: AsyncSession = Depends(get_async_db)):db_todo = TodoModel(**todo.dict())db.add(db_todo)await db.commit()await db.refresh(db_todo)return db_todo
增强读取接口的灵活性:
from fastapi import Query@router.get("/", response_model=List[TodoSchema])def read_todos(db: Session = Depends(get_db),skip: int = Query(0, ge=0),limit: int = Query(100, le=500),sort_by: str = Query("id", description="Field to sort by"),sort_order: str = Query("asc", description="Sort order (asc/desc)")):query = db.query(TodoModel)# 动态排序if sort_order == "desc":query = query.order_by(getattr(TodoModel, sort_by).desc())else:query = query.order_by(getattr(TodoModel, sort_by).asc())return query.offset(skip).limit(limit).all()
自定义异常处理器:
from fastapi import Requestfrom fastapi.responses import JSONResponsefrom fastapi.exceptions import HTTPException@app.exception_handler(HTTPException)async def http_exception_handler(request: Request, exc: HTTPException):return JSONResponse(status_code=exc.status_code,content={"message": exc.detail},)
使用 pytest 和 httpx 测试 API:
import pytestfrom httpx import AsyncClientfrom main import app@pytest.mark.anyioasync def test_create_todo():async with AsyncClient(app=app, base_url="http://test") as ac:response = await ac.post("/todos/", json={"title": "Test Todo","description": "Test Description"})assert response.status_code == 200assert response.json()["title"] == "Test Todo"
uvicorn 或 gunicorn + uvicorn 工作模式systemd 或 Docker 容器化部署
location / {proxy_pass http://127.0.0.1:8000;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;}
本文通过待办事项管理案例,系统展示了 FastAPI 实现 CRUD 路由的核心方法。关键收获包括:
Depends 管理数据库会话等共享资源扩展方向:
FastAPI 的简洁设计与强大功能,使其成为构建现代 Web API 的理想选择。通过本文的实践,开发者可快速掌握从路由设计到生产部署的全流程,为后续复杂项目开发奠定坚实基础。