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

FastAPI如何无需循环从HTML表单获取Map列表?

问题

我想在FastAPI中从HTML表单直接获取以下格式的数据:

[EmploymentHistory(work_description='3wert', location='ADSF'), EmploymentHistory(work_description='3wert', location='cghvjbn,m')]

或其字典列表形式:

[{'work_description': '3wert', 'location': '2q3wer'}, {'work_description': '3wert', 'location': 'ADSF'}]

当前已有的代码如下:

FastAPI端点代码

@app.post('/formz', response_class=HTMLResponse)
async def post_formz(request: Request,data: EmploymentHistory = Depends(EmploymentHistory.as_form)):
    print(data)
    return templates.TemplateResponse("resume/create.html",{"request": request})

Pydantic模型代码

class EmploymentHistory(BaseModel):
    work_description: str 
    location: str 

    @classmethod
    def as_form(
        cls,
        work_description: str = Form(...),
        location: str = Form(...)
    ):
        return cls(
            work_description=work_description,
            location= location,
        )

HTML表单代码

<form method = 'POST' enctype="multipart/form-data">
    <div>
        <p>Employment History</p>
    </div>
    <div>
        <ol id="employment_history-form">
            <li>
                <label for= 'work_description'>Work discription</label>
                <input type='work_description' required="required" name='work_description' placeholder='jone briggs' />
                <label for= 'location'>Location</label>
                <input type='location' required="required" name='location' placeholder='jone briggs' />
            </li> 
            <li>
                <label for= 'work_description'>Work discription</label>
                <input type='work_description' required="required" name='work_description' placeholder='jone briggs' />
                <label for= 'location'>Location</label>
                <input type='location' required="required" name='location' placeholder='jone briggs' />
            </li>     
        </ol>
    </div>
    <button type="submit" >create history</button>
</form>

尝试过以下方案但均报错:

  1. 直接用List包装模型:
@app.post('/formz', response_class=HTMLResponse)
async def post_formz(request: Request,data: List[EmploymentHistory] = Depends(List[EmploymentHistory.as_form])):

错误信息:

{"detail":[{"loc":["query","args"],"msg":"field required","type":"value_error.missing"},{"loc":["query","kwargs"],"msg":"field required","type":"value_error.missing"}]}
  1. 嵌套模型类:
class User(BaseModel):
    employments_history: List[EmploymentHistory]

    @classmethod
    def as_form(
        cls,
        employments_history: List[EmploymentHistory] = List[EmploymentHistory.as_form],
    ):
        return cls(
            employments_history=employments_history,
        )

错误信息:

{"detail":[{"loc":["body"],"msg":"value is not a valid list","type":"type_error.list"}]}
  1. 移除嵌套类的列表:
class User(BaseModel):
    employments_history: EmploymentHistory

    @classmethod
    def as_form(
        cls,
        employments_history: EmploymentHistory = EmploymentHistory.as_form,
    ):
        return cls(
            employments_history=employments_history,
        )

错误信息:

{"detail":[{"loc":["body"],"msg":"value is not a valid dict","type":"type_error.dict"}]}

目前通过zip循环实现了需求,但希望能避免在端点中写循环,寻求更简洁的方案。

可行方案

方案1:自定义依赖直接返回目标列表

先修正HTML表单的两个问题:

  • 输入框的type属性错误,应改为text(浏览器会默认解析为text,但显式声明更规范)
  • 表单字段名改为work_description[]和location[],让FastAPI自动解析为列表

修改后的HTML表单关键部分:

<li>
    <label for='work_description_0'>Work discription</label>
    <input type='text' required="required" name='work_description[]' placeholder='jone briggs' />
    <label for='location_0'>Location</label>
    <input type='text' required="required" name='location[]' placeholder='jone briggs' />
</li> 
<li>
    <label for='work_description_1'>Work discription</label>
    <input type='text' required="required" name='work_description[]' placeholder='jone briggs' />
    <label for='location_1'>Location</label>
    <input type='text' required="required" name='location[]' placeholder='jone briggs' />
</li>

然后创建自定义依赖函数,接收列表并直接转换为List[EmploymentHistory]:

from typing import List
from fastapi import Form, Depends

async def get_employment_history_list(
    work_description: List[str] = Form(...),
    location: List[str] = Form(...)
) -> List[EmploymentHistory]:
    # 用列表推导式替代显式循环,代码更简洁
    return [EmploymentHistory(work_description=w, location=l) for w, l in zip(work_description, location)]

最后修改端点代码,直接使用该依赖:

@app.post('/formz', response_class=HTMLResponse)
async def post_formz(request: Request, data: List[EmploymentHistory] = Depends(get_employment_history_list)):
    print(data)  # 直接得到目标格式的列表
    return templates.TemplateResponse("resume/create.html", {"request": request})

方案2:封装模型转换方法

如果希望保留EmploymentList模型,可以在模型内部封装转换逻辑,避免端点代码中出现循环:

修改EmploymentList模型:

class EmploymentList(BaseModel):
    work_description: List[str] 
    location: List[str] 

    @classmethod
    def as_form(
        cls,
        work_description: List[str] = Form(...),
        location: List[str] = Form(...)
    ):
        return cls(
            work_description=work_description,
            location= location
        )
    
    # 封装转换逻辑
    def to_employment_history(self) -> List[EmploymentHistory]:
        return [EmploymentHistory(work_description=w, location=l) for w, l in zip(self.work_description, self.location)]

端点代码简化为:

@app.post('/formz', response_class=HTMLResponse)
async def post_formz(request: Request, data: EmploymentList = Depends(EmploymentList.as_form)):
    employment_list = data.to_employment_history()
    print(employment_list)
    return templates.TemplateResponse("resume/create.html", {"request": request})

说明

两种方案本质都是通过映射列表元素生成目标模型,但将循环逻辑(或列表推导式)封装到了依赖函数或模型方法中,端点代码保持简洁,符合需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 07:50:57