fastAPIでfileのアップロードとBaseModelを継承したクラスのポストを同時にやろうとすると422エラーになってつまずいたので、その解決策の備忘録。
ファイルとモデルを同時にポストする (失敗)
コード
from fastapi import FastAPI, File, UploadFile
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
name: str = "Toshi"
@app.post("/uploadfile/")
async def upload_file(user: User, file: UploadFile = File(...)):
return {"filename": file.filename, 'user': user}
実行結果
/docs から実行すると、422 Error: Unprocessable Entity
。Response bodyは以下。
{
"detail": [
{
"loc": [
"body",
"user"
],
"msg": "value is not a valid dict",
"type": "type_error.dict"
}
]
}
Response headers
content-length: 95
content-type: application/json
date: Tue,31 Aug 2021 14:11:22 GMT
server: uvicorn
ファイルとモデルを同時にポストする (成功)
コード
from fastapi import FastAPI, File, UploadFile, Body
from pydantic import BaseModel
import json
app = FastAPI()
class User(BaseModel):
name: str = "Toshi"
@classmethod
def __get_validators__(cls):
yield cls.validate_to_json
@classmethod
def validate_to_json(cls, value):
if isinstance(value, str):
return cls(**json.loads(value))
return value
@app.post("/uploadfile/")
async def upload_file(user: User = Body(...), file: UploadFile = File(...)):
return {"filename": file.filename, 'user': user}
ポイント
BaseModelを継承したクラスをポストするときは application/x-www-form-urlencoded (curl の -d オプション)になる。ファイルをアップロードするときは multipart/form-data (curl の -f オプション)になる。User = Body(...)
とすることで -f にそろえる。
Userモデルでstringの入力をjson形式に変換する処理を記述する。
参考
- First Steps – FastAPI
- Using UploadFile and Pydantic model in one request · Issue #2257 · tiangolo/fastapi
- curl の POST オプション -d と -F の違いから、改めて MIME type を学ぶ – Qiita
Comments