0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pydanticでメールアドレスのカスタムバリデーションを作成するには

Posted at

概要

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を実行し、エラーメッセージが表示されたら成功です

スクリーンショット 2025-03-01 11.02.40.png

スクリーンショット 2025-03-01 11.03.03.png

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?