はじめに
pydantic という便利な validation ライブラリが python にはありますが、こちらでは validation 可能な型が限られています。それを他の型を拡張しないといけない時があったので、メモがてらまとめました。
どうやるのか
このあたりに詳しく書かれていますが、重要なのは __get_validators__
と、そこで yield される関数たちです。pydantic で validation を行う時には、まずは、 __get_validators__
が呼ばれます。これは、ジェネレータになっていて、記された実際の validation 関数を返し、一つ一つ実行していきます。
実際にやってみる。
上記の例を題材に、日本の郵便番号の validator を作ってみます。なお、実際に動かした環境は以下の通りです。
- Python 3.9.7
- pydantic 1.8.2
import re
from pydantic import BaseModel
class PostalCodeJP(str):
@classmethod
def __get_validators__(cls):
yield cls.postal_code_should_be_string
yield cls.remove_hyphen_if_exists
yield cls.postal_code_length_should_be_7
yield cls.postal_code_should_be_numeric_string
@classmethod
def postal_code_should_be_string(cls, v: str):
if not isinstance(v, str):
raise TypeError('postal code should be string')
return v
@classmethod
def remove_hyphen_if_exists(cls, v: str):
if '-' in v:
v = v.replace('-', '')
return v
@classmethod
def posta_code_length_should_be_7(cls, v: str):
if len(v) != 7:
raise ValueError('postal code should be 7 length string')
return v
@classmethod
def postal_code_should_be_numeric_string(cls, v: str):
regex = r'[0-9]+'
if not re.fullmatch(regex, v):
raise ValueError('postal code should be numeric string')
return v
class PostalCodeValueObject(BaseModel):
postal_code: PostalCodeJP
それでは __get_validators__
の一番上から順番に ValidationError を発生させてみます。まずは、郵便番号に文字列以外を用いた場合
p = PostalCodeValueObject(postal_code=1111111)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pydantic/main.py", line 406, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for PostalCodeValueObject
postal_code
postal code should be string (type=type_error)
次に -
を除いた文字列が 7 文字以外になっている場合
p = PostalCodeValueObject(postal_code='111-11111')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pydantic/main.py", line 406, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for PostalCodeValueObject
postal_code
postal code should be 7 length string (type=value_error)
最後に、全ての文字が数字文字列以外になっている場合
p = PostalCodeValueObject(postal_code='111-111l') # 最後の文字が L の小文字になっている
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pydantic/main.py", line 406, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for PostalCodeValueObject
postal_code
postal code should be numeric string (type=value_error)
まとめ
- pydantic で validation に用いる型を追加したい場合を記しました。
-
__get_validators__
に validation したい関数を yield する。 - 公式ドキュメントには重要なことが書かれているので、よく読もう。