如何通过Pydantic从HTML表单返回嵌套列表结构数据?
解决FormData转嵌套Pydantic模型的问题
要实现将扁平的FormData解析为包含ingredients嵌套数组的结构,需要手动处理表单数据的分组与转换,因为FastAPI默认不会自动将重复字段映射为嵌套数组。以下是具体实现步骤:
1. 编写表单数据解析辅助函数
该函数负责将扁平的FormData字段分组,转换为符合RecipeUpdate模型的字典结构:
from fastapi import FormData from typing import Optional, Dict, List def parse_form_to_recipe_update(form_data: FormData) -> Dict: # 提取基础字段 recipe_data = { "name": form_data.get("name"), "description": form_data.get("description") } # 定义食材相关字段的顺序 ing_field_names = ["ing_id", "ing_qty", "ing_type", "ing_name"] ingredients = [] # 获取每个食材字段的所有值(重复字段会返回列表) field_value_lists = {field: form_data.getlist(field) for field in ing_field_names} # 计算食材条目数量(取最长字段列表的长度) total_ingredients = max(len(vals) for vals in field_value_lists.values()) # 按索引分组,构建每个食材的字典 for idx in range(total_ingredients): ing_dict = {} for field in ing_field_names: # 取出对应索引的值,不存在则设为None raw_val = field_value_lists[field][idx] if idx < len(field_value_lists[field]) else None # 类型转换:将ID和数量转为整数(处理表单字符串) if field in ["ing_id", "ing_qty"] and raw_val is not None: try: ing_dict[field] = int(raw_val) except ValueError: ing_dict[field] = None else: ing_dict[field] = raw_val ingredients.append(ing_dict) recipe_data["ingredients"] = ingredients return recipe_data
2. 修改FastAPI路由
在路由中调用解析函数,将转换后的数据传入Pydantic模型进行验证:
from fastapi import APIRouter, Request, Depends from sqlalchemy.orm import Session from your_module import get_db, RecipeUpdate # 替换为实际的导入路径 router = APIRouter() @router.post("/edit/{recipe_id}", response_model=RecipeUpdate) async def edit_select_recipe(request: Request, recipe_id: int, db: Session = Depends(get_db)): form_data = await request.form() # 解析表单数据为模型所需结构 parsed_data = parse_form_to_recipe_update(form_data) # 用Pydantic模型验证数据 recipe_update = RecipeUpdate(**parsed_data) # 此处添加数据库更新逻辑,例如: # db.query(Recipe).filter(Recipe.id == recipe_id).update(recipe_update.dict(exclude_unset=True)) # db.commit() return recipe_update
关键说明
- 重复字段处理:使用
form_data.getlist(field)获取重复字段的所有值,而非get()(后者仅返回最后一个值)。 - 类型转换:表单数据默认是字符串类型,需手动将
ing_id和ing_qty转为整数,并处理转换失败的情况。 - 兼容性:即使部分食材字段缺失,代码会自动将对应值设为
None,符合Pydantic模型中Optional的定义。
内容的提问来源于stack exchange,提问作者Chris Plummer
相关产品推荐
相关产品推荐

