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

FastAPI 0.111.0中Pydantic V2表单数据field_validation未执行问题

解决FastAPI + Pydantic V2中@as_form装饰器不触发field_validator的问题

问题核心在于你自定义的@as_form装饰器没有触发Pydantic V2的完整验证流程,导致字段类型转换和验证器都未生效,下面是具体修复方案:

1. 适配Pydantic V2的字段验证器写法

Pydantic V2的field_validator需要指定mode="before"来处理原始的表单字符串输入——毕竟表单提交的布尔值(比如on/off、true/false)都是字符串类型,得先转成布尔再验证:

from pydantic import BaseModel, field_validator

class FormModel(BaseModel):
    c: bool

    @field_validator('c', mode='before')
    def parse_bool(cls, v):
        if isinstance(v, str):
            lower_v = v.lower()
            if lower_v in ('true', '1', 'on'):
                return True
            elif lower_v in ('false', '0', 'off', ''):
                return False
            raise ValueError('无效的布尔值')
        return v

2. 修复@as_form装饰器逻辑

原来的自定义@as_form大概率只是简单映射表单参数到模型字段,但没调用Pydantic的model_validate方法触发验证。修改装饰器,强制用model_validate创建模型实例:

from fastapi import Form
from typing import Type, TypeVar

T = TypeVar('T', bound=BaseModel)

def as_form(cls: Type[T]) -> Type[T]:
    def as_form_func(**kwargs):
        # 关键:用model_validate触发完整的验证和类型转换流程
        return cls.model_validate(kwargs)
    
    # 为模型每个字段生成对应的Form参数
    for field_name, field in cls.model_fields.items():
        field_info = field.field_info
        as_form_func.__annotations__[field_name] = field.annotation
        as_form_func.__defaults__ = as_form_func.__defaults__ or ()
        # 处理必填/可选字段的默认值
        if field.default is not None:
            as_form_func.__defaults__ += (field.default,)
        else:
            as_form_func.__defaults__ += (...,)
        # 为参数添加Form装饰器
        setattr(as_form_func, field_name, Form(description=field_info.description))
    
    cls.as_form = as_form_func
    return cls

3. 完整使用示例

把装饰器绑定到模型,再在FastAPI接口中调用:

from fastapi import FastAPI

app = FastAPI()

@as_form
class FormModel(BaseModel):
    c: bool

    @field_validator('c', mode='before')
    def parse_bool(cls, v):
        if isinstance(v, str):
            lower_v = v.lower()
            if lower_v in ('true', '1', 'on'):
                return True
            elif lower_v in ('false', '0', 'off', ''):
                return False
            raise ValueError('无效的布尔值')
        return v

@app.post('/submit-form')
async def submit_form(form_data: FormModel = FormModel.as_form()):
    return {'c的值': form_data.c, 'c的类型': type(form_data.c)}

原方法失效原因

Pydantic V2调整了验证流程,不像V1那样实例化时自动隐式转换类型。如果你的旧@as_form只是用cls(**kwargs)创建实例,会跳过验证器和类型转换逻辑,导致c始终是字符串。而model_validate会强制走完整的验证流程,包括你的自定义验证器。

测试时,提交c=true、c=on、c=1都会被正确转成布尔值;提交无效值(比如c=abc)会返回422错误,符合预期。

内容的提问来源于stack exchange,提问作者Himal Acharya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 12:52:42