
Python 타입 기반 데이터 검증 및 직렬화 라이브러리
v2에서는validator와serializer를 분리해 더 명확한 역할 제공
pydatic의 핵심은 BaseModel 상속하는 것
from pydantic import BaseModel, Field
class User(BaseModel):
id: int
name: str
password: str = Field(..., min_length=4)
field로 validation 규칙 설명 가능데이터 들어올 때 값을 검사하고 조정할 수 있음
@model_validator| mode | 동작 시점 | 첫 번째 인자 |
|---|---|---|
| before | 인스턴스 생성 전 | cls |
| after | 인스턴스 생성 후 | self |
| wrap | 생성 과정 감싸기 | validator 콜백 |
from typing import Any
from pydantic import BaseModel, model_validator
class UserModel(BaseModel):
username: str
@model_validator(mode='before')
@classmethod
def check_card_number_not_present(cls, data: Any) -> Any:
if isinstance(data, dict):
if 'card_number' in data:
raise ValueError("'card_number' should not be included")
return data
from typing_extensions import Self
from pydantic import BaseModel, model_validator
class UserModel(BaseModel):
username: str
password: str
password_repeat: str
@model_validator(mode='after')
def check_passwords_match(self) -> Self:
if self.password != self.password_repeat:
raise ValueError('Passwords do not match')
return self
import logging
from typing import Any
from typing_extensions import Self
from pydantic import BaseModel, ModelWrapValidatorHandler, ValidationError, model_validator
class UserModel(BaseModel):
username: str
@model_validator(mode='wrap')
@classmethod
def log_failed_validation(cls, data: Any, handler: ModelWrapValidatorHandler[Self]) -> Self:
try:
return handler(data)
except ValidationError:
logging.error('Model %s failed to validate with data %s', cls, data)
raise
@field_validatorfrom pydantic import BaseModel, ValidationInfo, field_validator
class UserModel(BaseModel):
password: str
password_repeat: str
username: str
@field_validator('password_repeat', mode='after')
@classmethod
def check_passwords_match(cls, value: str, info: ValidationInfo) -> str:
if value != info.data['password']:
raise ValueError('Passwords do not match')
return value
*상세 설명
check_passwrods_match는 @field_validator('password_repeat', mode='after') password_repeat 필드의 유효성 검사가 완료된 후 (mode='after') check_passwords_match함수가 실행된다는 의미value (현재 password_repeat의 값)와 into.data['password'] (이미 유효성 검사가 완료된 password 의 값)를 비교하여 두 비밀번호가 일치하는지 확인한다.주의해야할 점
username의 유효성이 검사된 값은 아직 사용할 수 없다. username은 password_repeat 뒤에 정의되어 있기 때문*상세설명
passwordpassword_repaeatusernamepassword의 유효성을 먼저 검사하고, 그 다음 password_repeat, 마지막으로 username 순으로 검사 수행데이터 나갈 때 값을 변환가능
→ API 응답 포맷 가공용
@model_serializerfrom pydantic import model_serializer
class User(BaseModel):
id: int
password: str
@model_serializer
def serialize(self):
return {
"id": self.id
}
user = User(id=1, password='secret')
print(user.model_dump())
# {'id': 1}
@model_serializer 의 역할
Pydantic 모델 전체를 어떻게 JSON(이나 다른 형태로)으로 변환(직렬화) 할 것인지 정의할 때 사용
일반적으로 Pydantic 모델 인스턴스를 .model_dump()나 .model_json()으로 변환하면, 모델에 정의된 모든 필드가 기본적으로 포함됨
하지만 @model_serializer를 사용하면, 이 serialize 메서드가 실행되어 반환하는 딕셔너리가 해당 모델 인스턴스의 직렬화된 결과물이 된다.
예를들어 @model_serializer 없이 할 경우는 다음과 같다.
from pydantic import model_serializer
class User(BaseModel):
id: int
password: str
def serialize(self):
return {
"id": self.id
}
user = User(id=1, password="secret")
print(user.model_dump())
# 출력: {'id': 1, 'password': 'secret'} # <--- password가 포함!
# serialize 메서드를 사용하려면 직접 호출해야 한다.
print(user.serialize())
# 출력: {'id': 1}
@field_serializerfrom pydantic import field_serializer
class Product(BaseModel):
price: int
@field_serializer("price")
def format_price(self, v):
return f"{v:,}원"
@field_serializer의 역할:
@model_serializer와 달리, @field_serializer는 개별 필드의 출력 형식을 제어함*상세 설명
Product 모델은 price라는 정수(int) 필드를 가지고 있음@field_serializer('price') 데코레이터가 적용된 format_price 메서드는 price 필드의 직렬화 시 실행된다.format_price 메서드는 입력값 v (여기서는 price의 값)를 받아서 f"{v:,}원 형태로 변환한다.[데이터 들어옴]
↓
@model_validator(before) 로 값 세팅 및 초기 정리
↓
@field_validator (before/after) 로 개별 필드 값 검증
↓
@model_validator(after) 로 전체 값 확인
↓
ValidationError 발생 시 예외 처리
↓
model_dump()/model_dump_json() 으로 직렬화
↓
@field_serializer/@model_serializer 로 응답 포맷 가공
↓
API 응답 or 저장