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 数据模型设计流程

下图展示了从业务需求到数据库表映射的完整设计流程:

数据模型设计流程 业务需求分析 Pydantic 模型定义 字段类型与校验 SQLAlchemy ORM 模型 数据库表映射 FastAPI 接口集成 💡 设计原则:先定义 Pydantic 模型明确接口契约,再映射到 ORM 模型,最后生成数据库表。避免反向设计导致接口与存储耦合。

3.5 模型设计避坑指南

  • 字段类型不匹配: Pydantic 的 int 与数据库的 SmallInteger 或 BigInteger 需显式对应,避免溢出或精度丢失。
  • 忽略时区处理: 运维数据涉及多地机房,建议统一使用 UTC 时间存储,API 层按需转换时区。
  • 过度校验: 数据库层已有唯一约束和默认值,API 层无需重复实现相同逻辑,只需做业务语义校验。
  • 模型复用混乱: 创建、更新、响应模型应分开定义,避免使用同一个模型导致字段暴露或校验冲突。
  • 忽略索引设计: 设备编码、文档 ID 等高频查询字段必须在 ORM 模型中显式声明索引。
移交场景建议: 在设备移交接口中,建议为 device_code、transfer_status、transfer_date 三个字段建立联合索引,可覆盖 90% 以上的查询场景。