API 를 개발하게 되면 Request 가 잘 들어왔는지 검증하는 로직을 추가하게 됩니다.
request 모델로 받겠다고 정의한 schema 에서 service
로직이 아닌 request 자체에 대한 유효성 검증을 대부분 처리하고 있는데, 이때 @root_validator
를 사용하면 보다 효율적으로 처리가 가능합니다.
아래와 같이 요청을 받는 router 가 존재하고,
# router.py
@router.put(
"/entity/capacity",
response_model=ApiRes,
summary="Update Capacity of an Abstract Entity",
)
async def update_entity_capacity(
req: EntityCapacityUpdateReq,
...
) -> ApiRes:
""" Update the capacity of an abstract entity
Update the maximum capacity for a specified abstract entity.
# Replace this with your service logic for updating the entity capacity
await entity_service.update_entity_capacity(
req=req,
...
)
return ApiRes.of_success()
router 에서 받는 request 모델을 정의한 schema
# schema.py
from pydantic import BaseModel, Body, root_validator
class CapacityUpdateRequest(BaseModel):
entity_id: int = Body(None, alias="entityId", description="Entity ID")
max_capacity: int = Body(..., alias="maxCapacity", description="Maximum capacity")
@root_validator(pre=True)
def values_validation(cls, values):
entity_id = values.get("entity_id")
max_capacity = values.get("max_capacity")
if entity_id is None:
raise ValueError("Invalid access. Please provide an entity ID.")
if max_capacity is None:
raise ValueError(f"Maximum capacity is required. maxCapacity: {max_capacity}")
if max_capacity < 0 or max_capacity > 10000:
raise ValueError(f"maxCapacity must be between 0 and 10,000. maxCapacity: {max_capacity}")
return values
간단하게만 로직을 처리하자면 위의 entity_id: int = Body(None, description="Entity ID")
와 같은 식으로 타입을 정의하고 Body
를 통해 default value 나 설명, 또는 간단한 유효성 체크 로직을 작성할 수 있습니다.
하지만 위처럼 작성하게 되면 원하지 않는 request value 가 들어왔을 때 exception 메시지가 pydantic 에서 정의한대로 나가서 원하는대로 커스텀하기 어렵습니다. 유효성을 보다 더 자세하게 체크하고 싶은 것도 쉽지 않고요.
이때 pydantic 에서 제공하는 @root_validator
를 사용할 수 있습니다.
root_validator 는 schema 내에 추가하여 request 로 넘어온 값들을 추가로 검증할 수 있게 해줍니다.
pre=
argument 를 통해 적용 순서를 정할 수 있는데 True
로 하게 되면 entity_id:int
로직을 먼저 타기 전에 @root_validator
를 타게 됩니다.
이때의 이점은 타입체크를 명확히 할 수 있다는 것인데, pre=False
로 할 경우 int
타입에 str
이 들어오면 에러를 내는게 아니라 값을 None 으로 치환해서 @root_validator
로 넘겨줍니다. 이러면 실제 이 값이 타입이 잘못된건지 아예 안들어온건지 명확하지가 않아 관련 exception 을 내보내기 어려워집니다.
pre=True
로 하게 되면 값은 그대로 넘어오고 @root_validator
로직을 탄 후에 entity_id:int
로직으로 넘어가서 타입이 다를 경우 타입 exception 을 내보내줍니다.
요런식으로요
"1 validation error for Request\nbody -> __root__\n '<' not supported between instances of 'str' and 'int' (type=type_error)",
하나 참고할 건 pre=True
로 할 경우 @root_validator
를 먼저 타서 values 에 entity_id
가 아닌 entityId
로 들어옵니다.
위 내용들 참고해서 유효성 검증 로직 잘 짜봅시다.