2023年8月31日木曜日

fastapiでリスト内にデータ構造が含まれるようなフォームデータを扱う方法

fastapiでリスト内にデータ構造が含まれるようなフォームデータを扱う方法

概要

リクエストでいうと以下のようなリクエストが来ることを想定して作ります

  • curl -XPOST -H "Action: Hoge" localhost:8000/submit/ -H "content-type: application/x-www-form-urlencoded" -d "data[0][lang]=ruby&data[0][framework]=sinatra&data[1][lang]=python&data[1][framework]=fastapi"

data はリストになっていてその中に lang, framework というデータ構造を持つオブジェクトが入っているイメージです

環境

  • Python 3.11.3
  • fastapi 0.100.1
  • pydantic 2.1.1

サンプルコード

from fastapi import Depends, FastAPI, Request
from pydantic import BaseModel, field_validator

app = FastAPI()


# リスト内のデータ構造を管理するクラス
class Profile(BaseModel):
    lang: str
    framework: str

    @field_validator("lang")
    @classmethod
    def validate_lang(cls, v: str):
        if v not in ["ruby", "python"]:
            raise ValueError()
        return v


# リクエストのリスト部分を管理するクラス
class RequestData(BaseModel):
    profile: list[Profile]


# 独自のフォームパーサ
async def parse_form_data(request: Request) -> RequestData:
    form_data = await request.form()
    print(form_data)
    request_data: list[Profile] = []
    # とりあえず 100 リストまで対応
    for i in range(100):
        # lang が含まれているか確認、0から始まっていないもしくは指定のデータ構造がない場合などは終了
        lang = form_data.get(f"data[{i}][lang]")
        if lang is None:
            break
        # framework が含まれているか確認、0から始まっていないもしくは指定のデータ構造がない場合などは終了
        framework = form_data.get(f"data[{i}][framework]")
        if framework is None:
            break
        # データ構造の作成
        profile = Profile(lang=str(lang), framework=str(framework))
        # リストへの追加
        request_data.append(profile)
    # Depends の返り値を指定
    return RequestData(profile=request_data)


@app.post("/submit/")
async def submit_form(form_data: RequestData = Depends(parse_form_data)):
    # Depends で parse_form_data を指定することでリクエストデータを独自でパースする
    return form_data

動作確認

  • curl -XPOST -H "Action: Hoge" localhost:8000/submit/ -H "content-type: application/x-www-form-urlencoded" -d "data[0][lang]=ruby&data[0][framework]=sinatra&data[1][lang]=python&data[1][framework]=fastapi"

は通ります

  • curl -XPOST -H "Action: Hoge" localhost:8000/submit/ -H "content-type: application/x-www-form-urlencoded" -d "data[0][lang]=ruby&data[0][framework]=sinatra&data[1][lang]=python3&data[1][framework]=fastapi"

はエラーになります

最後に

そもそもこういったリクエストは form ではなく json で受け取るべきだと思います
ファイルを送信したくどうしても form を使わざるを得ない場合は仕方ないかなと思います

0 件のコメント:

コメントを投稿