自作バリデーションを適用した型を作る【FastAPI】
FastAPIではバリデーションにPydanticを使用しています。
例えば、リクエストにパスワードが必要で、そのバリデーションを行いたい時にはどうすれば良いでしょうか?
ガード節でのバリデーションもできますが、リクエストのモデルを作成するときにバリデーションすれば宣言的に書けるのでより良いでしょう。
PydanticにはHttpUrl
型があり、以下のように書けばhttp_url
はHTTP URLであることをバリデーションしてくれます。この方が明示的にわかりやすく、処理の漏れもありません。
class RequestBody(BaseModel):
http_url: HttpUrl
このWikiではこのようなバリデーションを行う型を作る方法を紹介します。
このWikiではパスワードを例としてバリデーションを実装します。
パスワードの要件は次のようにしましょう。
- 5文字以上
- 英字、数字を含むこと
まずはバリデーションを行う関数を作りましょう。
バリデーションに失敗したらValueError
をraise
します。
import re
# 文字数チェック
def check_len_of_chars(v: str) -> str:
if len(v) < 5:
raise ValueError('At least 5 characters are required.')
return v
# 文字種チェック
def check_meeting_char_type(v: str) -> str:
if not re.match('.*[a-zA-Z]+.*', v):
raise ValueError('Password must contain alphabet.')
if not re.match('.*[0-9]+.*', v):
raise ValueError('Password must contain number.')
return v
既存の型にバリデーションを追加するには、Annotated
を使用します。
from typing import Annotated
from pydantic import AfterValidator
Password = Annotated[
str,
AfterValidator(check_len_of_chars),
AfterValidator(check_meeting_char_type)
]
後はSTEP2で作成した型を用いてレスポンスやリクエストで用いてモデルを作成します。
main.pyfrom fastapi import FastAPI
from typing import Annotated
from pydantic import BaseModel, AfterValidator
import re
def check_len_of_chars(v: str) -> str:
if len(v) < 5:
raise ValueError('At least 5 characters are required.')
return v
def check_meeting_char_type(v: str):
if not re.match('.*[a-zA-Z]+.*', v):
raise ValueError('Password must contain alphabet.')
if not re.match('.*[0-9]+.*', v):
raise ValueError('Password must contain number.')
return v
Password = Annotated[
str,
AfterValidator(check_len_of_chars),
AfterValidator(check_meeting_char_type)
]
class RequestBody(BaseModel):
password: Password
app = FastAPI()
@app.post('/')
def index(body: RequestBody):
return body
要件に適合しないパスワードをリクエストボディに与えてリクエストしてみると、バリデーションでエラーが発生することを確認します。
STEP1で作成したバリデーション用の関数を使うと以下のようなエラー文になります。
このエラー文をカスタマイズしたい時には、PydanticCustomError
を用いてエラーをraiseします。PydanticCustomError
については以下のドキュメントを参照。
PydanticCustomError
を用いてエラーをカスタマイズしてみます。
def check_len_of_chars(v: str) -> str:
if len(v) < 5:
raise PydanticCustomError('length_error', 'At least 5 characters are required.')
return v
def check_meeting_char_type(v: str):
if not re.match('.*[a-zA-Z]+.*', v):
raise PydanticCustomError('char_type_error', 'Password must contain alphabet.')
if not re.match('.*[0-9]+.*', v):
raise PydanticCustomError('char_type_error', 'Password must contain number.')
return v
Liquidsは誰でも投稿・編集ができる技術Wikiコミュニティ📝です。
あなたもLiquidsで技術Wikiを
書いてみませんか?