概要
Pydanticでたとえば特定のドメインのみ許可したい、などの要望があるときはPydanticのEmailStrを使うだけでなく、カスタムバリデーションを作成する必要があります
今回はEmailStr、email-validator、field_validatorのデコレータを使ってカスタムバリデーションを作成する方法について解説します
前提
- email-validatorをインストール済み(EmailStrを使用するにはemail-validatorがインストールされていないと使用できないため)
実装
from pydantic import BaseModel, EmailStr, field_validator
from email_validator import validate_email, EmailNotValidError
class CreateUserRequest(BaseModel):
username: str
email: EmailStr
first_name: str
last_name: str
password: str
is_admin: bool
phone_number: str
@field_validator("email")
@classmethod
def validate_custom_email(cls, value):
try:
allowed_domains = {"gmail.com", "test.com"}
emailinfo = validate_email(value, check_deliverability=True)
email = emailinfo.normalized
if emailinfo.ascii_domain not in allowed_domains:
raise ValueError(f"Only emails from {', '.join(allowed_domains)} are allowed")
if not email.isascii():
raise ValueError("Invalid email format")
return email
except EmailNotValidError:
raise ValueError("Invalid email format")
順番に解説します
validate_emailを使ったメールアドレスのバリデーション
Pydanticでカスタムバリデーションを作成するにはfield_validatorとclassmethodのデコレータを使用する必要があります
validate_email_methodでメールアドレスのバリデーションを行います
valueにはemailフィールドの値が入ります
check_deliverability=Trueにすることで初回のユーザ作成時などでDNSを使ったメールアドレスの存在確認ができます
今回はユーザ作成用のバリデーションをするためのBaseModelを定義しているのでTrueにしてますが、ログイン時などは無駄なDNSチェックを行わないようにFalseにすることが推奨されています
@field_validator("email")
@classmethod
def validate_custom_email(cls, value):
try:
allowed_domains = {"gmail.com", "test.com"}
emailinfo = validate_email(value, check_deliverability=True)
メールアドレスの正規化
email-validatorのREADMEに記載されていますが、IDNA ASCIIのドメイン名をUnicodeに変換し、ローカル部分とドメイン部分(元々Unicodeの場合)に対してUnicodeによる正規化を行うことが推奨されているので記載してます
email = emailinfo.normalized
バリデーション
- メールアドレスが該当するドメインを持つか
- メールアドレス内にascii以外が含まれているか
のバリデーションを行います
メールアドレスにascii以外が含まれているかのバリデーションを入れているのはEmailStrでは全角文字を検知できないからです
if emailinfo.ascii_domain not in allowed_domains:
raise ValueError(f"Only emails from {', '.join(allowed_domains)} are allowed")
if not email.isascii():
raise ValueError("Invalid email format")
return email
except EmailNotValidError:
raise ValueError("Invalid email format")
実際に検証してみよう!
- メールアドレスが該当するドメインを持つか
- メールアドレス内にascii以外が含まれているか
の検証を行います
以下のようにAPIを実行し、エラーメッセージが表示されたら成功です
参考