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

FastAPI表单绑定模型报错“str type expected”的解决方法咨询

解决FastAPI中Form参数绑定Pydantic模型的422错误问题

问题场景

开发Banner更新接口时,需同时上传两张图片(背景图、前景图)并提交Banner基础信息(title、sub_title、path_url、desc)。使用Form和UploadFile实现时,将BannerInputSchema作为Form参数传入会触发422验证错误,但单独传入每个属性可正常运行。

现有代码

Schema模型

from pydantic import BaseModel

class BannerInputSchema(BaseModel):
    """Banner Create Model"""
    title: str
    sub_title: str | None = None
    path_url: str | None = None
    desc: str | None = None

    class Config:
        orm_mode = True

路由代码

from fastapi import APIRouter, Form, File, UploadFile
from your_module import ResponseSchema, BannerService

router = APIRouter()

@router.put('/update_banner/{id}',
            response_model=ResponseSchema,
            response_model_exclude_none=True)
async def update_banner(
        banner_id: str = Form(),
        banner_info: BannerInputSchema = Form(),
        b_img: UploadFile = File(default=None, description='upload background img'),
        f_img: UploadFile = File(default=None, description='upload front img'),
):
    result = await BannerService.update_banner(
        banner_id=banner_id,
        banner_info=banner_info,
        b_img=b_img,
        f_img=f_img,
    )

    return ResponseSchema(detail='Success Banner Data', result=result)

触发的错误

{
  "detail": [
    {
      "loc": [
        "body",
        "banner_info",
        "title"
      ],
      "msg": "str type expected",
      "type": "type_error.str"
    }
  ]
}

错误原因

FastAPI默认无法将扁平的表单键值对自动映射到嵌套的Pydantic模型中。直接声明banner_info: BannerInputSchema = Form()时,FastAPI会期望表单中存在名为banner_info的字段,且其值是符合模型结构的JSON字符串,但实际前端提交的是title、sub_title等独立字段,导致解析失败。

修复方案

方案1:给Pydantic模型添加表单解析方法

通过给模型添加as_form类方法,将独立的表单字段组合成模型实例,再通过Depends注入到路由中。

修改后的Schema模型:

from pydantic import BaseModel
from fastapi import Form, Depends

class BannerInputSchema(BaseModel):
    """Banner Create Model"""
    title: str
    sub_title: str | None = None
    path_url: str | None = None
    desc: str | None = None

    class Config:
        orm_mode = True

    @classmethod
    def as_form(
        cls,
        title: str = Form(...),
        sub_title: str | None = Form(None),
        path_url: str | None = Form(None),
        desc: str | None = Form(None)
    ) -> "BannerInputSchema":
        return cls(
            title=title,
            sub_title=sub_title,
            path_url=path_url,
            desc=desc
        )

修改后的路由代码:

@router.put('/update_banner/{id}',
            response_model=ResponseSchema,
            response_model_exclude_none=True)
async def update_banner(
        banner_id: str = Form(),
        banner_info: BannerInputSchema = Depends(BannerInputSchema.as_form),
        b_img: UploadFile = File(default=None, description='upload background img'),
        f_img: UploadFile = File(default=None, description='upload front img'),
):
    result = await BannerService.update_banner(
        banner_id=banner_id,
        banner_info=banner_info,
        b_img=b_img,
        f_img=f_img,
    )

    return ResponseSchema(detail='Success Banner Data', result=result)

方案2:手动组合表单字段为模型实例

若不想修改模型,可直接在路由中声明所有表单字段,手动实例化BannerInputSchema:

@router.put('/update_banner/{id}',
            response_model=ResponseSchema,
            response_model_exclude_none=True)
async def update_banner(
        banner_id: str = Form(),
        title: str = Form(),
        sub_title: str | None = Form(None),
        path_url: str | None = Form(None),
        desc: str | None = Form(None),
        b_img: UploadFile = File(default=None, description='upload background img'),
        f_img: UploadFile = File(default=None, description='upload front img'),
):
    banner_info = BannerInputSchema(
        title=title,
        sub_title=sub_title,
        path_url=path_url,
        desc=desc
    )
    result = await BannerService.update_banner(
        banner_id=banner_id,
        banner_info=banner_info,
        b_img=b_img,
        f_img=f_img,
    )

    return ResponseSchema(detail='Success Banner Data', result=result)

说明

两种方案均可解决问题:方案1符合代码复用原则,适合多接口复用同一模型的场景;方案2更直接,适合快速修复单个接口的场景。

内容的提问来源于stack exchange,提问作者Michael Mecil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 05:35:10