ORM分层模型设计

前言

为了规范模型的创建与使用,减少不必要的代码冗余,维护数据的安全性,进行模型设计时,设置基本模型,输入模型,输出模型以及数据库操作模型;
基本模型中包含了基础字段,避免模型定义重复
输入模型用于用户输入字段时进行字段的校验
输出模型用于返回给用户特定信息,避免泄露敏感信息;
数据库操作模型用于进行模型的数据库操作;

以下是详细解释

输入验证 vs 输出序列化

  1. 输入验证模型 (UserCreate):验证客户端传入的数据
  2. 输出序列化模型 (UserResponse):控制返回给客户端的数据格式

1.分层模型设计

from pydantic import BaseModel

# 基础模型 (共享字段)
class UserBase(BaseModel):
    username: str

# 输入验证模型 (扩展基础模型)
class UserCreate(UserBase):
    # 添加创建用户特有的验证规则
    password: str  # 密码只在创建时需要
    email: str     # 邮箱验证等
    
    class Config:
        extra = "forbid"  # 禁止额外字段

# 输出序列化模型 (扩展基础模型)
class UserResponse(UserBase):
    id: str
    created_at: datetime
    
    class Config:
        orm_mode = True  # 允许从ORM对象转换

# 数据库模型 (SQLAlchemy)
class User(Base):
    __tablename__ = "users"
    id = Column(String, primary_key=True)
    username = Column(String)
    password = Column(String)  # 实际存储哈希值
    created_at = Column(DateTime)

2.路由中使用分层模型

@router.post("/users")
def create_user(
    user_data: UserCreate,  # 输入验证
    db: Session = Depends(get_db)
):
    try:
        # 1. 输入验证由UserCreate自动完成
        # 2. 创建数据库对象
        db_user = User(
            id=get_uuid(),
            username=user_data.username,# 传入校验的字段名
            password=hash_password(user_data.password),  # 安全处理
            created_at=datetime.utcnow()
        )
        
        db.add(db_user)
        db.commit()
        db.refresh(db_user)  # 获取数据库生成的值
        
        # 3. 使用输出模型序列化
        return UserResponse.from_orm(db_user)
    
    except Exception as e:
        db.rollback()
        return error_response(500, str(e))

3.关键优化点

  1. 字段复用

    • UserBase 包含共享字段(如 username
    • 输入和输出模型继承基础模型,避免重复定义
  2. 关注点分离

    • UserCreate:专注输入验证(密码强度、邮箱格式等)
    • UserResponse:专注输出控制(排除敏感字段如密码)
  3. 自动序列化

    # 使用Pydantic的orm_mode
    return UserResponse.from_orm(db_user)
    # 自动将SQLAlchemy对象转换为响应字典
    
  4. 安全控制

    • 输入模型包含密码字段(验证后立即哈希处理)
    • 输出模型完全排除密码字段

4.响应处理优化

修改您的 success_response 函数以更好地支持 Pydantic 模型:

def success_response(
    code: int = 0,
    message: str = "操作成功",
    data: Union[BaseModel, dict, list] = None
):
    # 如果数据是Pydantic模型,自动转换为dict
    if isinstance(data, BaseModel):
        data = data.dict()
    
    return JSONResponse(
        content={"code": code, "message": message, "data": data},
        status_code=200
    )

5.完整工作流程

Client Router Pydantic SQLAlchemy Database POST /users {username, password} 使用UserCreate验证输入 验证通过/返回错误 创建User对象 INSERT 新用户记录 使用UserResponse.from_orm() 序列化数据 返回安全数据 {id, username} Client Router Pydantic SQLAlchemy Database

6.from_orm()方法详解

UserResponse.from_orm(实例类) 是 Pydantic 提供的一个强大功能,用于将 ORM 对象(如 SQLAlchemy 模型实例)自动转换为 Pydantic 模型实例。

1.核心概念解析
  1. from_orm() 方法

    • 是 Pydantic 模型的类方法
    • 用于从 ORM 对象创建 Pydantic 实例
    • 自动提取 ORM 对象中与 Pydantic 模型匹配的属性
  2. 必需配置

    class UserResponse(BaseModel):
        # 字段定义...
        
        class Config:
            orm_mode = True  # 启用 ORM 模式
    
2.工作流程
SQLAlchemy ORM 对象
UserResponse.from_orm
自动提取匹配字段
创建 Pydantic 实例
可序列化的字典
3.实际示例

假设有以下 SQLAlchemy 模型:

class User(Base):
    __tablename__ = "users"
    
    id = Column(String, primary_key=True)
    username = Column(String)  # 校验字段
    password = Column(String)  # 敏感字段
    created_at = Column(DateTime)
    is_admin = Column(Boolean)

定义 Pydantic 响应模型:

class UserResponse(BaseModel):
    id: str
    username: str
    created_at: datetime
    
    class Config:
        orm_mode = True  # 关键配置

使用方式:

@router.post("/users")
def create_user(db: Session = Depends(get_db)):
    # 创建数据库用户
    db_user = User(
        id="user_123",
        username="john_doe",
        password="secret",  # 敏感字段
        created_at=datetime.utcnow(),
        is_admin=False
    )
    
    # 转换为响应模型
    return UserResponse.from_orm(db_user)
4.输出结果
{
    "id": "user_123",
    "username": "john_doe",
    "created_at": "2023-07-20T12:34:56.789Z"
}
5.关键特性
  1. 自动字段映射

    • 只提取 Pydantic 模型中定义的字段
    • 忽略 ORM 对象中的其他字段(如 password, is_admin
  2. 类型转换

    • 自动将数据库类型转换为 Python 类型
    • 例如:SQLAlchemy DateTime → Python datetime
  3. 安全过滤

    • 天然防止敏感数据泄露
    • 无需手动排除字段
  4. 嵌套支持

    class TeamResponse(BaseModel):
        id: str
        name: str
        members: List[UserResponse]  # 嵌套模型
        
        class Config:
            orm_mode = True
    
    # 自动处理关系
    team = session.query(Team).options(joinedload(Team.members)).first()
    return TeamResponse.from_orm(team)
    
6.与直接序列化的区别
方法优点缺点
UserResponse.from_orm(db_user)自动过滤敏感字段
类型安全
支持复杂嵌套
需要预先定义模型
db_user.__dict__简单直接暴露所有字段
包含内部属性
需要手动清理
{k: v for k, v in db_user.__dict__ if k[0] != '_'}过滤内部属性仍暴露所有数据库字段
无类型控制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ACERT333

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

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

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

打赏作者

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

抵扣说明:

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

余额充值