You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Pydantic外部验证中保持Pydantic风格的错误格式?

解决方案:用Pydantic V2实现数据库级验证的标准化多错误返回

针对你提到的数据库/全局状态类验证无法在Pydantic模型中处理、且需要聚合多错误返回标准化格式的需求,以下是基于Pydantic V2的官方方案实现:

1. 定义业务异常类

先把数据库相关的验证异常封装成专用类,便于后续捕获和处理:

class UserAlreadyExists(Exception):
    def __init__(self, username: str):
        self.username = username
        super().__init__(f"用户名 '{username}' 已被占用")

class DocumentNotFound(Exception):
    def __init__(self, doc_id: int, index: int):
        self.doc_id = doc_id
        self.index = index
        super().__init__(f"第{index+1}个关联文档ID {doc_id} 不存在")

class DocumentNameDuplicate(Exception):
    def __init__(self, name: str):
        self.name = name
        super().__init__(f"文档名称 '{name}' 已存在")

2. 封装Pydantic错误生成工具

Pydantic V2的ErrorDetails类可以直接生成符合格式要求的错误项,写个工具函数简化创建:

from pydantic import ErrorDetails

def build_validation_error(loc: list[str | int], msg: str, error_type: str = "value_error") -> ErrorDetails:
    return ErrorDetails(loc=loc, msg=msg, type=error_type)

3. 在API端点中聚合错误并抛出ValidationError

在业务逻辑中收集所有验证错误,最后一次性抛出Pydantic的ValidationError——FastAPI会自动将其转换成你需要的标准化响应格式:

from fastapi import FastAPI
from pydantic import BaseModel, ValidationError
from typing import List

app = FastAPI()

# Pydantic模型定义
class UserCreate(BaseModel):
    username: str
    email: str

class DocumentCreate(BaseModel):
    name: str
    linked_doc_ids: List[int]

# 创建用户接口
@app.post("/users/")
def create_user(user: UserCreate):
    errors = []
    try:
        # 数据库原子性创建操作(自带唯一性检查)
        db.create_user(user.model_dump())
    except UserAlreadyExists as e:
        errors.append(build_validation_error(
            loc=["body", "username"],
            msg=str(e),
            error_type="value_error.username_duplicate"
        ))
    
    if errors:
        raise ValidationError(errors=errors, model=UserCreate)
    return {"status": "success"}

# 创建文档接口
@app.post("/documents/")
def create_document(doc: DocumentCreate):
    errors = []
    # 检查文档名称唯一性
    try:
        db.check_doc_name_unique(doc.name)
    except DocumentNameDuplicate as e:
        errors.append(build_validation_error(
            loc=["body", "name"],
            msg=str(e),
            error_type="value_error.doc_name_duplicate"
        ))
    
    # 批量检查关联文档ID合法性
    for idx, doc_id in enumerate(doc.linked_doc_ids):
        try:
            db.get_document(doc_id)
        except DocumentNotFound as e:
            errors.append(build_validation_error(
                loc=["body", "linked_doc_ids", idx],
                msg=str(e),
                error_type="value_error.linked_doc_not_found"
            ))
    
    if errors:
        raise ValidationError(errors=errors, model=DocumentCreate)
    
    db.create_document(doc.model_dump())
    return {"status": "success"}

4. 最终错误响应示例

当同时存在多个验证错误时,返回的格式会和Pydantic原生错误完全一致,比如:

{
  "detail": [
    {
      "loc": [
        "body",
        "name"
      ],
      "msg": "文档名称 '月度报告.pdf' 已存在",
      "type": "value_error.doc_name_duplicate"
    },
    {
      "loc": [
        "body",
        "linked_doc_ids",
        2
      ],
      "msg": "第3个关联文档ID 123 不存在",
      "type": "value_error.linked_doc_not_found"
    }
  ]
}

关键注意事项

  • 数据库原子性:必须依赖数据库的原子操作(比如UNIQUE约束+捕获异常)实现唯一性检查,避免先查询再创建的竞态条件。
  • 错误类型自定义:error_type字段可以根据业务场景自定义,方便前端做针对性错误处理。
  • FastAPI自动处理:FastAPI会自动捕获ValidationError并返回422状态码,无需额外编写异常处理器(若需自定义格式可单独配置)。

内容的提问来源于stack exchange,提问作者Luke Nelson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 01:07:50