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
相关产品推荐
相关产品推荐

