Liquidsのロゴ Liquids

0

自作バリデーションを適用した型を作る【FastAPI】

Pydantic
Python
FastAPI

FastAPIではバリデーションにPydanticを使用しています。

例えば、リクエストにパスワードが必要で、そのバリデーションを行いたい時にはどうすれば良いでしょうか?

ガード節でのバリデーションもできますが、リクエストのモデルを作成するときにバリデーションすれば宣言的に書けるのでより良いでしょう。

PydanticにはHttpUrl型があり、以下のように書けばhttp_urlはHTTP URLであることをバリデーションしてくれます。この方が明示的にわかりやすく、処理の漏れもありません。

class RequestBody(BaseModel):
    http_url: HttpUrl

このWikiではこのようなバリデーションを行う型を作る方法を紹介します。

このWikiではパスワードを例としてバリデーションを実装します。
パスワードの要件は次のようにしましょう。

  • 5文字以上
  • 英字、数字を含むこと

まずはバリデーションを行う関数を作りましょう。

バリデーションに失敗したらValueErrorraiseします。

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.py
from 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のロゴ Liquids

Liquidsは誰でも投稿・編集ができる技術Wikiコミュニティ📝です。

あなたもLiquidsで技術Wikiを
書いてみませんか?