logo

FastAPI 实战:从零构建待办事项 API 的 CRUD 操作指南

作者:沙与沫2025.09.23 13:14浏览量:0

简介:本文详细讲解如何使用 FastAPI 快速开发一个完整的待办事项 Web API 项目,涵盖路由设计、CRUD 操作实现、数据验证及项目结构优化,助力开发者高效构建 RESTful 服务。

FastAPI 实战:从零构建待办事项 API 的 CRUD 操作指南

FastAPI 作为现代 Python Web 框架的代表,凭借其高性能、自动生成文档和异步支持等特性,已成为开发 RESTful API 的首选工具。本文将以待办事项(Todo)管理为例,深入讲解如何使用 FastAPI 实现完整的增删改查(CRUD)路由,覆盖从项目初始化到功能实现的完整流程。

一、项目初始化与基础配置

1. 环境准备与依赖安装

开发 FastAPI 项目前,需确保 Python 版本 ≥3.7(推荐 3.9+),并通过 pip 安装核心依赖:

  1. pip install fastapi uvicorn[standard] pydantic sqlalchemy
  • fastapi:核心框架
  • uvicorn:ASGI 服务器
  • pydantic:数据验证与序列化
  • sqlalchemy:ORM 工具(可选,用于数据库操作)

2. 项目结构规划

合理的项目结构能提升代码可维护性。推荐采用以下分层架构:

  1. todo_api/
  2. ├── main.py # 应用入口
  3. ├── models/ # 数据模型
  4. └── todo.py # Todo 模型定义
  5. ├── schemas/ # 数据验证 schema
  6. └── todo.py # Todo 请求/响应模型
  7. ├── routers/ # 路由模块
  8. └── todos.py # Todo 相关路由
  9. ├── database.py # 数据库连接(可选)
  10. └── requirements.txt # 依赖清单

二、核心功能实现:CRUD 路由设计

1. 数据模型与 Schema 定义

数据模型(models/todo.py)

  1. from sqlalchemy import Column, Integer, String, Boolean
  2. from sqlalchemy.ext.declarative import declarative_base
  3. Base = declarative_base()
  4. class Todo(Base):
  5. __tablename__ = "todos"
  6. id = Column(Integer, primary_key=True, index=True)
  7. title = Column(String, index=True)
  8. description = Column(String)
  9. completed = Column(Boolean, default=False)

请求/响应 Schema(schemas/todo.py)

  1. from pydantic import BaseModel
  2. class TodoBase(BaseModel):
  3. title: str
  4. description: str = None
  5. completed: bool = False
  6. class TodoCreate(TodoBase):
  7. pass
  8. class TodoUpdate(TodoBase):
  9. pass
  10. class Todo(TodoBase):
  11. id: int
  12. class Config:
  13. orm_mode = True # 允许从 ORM 对象转换

2. 路由实现(routers/todos.py)

初始化路由与依赖注入

  1. from fastapi import APIRouter, Depends, HTTPException
  2. from sqlalchemy.orm import Session
  3. from typing import List
  4. from ..database import get_db # 假设已实现数据库会话工厂
  5. from ..models.todo import Todo as TodoModel
  6. from ..schemas.todo import TodoCreate, TodoUpdate, Todo as TodoSchema
  7. router = APIRouter()

创建(Create)

  1. @router.post("/", response_model=TodoSchema)
  2. def create_todo(
  3. todo: TodoCreate,
  4. db: Session = Depends(get_db)
  5. ):
  6. db_todo = TodoModel(**todo.dict())
  7. db.add(db_todo)
  8. db.commit()
  9. db.refresh(db_todo)
  10. return db_todo

关键点

  • 使用 TodoCreate Schema 验证输入数据
  • 通过依赖注入获取数据库会话
  • 返回完整的 TodoSchema 对象

读取(Read)

获取所有待办事项

  1. @router.get("/", response_model=List[TodoSchema])
  2. def read_todos(
  3. db: Session = Depends(get_db),
  4. skip: int = 0,
  5. limit: int = 100
  6. ):
  7. return db.query(TodoModel).offset(skip).limit(limit).all()

获取单个待办事项

  1. @router.get("/{todo_id}", response_model=TodoSchema)
  2. def read_todo(
  3. todo_id: int,
  4. db: Session = Depends(get_db)
  5. ):
  6. db_todo = db.query(TodoModel).filter(TodoModel.id == todo_id).first()
  7. if db_todo is None:
  8. raise HTTPException(status_code=404, detail="Todo not found")
  9. return db_todo

更新(Update)

  1. @router.put("/{todo_id}", response_model=TodoSchema)
  2. def update_todo(
  3. todo_id: int,
  4. todo_update: TodoUpdate,
  5. db: Session = Depends(get_db)
  6. ):
  7. db_todo = db.query(TodoModel).filter(TodoModel.id == todo_id).first()
  8. if db_todo is None:
  9. raise HTTPException(status_code=404, detail="Todo not found")
  10. update_data = todo_update.dict(exclude_unset=True)
  11. for key, value in update_data.items():
  12. setattr(db_todo, key, value)
  13. db.commit()
  14. db.refresh(db_todo)
  15. return db_todo

