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 ORMPydantic
字段类型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自动生成)
  • 保持代码的可维护性和可测试性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风吹落叶花飘荡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值