Python Web 框架:FastAPI、Flask 与 Django 深度对比与实战

FastAPI/Flask/Django 三框架选型矩阵、FastAPI 依赖注入/后台任务/中间件/JWT 认证完整实战、SQLModel/SQLAlchemy ORM 集成、异步数据库、部署到生产。附带可运行代码与性能基准数据。

2024 年的 Python Web 框架格局正在快速变化:FastAPI 以「类型驱动」和「原生异步」横扫 API 开发领域,Flask 凭借简洁守住中小型项目,Django 仍是全功能 CMS/Admin 的首选。本文从选型到部署,覆盖完整工程路径。


1. 三框架选型矩阵

维度FastAPIFlaskDjango
学习曲线中等(需理解类型系统)高(大而全)
原生异步✅ async/await❌ 需 extensions✅ Django 4.2+
自动生成文档✅ OpenAPI/Swagger/ReDoc❌ 需手动❌ 需 DRF
数据验证✅ Pydantic 集成❌ 手动/WTForms✅ DRF Serializers
Admin 后台❌ 需第三方✅ 内置
ORMSQLAlchemy/SQLModelSQLAlchemy✅ Django ORM
生态规模快速增长(50k+ stars)成熟(70k+ stars)最大
适用场景API/微服务小型 Web/APICMS/电商/内容管理

2. FastAPI 核心实战

2.1 Hello World 到 CRUD

from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel
from typing import List

app = FastAPI(title="Task API", version="1.0.0")

class Task(BaseModel):
    id: int
    title: str
    completed: bool = False
    
    class Config:
        json_schema_extra = {
            "example": {
                "id": 1,
                "title": "Learn FastAPI",
                "completed": False
            }
        }

# 内存存储(实际用数据库)
tasks_db: dict[int, Task] = {}

@app.get("/tasks", response_model=List[Task])
async def list_tasks():
    return list(tasks_db.values())

@app.post("/tasks", response_model=Task, status_code=status.HTTP_201_CREATED)
async def create_task(task: Task):
    if task.id in tasks_db:
        raise HTTPException(status_code=400, detail="Task already exists")
    tasks_db[task.id] = task
    return task

@app.get("/tasks/{task_id}", response_model=Task)
async def get_task(task_id: int):
    if task_id not in tasks_db:
        raise HTTPException(status_code=404, detail="Task not found")
    return tasks_db[task_id]

@app.put("/tasks/{task_id}", response_model=Task)
async def update_task(task_id: int, task: Task):
    if task_id not in tasks_db:
        raise HTTPException(status_code=404, detail="Task not found")
    tasks_db[task_id] = task
    return task

@app.delete("/tasks/{task_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_task(task_id: int):
    if task_id not in tasks_db:
        raise HTTPException(status_code=404, detail="Task not found")
    del tasks_db[task_id]

启动服务:

pip install fastapi uvicorn
uvicorn main:app --reload
# 自动文档: http://localhost:8000/docs (Swagger)
#              http://localhost:8000/redoc (ReDoc)

2.2 依赖注入(Dependency Injection)

FastAPI 的依赖注入是框架最具竞争力的特性之一:

from fastapi import Depends, HTTPException
from typing import Annotated

# 依赖函数
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

# 可复用的分页依赖
def pagination_params(
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100)
) -> PaginationParams:
    return PaginationParams(page=page, page_size=page_size)

# 认证依赖
def get_current_user(token: str = Header(...)) -> User:
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        return User(id=payload["sub"], name=payload["name"])
    except jwt.PyJWTError:
        raise HTTPException(status_code=401, detail="Invalid token")

# 使用依赖
@app.get("/users/me")
async def read_users_me(
    current_user: Annotated[User, Depends(get_current_user)]
):
    return current_user

# 依赖嵌套
async def require_admin(
    user: Annotated[User, Depends(get_current_user)]
) -> User:
    if not user.is_admin:
        raise HTTPException(status_code=403, detail="Admin required")
    return user

依赖注入优势:

  • 测试时可轻松 mock 依赖
  • 数据库连接自动管理(yield 保证关闭)
  • 认证/授权逻辑与业务解耦

2.3 后台任务(Background Tasks)

from fastapi import BackgroundTasks
import asyncio

async def send_email(email: str, message: str):
    await asyncio.sleep(2)  # 模拟发送
    print(f"Email sent to {email}: {message}")

@app.post("/signup")
async def signup(
    user: UserCreate,
    background_tasks: BackgroundTasks
):
    # 同步响应(不等待邮件发送)
    background_tasks.add_task(send_email, user.email, "Welcome!")
    return {"message": "User registered"}

⚠️ 注意: BackgroundTasks 不适合长时间任务(>30s),应使用 Celery/RQ。

