Validator
pydantic의 BaseModel을 사용하여 프론트에서 받을 Body 값을 지정하는 동시에 Body 값 중에 validator가 필요한 경우 pydantic model 안에서 정의할 수 있다.
아래의 예시는 pydantic 공식 페이지에서 제공하는 예시와 설명이다.
from pydantic import BaseModel, ValidationError, validator
class UserModel(BaseModel):
name: str
username: str
password1: str
password2: str
@validator('name')
def name_must_contain_space(cls, v):
if ' ' not in v:
raise ValueError('must contain a space')
return v.title()
@validator('password2')
def passwords_match(cls, v, values, **kwargs):
if 'password1' in values and v != values['password1']:
raise ValueError('passwords do not match')
return v
@validator('username')
def username_alphanumeric(cls, v):
assert v.isalnum(), 'must be alphanumeric'
return v
- validator는 class method이기 때문에 첫 번째 인자로 UserModel의 instance가 아니라 UserModel class를 받아야 한다.
- 두 번째 인자는 유효성 검사를 진행할 값이 들어온다.
- 두 값은 필수이고 이후로 다른 값들도 들어올 수 있는데 아래의 키값이 변해서는 안된다. (설명은 공식 페이지에서 가져옴)
- values: a dict containing the name-to-value mapping of any previously-validated fields
- config: the model config
- field: the field being validated. Type of object is pydantic.fields.ModelField
- **kwargs: if provided, this will include the arguments above not explicitly listed in the signature
- validator는 통과된 값을 리턴하거나 ValueError, TypeError, AssertionError를 리턴할 수 있다.
Validator 재사용
pydantic 공식 페이지에서 제공하는 예시가 아니라 실제 코드에서 적용시킨 예시는 아래와 같다. (내용은 조금 다름)
class UserRegisterDto(BaseModel):
email: EmailStr
password1: str
password2: str
system_language: str
@validator("password1")
def password_regex(cls, password):
# 영문+숫자 8자리 이상
regex = r"^(?=.*?[A-Za-z])(?=.*?[0-9]).{8,}$"
if not re.match(regex, password):
raise ValueError("Invalid password form")
return password
주석처리로 설명한 대로 비밀번호가 영문 + 숫자 8자리 이상인지 체크하는 validator이다. 만약 틀리다면 ValuError를 리턴하기 때문에 raise ValueError를 하고 있다.
위 클래스는 회원가입 시 Body로 들어오는 값들이고 그중에서 validator로 비밀번호를 체크한다. 그러다 다른 곳에서도 같은 비밀번호 validator가 필요한 경우가 생겨 아래와 같이 validator를 함수로 분리하고, 각 클래스 안에서 재사용했다.
def password_regex(password):
regex = r"^(?=.*?[A-Za-z])(?=.*?[0-9]).{8,}$"
if not re.match(regex, password):
raise ValueError("Invalid password form")
return password
...
class UserRegisterDto(BaseModel):
email: EmailStr
password1: str
password2: str
system_language: str
_password_regex = validator("password1", allow_reuse=True)(password_regex)
class UserPasswordChangeDto(BaseModel):
old_password: str
password1: str
password2: str
_password_regex = validator("password1", allow_reuse=True)(password_regex)
- allow_reuse는 validator 데코레이터에서 제공하는 옵션 중 하나로, True로 설정하면 같은 validator 함수를 다른 필드에서도 사용할 수 있도록 허용한다. 즉 해당 validator 함수를 다른 필드에 대해서도 재사용할 수 있다.
- True로 하지 않으면 같은 validator 함수를 다른 필드에 대해 사용하려고 했을 때 새로운 validator 함수를 정의해야 하는데, 이는 코드 중복을 유발하고 코드의 유지보수를 어렵게 한다. 따라서 같은 validator를 사용할 때는 위와 같이 함수로 분리해서 사용할 수 있다.
- validator("password1", allow_reuse=True)(password_regex)는 password_regex 함수를 적용한 새로운 validator 함수를 생성하며, 이 validator 함수는 password1 필드의 값에 대해 password_regex 함수의 검증을 수행한다. 이렇게 생성된 validator 함수는 _password_regex에 할당되어, UserPasswordChangeReqDto 클래스에서 password1 필드의 검증에 사용된다.
'FastAPI' 카테고리의 다른 글
[FastAPI] 쿠키 설정 및 쿠키 삭제 (0) | 2023.05.02 |
---|---|
[FastAPI] Request에서 user 정보 얻기 (0) | 2023.04.23 |