DEV Community

MrChoke
MrChoke

Posted on

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

ปกติถ้าหากจำนวน 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
Enter fullscreen mode Exit fullscreen mode

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

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

stackoverflow

ตัวอย่าง 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]

Enter fullscreen mode Exit fullscreen mode

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

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

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

arg.replace(default=Form(default=arg.default) if arg.default is not inspect._empty else Form(...))
Enter fullscreen mode Exit fullscreen mode

ตัวอย่าง 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

Enter fullscreen mode Exit fullscreen mode

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

Swagger

Swagger input with default

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

Discussion (4)

Collapse
veer66 profile image
Vee Satayamas

คือเอาไว้ทำอะไรนะครับ ?

Collapse
mrchoke profile image
MrChoke Author

แค่เอาไว้ลดการเขียน arguments ใน router function ให้สั้นลงครับ ถ้าไม่ใช้ model ก็ต้องยัดทั้งหมดลงใน arguments และอีกอย่างก็สามารถเอา model ไป reused ได้ด้วยครับ

Collapse
veer66 profile image
Vee Satayamas

Python นี่รับ request เข้ามาตัวแปรเดียวไม่ได้ใช่เปล่าครับ เลยต้องทำแบบนี้ ?

Thread Thread
mrchoke profile image
MrChoke Author

ถ้าเป็น json ส่งมาเป็น object แล้วใช้ model รับแบบนี้ได้เเลย แต่ form data มันมาแบบ field หรืออาจจะมีแล้วหว่ายังไม่ได้อ่าน doc ใหม่ 😂 ครับ