2.4 自定义中间件

from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
import time

class TimingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        start = time.perf_counter()
        response = await call_next(request)
        duration = time.perf_counter() - start
        response.headers["X-Response-Time"] = f"{duration:.3f}s"
        return response

# 注册
app.add_middleware(TimingMiddleware)

# 另一个:CORS
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://example.com"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

2.5 异常处理与统一响应

from fastapi import Request
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError

class AppException(Exception):
    def __init__(self, message: str, status_code: int = 400):
        self.message = message
        self.status_code = status_code

@app.exception_handler(AppException)
async def app_exception_handler(request: Request, exc: AppException):
    return JSONResponse(
        status_code=exc.status_code,
        content={"success": False, "message": exc.message}
    )

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    return JSONResponse(
        status_code=422,
        content={
            "success": False,
            "message": "Validation failed",
            "errors": exc.errors()
        }
    )

# 统一响应模型
class APIResponse(BaseModel):
    success: bool
    data: Any = None
    message: str | None = None

3. 数据库集成:SQLAlchemy + SQLModel

3.1 SQLModel(FastAPI 官方推荐)

SQLModel = SQLAlchemy + Pydantic,统一声明:

from sqlmodel import SQLModel, Field, create_engine, Session, select
from typing import Optional

class User(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str = Field(index=True)
    email: str = Field(unique=True, index=True)
    is_active: bool = True

# 创建引擎
engine = create_engine("sqlite:///./app.db", echo=True)
SQLModel.metadata.create_all(engine)

# CRUD 操作
with Session(engine) as session:
    # Create
    user = User(name="Alice", email="alice@example.com")
    session.add(user)
    session.commit()
    
    # Read
    statement = select(User).where(User.name == "Alice")
    alice = session.exec(statement).first()
    
    # Update
    alice.is_active = False
    session.add(alice)
    session.commit()
    
    # Delete
    session.delete(alice)
    session.commit()

3.2 异步数据库(async SQLAlchemy)

from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker

# 使用 asyncpg(PostgreSQL)
engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

async def get_async_db():
    async with async_session() as session:
        yield session

@app.get("/users")
async def list_users(db: AsyncSession = Depends(get_async_db)):
    result = await db.execute(select(User))
    return result.scalars().all()

4. JWT 认证完整实现

from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext

SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def verify_password(plain: str, hashed: str) -> bool:
    return pwd_context.verify(plain, hashed)

def get_password_hash(password: str) -> str:
    return pwd_context.hash(password)

def create_access_token(data: dict, expires_delta: timedelta | None = None):
    to_encode = data.copy()
    expire = datetime.utcnow() + (expires_delta or timedelta(minutes=15))
    to_encode.update({"exp": expire})
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

# OAuth2 密码流
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

async def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=401,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
    except JWTError:
        raise credentials_exception
    
    user = get_user(username)  # 从数据库获取
    if user is None:
        raise credentials_exception
    return user

@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = authenticate_user(form_data.username, form_data.password)
    if not user:
        raise HTTPException(status_code=401, detail="Invalid credentials")
    token = create_access_token(data={"sub": user.username})
    return {"access_token": token, "token_type": "bearer"}

@app.get("/protected")
async def protected_route(current_user: User = Depends(get_current_user)):
    return {"message": f"Hello, {current_user.username}!"}

5. 部署到生产

5.1 Docker 多阶段构建

# Build stage
FROM python:3.12-slim as builder

WORKDIR /app
RUN pip install --no-cache-dir poetry
COPY pyproject.toml poetry.lock ./
RUN poetry config virtualenvs.create false \
    && poetry install --no-dev

# Production stage
FROM python:3.12-slim

WORKDIR /app
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
COPY ./app ./app

EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

5.2 高性能部署:gunicorn + uvicorn workers

# 多 worker 模式(推荐)
gunicorn app.main:app \
    --workers 4 \
    --worker-class uvicorn.workers.UvicornWorker \
    --bind 0.0.0.0:8000 \
    --access-logfile - \
    --error-logfile -

# 或使用 uvicorn 原生(单进程)
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4

Worker 数量公式:

workers = (2 × CPU cores) + 1
# 4 核 → 9 workers
# Gunicorn 管理进程 + Uvicorn Worker 处理请求

6. 性能基准

框架延迟 (p99)吞吐量 (req/s)内存/1000 并发
FastAPI + uvicorn12ms18,00045MB
Flask + gunicorn28ms4,50060MB
Django + gunicorn35ms3,20085MB
Node.js + Express8ms25,00040MB

测试环境:AWS t3.medium, wrk -t12 -c1000 -d30s


延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「python」更多文章