3. 数据模型设计基础:Pydantic 模型定义、字段类型与校验、数据模型与数据库表映射
在运维数据移交接口开发中,数据模型是连接业务逻辑与持久化存储的核心桥梁。本章将系统讲解如何使用 Pydantic 定义接口数据模型、配置字段类型与校验规则,并建立与数据库表的映射关系,确保数据在传输与存储过程中的完整性与一致性。
3.1 Pydantic 模型定义基础
Pydantic 是 Python 生态中最流行的数据验证库,通过声明式类定义实现数据模型。在 FastAPI 框架中,Pydantic 模型同时承担请求体、响应体与数据校验的职责。
核心原则: 每个接口数据模型应只关注一个业务实体,避免大而全的“上帝模型”。运维移交场景中,建议按设备、文档、配置、工单等维度拆分模型。
3.1.1 基础模型定义示例
from pydantic import BaseModel, Field
from typing import Optional, List
from datetime import datetime
class DeviceBase(BaseModel):
"""设备基础模型"""
device_code: str = Field(..., description="设备编码,全局唯一")
device_name: str = Field(..., max_length=128, description="设备名称")
device_type: str = Field(..., description="设备类型:server/network/storage")
rack_position: Optional[str] = Field(None, max_length=64, description="机柜位置")
status: int = Field(default=1, ge=0, le=1, description="状态:0-停用,1-启用")
class DeviceCreate(DeviceBase):
"""创建设备请求模型"""
pass
class DeviceResponse(DeviceBase):
"""设备响应模型,包含数据库生成的字段"""
id: int
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True # 支持 ORM 模式
3.2 字段类型与校验规则
Pydantic 提供丰富的字段类型和校验器,能够覆盖运维数据移交中的绝大多数场景。下表列出常用字段类型及其适用场景:
| 字段类型 | Pydantic 类型 | 运维场景示例 | 校验要点 |
|---|---|---|---|
| 字符串 | str |
设备编码、文档标题、IP 地址 | min_length、max_length、regex |
| 整数 | int |
设备状态、端口数量、机柜层数 | ge、le、multiple_of |
| 浮点数 | float |
设备功率、温度阈值、版本号 | ge、le、decimal_places |
| 布尔值 | bool |
是否启用、是否已移交、是否备份 | 自动转换 0/1、yes/no |
| 日期时间 | datetime |
移交时间、维保到期、巡检日期 | ge、le 时间范围 |
| 枚举 | Enum |
设备类型、文档分类、移交阶段 | 限定可选值集合 |
| 列表 | List[type] |
关联文档 ID 列表、IP 地址列表 | min_items、max_items |
| 可选字段 | Optional[type] |
备注、扩展属性、非必填关联 | 默认 None |
3.2.1 高级校验器应用
当内置校验无法满足业务规则时,使用 @validator 或 @field_validator 实现自定义逻辑:
from pydantic import BaseModel, field_validator
import re
class DeviceTransferModel(BaseModel):
device_code: str
ip_address: str
transfer_date: str
@field_validator('device_code')
@classmethod
def validate_device_code(cls, v):
"""设备编码格式:DC-{机房}-{楼层}-{机柜}-{序号}"""
pattern = r'^DC-[A-Z]{2,4}-\d{2}-[A-Z]\d{2}-\d{3}$'
if not re.match(pattern, v):
raise ValueError('设备编码格式不正确,示例:DC-BJ01-03-A12-001')
return v
@field_validator('ip_address')
@classmethod
def validate_ip(cls, v):
parts = v.split('.')
if len(parts) != 4 or not all(0 <= int(p) <= 255 for p in parts):
raise ValueError('IP 地址格式无效')
return v
最佳实践: 将校验规则与业务语义对齐。例如设备编码的正则校验应反映实际的编码规范,而非简单的长度限制。建议在模型注释中写明校验示例,方便前端与测试人员理解。
3.3 数据模型与数据库表映射
在 FastAPI + SQLAlchemy 架构中,Pydantic 模型负责 API 层数据交换,ORM 模型负责数据库持久化。两者通过 映射配置 实现双向转换。
3.3.1 ORM 模型定义
from sqlalchemy import Column, Integer, String, DateTime, SmallInteger
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime
Base = declarative_base()
class DeviceORM(Base):
__tablename__ = 'devices'
id = Column(Integer, primary_key=True, autoincrement=True)
device_code = Column(String(64), unique=True, nullable=False, index=True)
device_name = Column(String(128), nullable=False)
device_type = Column(String(32), nullable=False)
rack_position = Column(String(64), nullable=True)
status = Column(SmallInteger, default=1)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
3.3.2 映射关系对照表
| 映射维度 | Pydantic 模型 | SQLAlchemy ORM | 说明 |
|---|---|---|---|
| 字段定义 | Field(...) |
Column(...) |
类型与约束需保持语义一致 |
| 必填/可选 | Optional / 无默认值 |
nullable=True/False |
API 层可选字段对应数据库可空字段 |
| 默认值 | Field(default=...) |
Column(default=...) |
建议在 ORM 层也设置默认值 |
| 唯一约束 | @validator 校验 |
unique=True |
数据库层保证唯一性,API 层做前置校验 |
| 索引 | 不涉及 | index=True |
高频查询字段建议加索引 |
| 关联关系 | List[int] 等 |
relationship |
API 层传递 ID 列表,ORM 层处理关联 |
3.3.3 模型转换与响应序列化
from sqlalchemy.orm import Session
def create_device(db: Session, device_in: DeviceCreate) -> DeviceResponse:
"""将 Pydantic 模型转换为 ORM 模型并写入数据库"""
device_orm = DeviceORM(**device_in.model_dump())
db.add(device_orm)
db.commit()
db.refresh(device_orm)
# ORM 模型自动转换为 Pydantic 响应模型
return DeviceResponse.model_validate(device_orm)
常见陷阱: 不要将 Pydantic 模型直接作为数据库模型使用。Pydantic 模型包含校验逻辑和序列化方法,而 ORM 模型包含会话管理和延迟加载特性。两者职责分离是架构清晰的关键。
3.4 数据模型设计流程
下图展示了从业务需求到数据库表映射的完整设计流程:
3.5 模型设计避坑指南
- 字段类型不匹配: Pydantic 的
int与数据库的SmallInteger或BigInteger需显式对应,避免溢出或精度丢失。 - 忽略时区处理: 运维数据涉及多地机房,建议统一使用 UTC 时间存储,API 层按需转换时区。
- 过度校验: 数据库层已有唯一约束和默认值,API 层无需重复实现相同逻辑,只需做业务语义校验。
- 模型复用混乱: 创建、更新、响应模型应分开定义,避免使用同一个模型导致字段暴露或校验冲突。
- 忽略索引设计: 设备编码、文档 ID 等高频查询字段必须在 ORM 模型中显式声明索引。
移交场景建议: 在设备移交接口中,建议为
device_code、transfer_status、transfer_date 三个字段建立联合索引,可覆盖 90% 以上的查询场景。