Frame Work/FastAPI

[FastAPI] Pydantic BaseModel로 Form 데이터 처리하기

j4ko 2024. 3. 11. 22:20
728x90
반응형

목차

     

    개요 

    어째선지 FastAPI의 문서에는 Pydantic의 BaseModel을 이용해서 Form 데이터를 처리하는 경우의 코드 예제는 보이지 않는다. 다양한 시도 끝에 BaseModel을 이용해 Form 데이터를 처리할 수 있는 방법을 찾았지만 꼼수를 부린 방법이라 그런지 FastAPI Log에서 다음과 같은 Warning 이 나고 있음을 발견했다.

     

    해당 Warning을 인지하고 Pydantic의 BaseModel에서 Form 데이터를 처리할 수 있는 다른 방법을 조사한 뒤 해결했고 이 포스팅은 그 과정에 대한 것이다.

     

     

    1. PydanticJsonSchemaWarning

    먼저 "개요"에서도 언급했듯 PydanticJsonSchemaWarning을 발견하게 된 경위이다.

     

    FastAPI에는 API Endpoint의 함수에 Type Hint을 통해 해당 EndPoint가 어떤 데이터를 반환할지 표시할 수 있는게 가능하다.

     

    앞서 언급한 "꼼수"라고 표현된 방법이 적용된 BaseModel을 End Point 함수에 Type Hint를 적용한 뒤 Swagger 문서를 'F5' 눌러  새로고침할 때 PydanticJsonSchemaWarning이 일어났다.

     

    '꼼수'라고 표현된 BaseModel은 다음과 같이 생겼다.

    from pydantic import BaseModel
    from fastapi.routing import APIRouter
    
    test_router = APIRouter(tags=['PET'], prefix='/api/v1')
    
    class RequestTestDto(BaseModel):
        is_selected: bool = Field(Form(default=None))
    
    @test_router.put(path="")
    def test_api(
            request: RequestTestDto = Depends(RequestTestDto),
    ) -> RequestTestDto:
        return JSONResponse(status_code=200, content=request.json())

    "Warning"이 발생하는 것이다 보니 치명적인 에러를 일으킬 가능성은 낮지만 Log에 떠있으니 아무래도 신경이 쓰였기에 Pydantic에 BaseModel을 이용해 Form 데이터를 처리하는 다른 방법을 찾아봐야 했다.

     

    2.  Pydantic BaseModel에서 Form 데이터 처리하기

    해결 방법을 찾던 중에 StackOverflow에 올라온 글을 참고하여 해결하니 더 이상 PydanticJsonSchemaWarning이 발생하진 않았다. 이 방법을 간단히 요약하자면  BaseModel에 classmethod 데코레이터를 이용하여 Form 데이터를 처리하는 방법이다.

     

    이 방법의 코드 예제는 다음과 같이 생겼다.

    from fastapi import Form
    from pydantic import BaseModel
    
    class RequestTestDto(BaseModel):
        is_selected: bool
    
        @classmethod
        def as_form(cls, is_selected: bool = Form(None)):
            return cls(is_selected=is_selected
          
      
    @test_router.put(path="")
    def test_api(
            request: RequestTestDto = Depends(RequestTestDto.as_form),
    ) -> RequestTestDto:
        return JSONResponse(status_code=200, content=request.json())

    request를 통해 입력받을 데이터를 classmethod 데코레이터를 이용해 정의한 함수의 인자로 받는 것이다. 이렇게 정의된 BaseModel은 FastAPI의 Depands와 함께 이용하면 Pydantic의 BaseModel을 이용해 Form 데이터를 처리하는 게 가능해진다.

     

    물론 pydantic2.0부터 도입된 field_validator 역시 정상적으로 작동한다.

    class RequestTestDto(BaseModel):
        is_selected: bool
    
        @classmethod
        def as_form(cls, is_selected: bool = Form(None)):
            return cls(is_selected=is_selected)
    
        @field_validator('is_selected')
        def allow_is_selected(cls, value):
            if value is False:
                raise HTTPException(status_code=400, detail="is_selected is False")
            return value

     

     

    마치며

    처음에는 PydanticJsonSchemaWarning이 발생하는 원인이 python의 bool 이 아닌 pydantic에서 정의된 타입(StrictBool)을 쓰지 않아서 발생하게 된 것인가 지레 짐작했지만 다시 조사하는 과정에서 포인트를 다시 잡을 수 있었다.

     

    Pydanic의 BaseModel을 이용해 Form 데이터를 처리하는 방법 또한 비슷한 맥락인데 여러 케이스를 경험해 보는 것이 역시 중요하다는 생각이 든다.

    728x90
    반응형