01-从零入门教程

知识库
知识库文档
/tech-stacks/fastapi/tutorial/01-从零入门教程.md

文档

FastAPI 入门教程 —— 现代 Python API 开发

本章目标

  • 理解 FastAPI 的异步架构与性能优势
  • 掌握 Pydantic v2 数据模型深度用法
  • 构建完整 CRUD API(含 SQLAlchemy 异步数据库)
  • 理解 FastAPI 与 Flask/Django 的定位差异

1. FastAPI 为何这么快?

传统 WSGI(Flask/Django)
 请求 → 线程池 → 阻塞等待(DB、网络)→ 响应
        每个请求占一个线程

FastAPI ASGI
 请求 → 事件循环 → async/await(非阻塞)→ 响应
        一个线程处理成千上万并发

性能基准(每秒请求数,粗略对比):

  • Flask(sync):~5,000 req/s
  • FastAPI(async):~25,000 req/s
  • Node.js(Express):~20,000 req/s

2. Pydantic v2 深度用法

from pydantic import BaseModel, Field, field_validator, model_validator
from typing import Optional, List, Union
from datetime import date
from enum import Enum
import re


class ProductCategory(str, Enum):
    ELECTRONICS = "electronics"
    CLOTHING = "clothing"
    FOOD = "food"


class ProductCreate(BaseModel):
    """创建产品请求模型"""
    name: str = Field(..., min_length=1, max_length=200)
    price: float = Field(..., gt=0, description="价格必须大于0")
    category: ProductCategory
    tags: List[str] = Field(default=[], max_length=10)
    discount_percent: Optional[float] = Field(default=None, ge=0, le=100)
    release_date: Optional[date] = None

    # 字段验证器(v2 风格)
    @field_validator("name")
    @classmethod
    def name_must_not_be_empty(cls, v: str) -> str:
        if not v.strip():
            raise ValueError("名称不能为空或全是空格")
        return v.strip()

    @field_validator("tags")
    @classmethod
    def tags_must_be_unique(cls, v: List[str]) -> List[str]:
        if len(v) != len(set(v)):
            raise ValueError("标签不能重复")
        return [tag.strip().lower() for tag in v]

    # 模型级验证器(跨字段)
    @model_validator(mode="after")
    def validate_pricing(self):
        if self.discount_percent is not None and self.price < 10:
            raise ValueError("价格低于10元的产品不能设置折扣")
        return self


class ProductResponse(BaseModel):
    """产品响应模型"""
    id: int
    name: str
    price: float
    category: ProductCategory
    tags: List[str]
    final_price: float  # 计算字段

    model_config = {"from_attributes": True}

Pydantic v2 vs v1 关键变化

变化 v1 v2
字段验证器 @validator("field") @field_validator("field")
根验证器 @root_validator @model_validator(mode="after")
ORM 模式 class Config: orm_mode = True model_config = {"from_attributes": True}
正则 Field(regex=...) Field(pattern=...)

3. 异步 SQLAlchemy 集成

# database.py
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from sqlalchemy import String, Float, Enum as SAEnum, DateTime, func
import enum

DATABASE_URL = "sqlite+aiosqlite:///./products.db"

engine = create_async_engine(DATABASE_URL, echo=False)
async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)


class Base(DeclarativeBase):
    pass


class ProductCategory(str, enum.Enum):
    ELECTRONICS = "electronics"
    CLOTHING = "clothing"
    FOOD = "food"


class Product(Base):
    __tablename__ = "products"

    id: Mapped[int] = mapped_column(primary_key=True, index=True)
    name: Mapped[str] = mapped_column(String(200))
    price: Mapped[float] = mapped_column(Float)
    category: Mapped[ProductCategory] = mapped_column(SAEnum(ProductCategory))
    tags: Mapped[str | None] = mapped_column(String(500), nullable=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )


# 依赖项 —— 获取数据库会话
async def get_db() -> AsyncSession:
    async with async_session() as session:
        try:
            yield session
            await session.commit()
        except Exception:
            await session.rollback()
            raise
        finally:
            await session.close()
# main.py - CRUD 路由
@app.post("/products", response_model=ProductResponse, status_code=201)
async def create_product(
    product: ProductCreate,
    db: AsyncSession = Depends(get_db),
):
    db_product = Product(
        name=product.name,
        price=product.price,
        category=product.category,
        tags=",".join(product.tags),
    )
    db.add(db_product)
    await db.flush()
    await db.refresh(db_product)
    return db_product


@app.get("/products", response_model=List[ProductResponse])
async def list_products(
    skip: int = 0,
    limit: int = 20,
    category: Optional[ProductCategory] = None,
    db: AsyncSession = Depends(get_db),
):
    from sqlalchemy import select
    stmt = select(Product).offset(skip).limit(limit)
    if category:
        stmt = stmt.where(Product.category == category)
    result = await db.execute(stmt)
    return result.scalars().all()

4. FastAPI 与 Flask/Django 对比

维度 FastAPI Flask Django
类型驱动 ✅ 原生 ❌(需 DRF)
异步原生 ✅ ASGI ⚠️ 2.0+ 部分支持 ⚠️ 3.1+ 部分支持
自动文档 ✅ OpenAPI 内置 ❌ 需插件 ❌ 需 DRF + drf-spectacular
数据验证 ✅ Pydantic ❌ 需手动 ✅ DRF Serializer
性能 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐
生态成熟度 ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
学习曲线
适合场景 API/微服务/ML 小型应用/原型 全栈大型应用

5. 项目结构最佳实践

fastapi_project/
├── app/
│   ├── __init__.py
│   ├── main.py              # FastAPI 实例创建 + 路由注册
│   ├── core/
│   │   ├── config.py        # 配置(Pydantic Settings)
│   │   └── security.py      # 认证/密码工具
│   ├── models/              # SQLAlchemy 模型
│   ├── schemas/             # Pydantic 请求/响应模型
│   ├── api/
│   │   ├── __init__.py
│   │   ├── deps.py          # 共享依赖项
│   │   └── v1/
│   │       ├── __init__.py
│   │       ├── router.py    # 聚合子路由
│   │       └── endpoints/
│   │           ├── users.py
│   │           └── products.py
│   └── services/            # 业务逻辑层
├── alembic/                 # 数据库迁移
├── tests/
├── requirements.txt
└── .env

思考题

  1. FastAPI 的异步是否意味着所有代码都必须是 async def?什么场景同步函数也能获益?
  2. Pydantic v2 的 model_validatorfield_validator 分别何时使用?
  3. FastAPI 的依赖注入与类的构造函数注入有何异同?为什么它更适合 Web 场景?
  4. 将 Flask 项目迁移到 FastAPI 时,哪些部分最需要重写?

信息

路径
/tech-stacks/fastapi/tutorial/01-从零入门教程.md
更新时间
2026/5/30