การใช้ Pydantic Model กับ Form Data ใน FastAPI

Supphachoke Suntiwichaya
NECTEC
Published in
2 min readFeb 25, 2022

ปกติถ้าหากจำนวน Fields ใน Form มีไม่มากก็ไม่มีปัญหาเราสามารถเขียนแบบปกติได้

เช่น

@app.post(“/abc”, response_model=bool)
def abc(id: int = Form(…), name: str = Form(default=None),status: str = Form(default=”single”)):
return True

แต่ถ้า Fields มีเยอะ ๆ สัก 10 Fields ขึ้นไป Code ก็จะเริ่มดูยากยาวมาก การใช้ Pydantic model เข้ามาช่วยก็จะจัดการง่ายขึ้น ปกติเราใช้ Pydantic model ใน Body แบบ JSON กันอยู่แล้วแต่สำหรับ Form Data จะมีลูกเล่นนิดหนึ่ง ค้นหาใน internet ไปเจอวิธีที่คิดว่าง่ายที่สุดละเลยบันทึกไว้สักหน่อย

ต้นฉบับที่ผมนำมาใช้

https://stackoverflow.com/a/65547551

ตัวอย่าง model.py

from typing import Optional
from fastapi import Form
from pydantic import BaseModel
def form_body(cls):
cls.__signature__ = cls.__signature__.replace(
parameters=[
arg.replace(default=Form(default=arg.default) if arg.default is not inspect._empty else Form(…))
for arg in cls.__signature__.parameters.values()
]
)
return cls
@form_body
class Profile(BaseModel):
passport_no: Optional[str]
hn: Optional[str]
patient_guid: Optional[str]
prefix: str = “นาย”
first_name: str
last_name: str
prefix_eng: str = “Mr”
first_name_eng: Optional[str]
middle_name_eng: Optional[str]
last_name_eng: Optional[str]
gender: int = 1
birth_date: Optional[str]
mobile_phone: Optional[str]
installed_line_connect: Optional[str]
address: Optional[str]
moo: Optional[str]
road: Optional[str]
chw_code: Optional[str]
amp_code: Optional[str]
tmb_code: Optional[str]
address_full_thai: Optional[str]
address_full_english: Optional[str]
nationality: Optional[str]

ในส่วนของ Form มันจะมีหลัก ๆ อยู่สามแบบคือ

* บังคับ
* มีค่า Default
* มีก็ได้ไม่มีก็ได้

ผมเลยปรับในตัว Decorators เพิ่มเติมให้มันปรับตาม Model ที่เรากำหนดไว้


arg.replace(default=Form(default=arg.default) if arg.default is not inspect._empty else Form(…))

ตัวอย่าง Router

from model import Profile
from fastapi import Depends, FastAPI, File, UploadFile
app = FastAPI()
@app.post(“/abc”, response_model=bool)
def abc(profile: Profile = Depends(Profile), photo: UploadFile = File(…)):
return True

ตอน Render ก็จะได้ประมาณนี้

Swagger
Swagger input with default

ลองเอาไปประยุกต์ใช้กันดูครับ

--

--