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_baseBase = 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, EmailStrclass UserCreate(BaseModel): # 创建用户的请求模型username: stremail: EmailStr # 自动邮箱格式验证password: strclass UserPublic(BaseModel): # 返回给用户的响应模型id: intusername: stremail: strclass 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. 协作流程示例
用户注册场景:
代码实现:
# schemas.py
class UserCreate(BaseModel):username: stremail: EmailStrpassword: strclass UserPublic(BaseModel):id: intusername: stremail: 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. 为什么需要分离?
-
安全隔离
- 数据库模型包含敏感字段(如密码哈希)
- 接口模型可排除敏感字段(通过
response_model
)
-
职责分离
- 数据库模型关注存储结构
- 接口模型关注API契约
-
灵活性
# 同一个数据库模型对应多个接口模型 class UserUpdate(BaseModel): # 更新专用模型email: Optional[EmailStr]password: Optional[str]
-
验证与约束解耦
- 数据库约束:唯一索引、外键关系
- API验证:邮箱格式、密码强度
-
避免技术泄露
- 接口模型不暴露数据库技术细节(如 SQLAlchemy 类型)
- 保持 API 的稳定性和可替换性
最佳实践建议
-
单向依赖
Schemas 应该 不依赖 Models,保持解耦 -
转换层
在路由和 CRUD 操作之间进行模型转换:# 在路由处理函数中 db_user = UserDB(**user_create.dict())
-
使用
orm_mode
使 Pydantic 能直接处理 ORM 对象:class UserPublic(BaseModel):class Config:orm_mode = True # 允许从ORM对象创建
-
分层设计
app/ ├── models.py # SQLAlchemy 模型 ├── schemas.py # Pydantic 模型 ├── crud.py # 数据库操作(使用 models) └── api.py # 路由(使用 schemas 和 crud)
这种分离设计使得 FastAPI 应用能够:
- 安全地处理敏感数据
- 独立演化数据库和API
- 提供清晰的API文档(通过Pydantic自动生成)
- 保持代码的可维护性和可测试性