优化点

  • 使用 exclude_unset=True 忽略未提供的字段
  • 通过 setattr 动态更新属性

删除(Delete)

  1. @router.delete("/{todo_id}")
  2. def delete_todo(
  3. todo_id: int,
  4. db: Session = Depends(get_db)
  5. ):
  6. db_todo = db.query(TodoModel).filter(TodoModel.id == todo_id).first()
  7. if db_todo is None:
  8. raise HTTPException(status_code=404, detail="Todo not found")
  9. db.delete(db_todo)
  10. db.commit()
  11. return {"message": "Todo deleted successfully"}

3. 主应用集成(main.py)

  1. from fastapi import FastAPI
  2. from .routers import todos
  3. app = FastAPI()
  4. # 包含子路由
  5. app.include_router(todos.router, prefix="/todos", tags=["todos"])
  6. @app.get("/")
  7. def read_root():
  8. return {"message": "Welcome to the Todo API"}

三、进阶优化与最佳实践

1. 异步支持

FastAPI 原生支持异步路由,可提升 I/O 密集型操作性能:

  1. from sqlalchemy.ext.asyncio import AsyncSession
  2. from .database import get_async_db # 异步数据库会话
  3. @router.post("/async/", response_model=TodoSchema)
  4. async def create_todo_async(
  5. todo: TodoCreate,
  6. db: AsyncSession = Depends(get_async_db)
  7. ):
  8. db_todo = TodoModel(**todo.dict())
  9. db.add(db_todo)
  10. await db.commit()
  11. await db.refresh(db_todo)
  12. return db_todo

2. 分页与排序

增强读取接口的灵活性:

  1. from fastapi import Query
  2. @router.get("/", response_model=List[TodoSchema])
  3. def read_todos(
  4. db: Session = Depends(get_db),
  5. skip: int = Query(0, ge=0),
  6. limit: int = Query(100, le=500),
  7. sort_by: str = Query("id", description="Field to sort by"),
  8. sort_order: str = Query("asc", description="Sort order (asc/desc)")
  9. ):
  10. query = db.query(TodoModel)
  11. # 动态排序
  12. if sort_order == "desc":
  13. query = query.order_by(getattr(TodoModel, sort_by).desc())
  14. else:
  15. query = query.order_by(getattr(TodoModel, sort_by).asc())
  16. return query.offset(skip).limit(limit).all()

3. 错误处理与日志

自定义异常处理器:

  1. from fastapi import Request
  2. from fastapi.responses import JSONResponse
  3. from fastapi.exceptions import HTTPException
  4. @app.exception_handler(HTTPException)
  5. async def http_exception_handler(request: Request, exc: HTTPException):
  6. return JSONResponse(
  7. status_code=exc.status_code,
  8. content={"message": exc.detail},
  9. )

四、测试与部署

1. 单元测试示例

使用 pytesthttpx 测试 API:

  1. import pytest
  2. from httpx import AsyncClient
  3. from main import app
  4. @pytest.mark.anyio
  5. async def test_create_todo():
  6. async with AsyncClient(app=app, base_url="http://test") as ac:
  7. response = await ac.post("/todos/", json={
  8. "title": "Test Todo",
  9. "description": "Test Description"
  10. })
  11. assert response.status_code == 200
  12. assert response.json()["title"] == "Test Todo"

2. 生产部署建议

  • ASGI 服务器:推荐 uvicorngunicorn + uvicorn 工作模式
  • 进程管理:使用 systemdDocker 容器化部署
  • 反向代理:Nginx 配置示例:
  1. location / {
  2. proxy_pass http://127.0.0.1:8000;
  3. proxy_set_header Host $host;
  4. proxy_set_header X-Real-IP $remote_addr;
  5. }

五、总结与扩展

本文通过待办事项管理案例,系统展示了 FastAPI 实现 CRUD 路由的核心方法。关键收获包括:

  1. 分层架构:分离路由、模型和 Schema,提升可维护性
  2. 依赖注入:通过 Depends 管理数据库会话等共享资源
  3. 数据验证:利用 Pydantic Schema 确保输入输出合法性
  4. 异步支持:优化高并发场景下的性能表现

扩展方向

  • 添加用户认证(JWT/OAuth2)
  • 实现事件驱动(如 WebSocket 通知)
  • 集成缓存(Redis
  • 添加 API 版本控制

FastAPI 的简洁设计与强大功能,使其成为构建现代 Web API 的理想选择。通过本文的实践,开发者可快速掌握从路由设计到生产部署的全流程,为后续复杂项目开发奠定坚实基础。

相关文章推荐

发表评论