본문 바로가기

Python

FastAPI + Pydantic: 데이터 검증 ORM-DTO # model_validate # from_attributes

728x90
반응형

FastAPI에서 데이터 검증을 위해 Pydantic을 주로 사용.

 

Pydantic을 통해 검증을 하는 과정에서

 

ORM 객체를 DTO로 변환하는 과정을 살펴보자.

 

1. FastAPI의 request body와 response model

  request에서 어떠한 속성을 받을것인지는 router함수 안에 Body를 통해 정의.

 

  * 예시코드

from fastapi import APIRouter, Body

router = APIRouter()

@router.post("/test")
async def api_test(
    name: str = Body(...)
)
    pass

 

* Pydantic으로 모델 클래스를 만들어서 데이터 검증을 거치고,

  Depends로 의존성함수를 주입해서 관리할 수 있다. (가독성, 독립성 유지 등의 효과)

from typing_extensions import Annotated, List
from fastapi import Body
from pydantic import BaseModel, ConfigDict, model_validator

class TestModel(BaseModel):
    test1: Annotated[str, Body(...)]
    test2: Annotated[str, Body(...)]
    test_int3: Annotated[int, Body(...)]
    

def validate_test(test_data: TestModel)
     """
     검증 로직
     ...
     """
     return ...    # ex: modified test_data
 
    
@router.post("/test")
async def api_test(
    test_data: Annotated[TestModel, Depends(validate_test)]
):
	"""
    상황에 맞는 로직..
    """
    return ...

 

 

2. response되는 속성들을 제한 + 속성 유무 검증

from typing_extensions import Annotated, List
from fastapi import Body
from pydantic import BaseModel

class TestModel(BaseModel):
    test1: Annotated[str, Body(...)]
    test2: Annotated[str, Body(...)]
    test_int3: Annotated[int, Body(...)]


def validate_test(test_data: TestModel)
     """
     검증 로직
     ...
     """
     return ...    # ex: modified test_data


class ResponseTestModel(BaseModel):
    test1: str
    test2: str
    # test_int3는 의도적으로 받지 않는다.


@router.post("/test",
            response_model = ResponseTestModel    # response_model 옵션 추가
            )
async def api_test(
    test_data: Annotated[TestModel, Depends(validate_test)],
):
    ...
    return ...    # ex: modified test_data -> 이 때 response_model에 정의해준 ResponseTestModel의 양식에 따라서 test_int3는 빠지게 된다.

 

 2번의 과정을 거쳐서

  1) pydantic model에 정의되어 있는데, 입력받은 json body 데이터에는 없다면 오류를 일으켜준다.

  2) pydantic model에 정의되지 않은 속성인데, 입력받은 json body 데이터에는 있다면 필터링을 해서 받아오지 않는다.

  * pydantic model에 정의되어 있고, json body에 있는 데이터의 경우 그대로 받아온다.

 

 

3.  BaseModel.model_validate() 메소드를 통해 데이터 속성 검증

def validate_test(test_data: TestModel)
     """
     검증 로직
     ...
     """
     validated_data = TestModel.model_validate(test_data)
     return validated_data

위와 같이 model_validate 메소드를 이용해서 test_data의 개별 속성 데이터를 검증할 수도 있다.

 

* TestModel은 BaseModel을 상속받은 pydantic의 model

* pydantic의 model이 아닌 orm 객체 등을 동일한 로직으로 이용할 수도 있음

  -> 그냥은 안됨 (오류 발생)

  -> BaseModel을 상속받는 class에 from_attributes 값을 True로 지정해줘야 한다.

 

     아래와 같이 두가지 방법 모두 가능

# a.
class TestModel(BaseModel):
    ...
    
    class Config:
        from_attributes = True
        
        
# b.
class TestModel(BaseModel):
    ...
    
    model_config = ConfigDict(from_attributes = True)

 

 

* 주의할 점

  from_attributes = True가 적용되는 부분에 대한 파악

   ORM -> DTO 등으로 바로 데이터를 가져다 쓰면서 데이터 검증을 하기 위해 필요

   BaseModel.model_validate()를 하는 과정에서 from_attributes=True가 되어 있는 상태여야 진행된다.

 

 

이런 과정들이 꼭 필요할까?

Pydantic을 통한 데이터 검증이 꼭 필요할까라는 생각을 많이 했다.

데이터를 넣으면 되지 이렇게 번거롭게 방대한 library를 공부해가며 데이터 검증을 해야하나 싶은데,

int가 string이 되기도 하고 float이 되기도 하는 파이썬의 자유로운 형변환 때문에 체크를 해주는게 꼭 필요하다 느낀다.

 

코드를 잘짜면 되는거 아니야?라는 생각이 들었지만, 공동작업시에는 나만 코드를 잘짠다고 되는게 아니라, 모두가 잘짜야하고, 모두가 잘짜도록 강제하기 위해서 pydantic같은 데이터 검증 라이브러리를 적극 활용하는 것이 정말 필요하다.

실은 너무 귀찮다고 생각하면서 코드를 작성했는데, 작성하고 돌리자마자 다른사람의 코드에서 타입 지정이 잘못된 부분을 찾고보니 꼭 필요하다는 생각이 든다.

728x90
반응형