FastAPI 中,数据库模型(通常使用 SQLAlchemy 定义)和接口模型(使用 Pydantic 定义的 schemas)的差异

在 FastAPI 中,数据库模型(通常使用 SQLAlchemy 定义)和接口模型(使用 Pydantic 定义的 schemas)虽然都用于表示数据结构,但它们有明确的职责区分。以下是它们的核心区别和协作方式:


1. 数据库模型 (Models)

位置:通常在 models.py 中定义
技术:使用 SQLAlchemy ORM
目的:直接映射数据库表结构,处理数据库操作
特点

from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class UserDB(Base):  # 数据库模型
    __tablename__ = "users"
    
    id = Column(Integer, primary_key=True, index=True)
    username = Column(String(50), unique=True)  # 数据库约束
    email = Column(String(100))
    hashed_password = Column(String(200))  # 敏感字段
    created_at = Column(DateTime)  # 数据库自动生成的时间戳

关键特性

  • 包含数据库特有的字段类型(如 Column
  • 定义表名、主键、索引、约束等
  • 包含敏感字段(如密码哈希)
  • 包含 ORM 关系(如 relationship
  • 可能有数据库自动生成的字段(如创建时间)

2. 接口模型 (Schemas)

位置:通常在 schemas.py 中定义
技术:使用 Pydantic BaseModel
目的:定义 API 输入/输出的数据结构
特点

from pydantic import BaseModel, EmailStr

class UserCreate(BaseModel):  # 创建用户的请求模型
    username: str
    email: EmailStr  # 自动邮箱格式验证
    password: str

class UserPublic(BaseModel):  # 返回给用户的响应模型
    id: int
    username: str
    email: str

class UserPrivate(UserPublic):  # 内部使用的扩展模型
    hashed_password: str

关键特性

  • 使用 Python 原生类型(str, int 等)
  • 内置数据验证(如 EmailStr
  • 可定义不同场景的模型(创建/响应/更新)
  • 不包含数据库技术细节
  • 可配置响应排除敏感字段

3. 核心区别对比表

特性 数据库模型 (Models) 接口模型 (Schemas)
职责 数据库表映射 API 数据验证和序列化
技术 SQLAlchemy ORM Pydantic
字段类型 Column(Integer) Python 原生类型 (int, str)
敏感字段 包含密码等敏感信息 通常排除敏感字段
关系处理 直接定义 relationship 通过嵌套模型表示
自动生成字段 包含 (如 created_at) 通常不包含
数据验证 基础约束 (如 unique=True) 高级验证 (正则、邮箱格式等)

4. 协作流程示例

用户注册场景:
客户端 FastAPI 路由 Pydantic 模型 数据库模型 数据库 POST /users (JSON: {username, email, password}) 用 UserCreate 验证输入 验证通过的数据 转换 UserCreate → UserDB 模型 执行 INSERT 操作 返回创建的数据库对象 转换 UserDB → UserPublic 模型 序列化为 JSON 返回 201 Created (UserPublic) 客户端 FastAPI 路由 Pydantic 模型 数据库模型 数据库
代码实现:
# schemas.py
class UserCreate(BaseModel):
    username: str
    email: EmailStr
    password: str

class UserPublic(BaseModel):
    id: int
    username: str
    email: str

# models.py
class UserDB(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    username = Column(String(50), unique=True)
    email = Column(String(100))
    hashed_password = Column(String(200))

# crud.py
def create_user(db: Session, user: schemas.UserCreate):
    # 转换并处理密码
    hashed_pw = hash_password(user.password)
    
    # 创建数据库模型
    db_user = models.UserDB(
        username=user.username,
        email=user.email,
        hashed_password=hashed_pw
    )
    
    db.add(db_user)
    db.commit()
    return db_user

# main.py
@app.post("/users", response_model=schemas.UserPublic)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
    db_user = crud.create_user(db, user)
    return db_user  # 自动转换为 UserPublic

5. 为什么需要分离?

  1. 安全隔离

    • 数据库模型包含敏感字段(如密码哈希)
    • 接口模型可排除敏感字段(通过 response_model
  2. 职责分离

    • 数据库模型关注存储结构
    • 接口模型关注API契约
  3. 灵活性

    # 同一个数据库模型对应多个接口模型
    class UserUpdate(BaseModel):  # 更新专用模型
        email: Optional[EmailStr]
        password: Optional[str]
    
  4. 验证与约束解耦

    • 数据库约束:唯一索引、外键关系
    • API验证:邮箱格式、密码强度
  5. 避免技术泄露

    • 接口模型不暴露数据库技术细节(如 SQLAlchemy 类型)
    • 保持 API 的稳定性和可替换性

最佳实践建议

  1. 单向依赖
    Schemas 应该 不依赖 Models,保持解耦

  2. 转换层
    在路由和 CRUD 操作之间进行模型转换:

    # 在路由处理函数中
    db_user = UserDB(**user_create.dict())
    
  3. 使用 orm_mode
    使 Pydantic 能直接处理 ORM 对象:

    class UserPublic(BaseModel):
        class Config:
            orm_mode = True  # 允许从ORM对象创建
    
  4. 分层设计

    app/
    ├── models.py    # SQLAlchemy 模型
    ├── schemas.py   # Pydantic 模型
    ├── crud.py      # 数据库操作(使用 models)
    └── api.py       # 路由(使用 schemas 和 crud)
    

这种分离设计使得 FastAPI 应用能够:

  • 安全地处理敏感数据
  • 独立演化数据库和API
  • 提供清晰的API文档(通过Pydantic自动生成)
  • 保持代码的可维护性和可测试性

你可能感兴趣的:(FastAPI 中,数据库模型(通常使用 SQLAlchemy 定义)和接口模型(使用 Pydantic 定义的 schemas)的差异)