概要
FastAPIではPydanticというライブラリを利用してモデルスキーマとバリデーションを宣言的に実装できるようになっている。
ここではその具体的な方法を記述する。
確認したバージョンは以下の通り。
- FastAPI: 0.68.1
- Pydantic: 1.8.2
使い方
モデルの記述と型チェック
モデルの定義
from pydantic import BaseModel
class Hoge(BaseModel):
id: int
name: str
のように書けばHoge
モデルが作成される。
Hoge
モデルは整数(int
)のid
が必ず存在し、文字列(str
)のname
が必ず存在する。この条件を満たさない場合、バリデーションエラーとなる。
型
型はPythonの型ヒント(type hints)を使って記述する。
一般的なものを以下に示す。
型 | 説明 | JSON schema type |
---|---|---|
None |
None そのもの |
null |
bool |
真偽値 | boolean |
int |
整数 | number |
float |
浮動小数点数 | number |
str |
文字列 | string |
List |
リスト | array |
Tuple |
タプル | array |
Set |
セット |
array (uniqueitems=true となる) |
Dict |
辞書 | object |
また、次のものはdatetime
モジュールをimport
して型に使用できる。
型 | 説明 | JSONでの対応 |
---|---|---|
datetime.date |
日付 | ISO8601形式の日付を表す文字列、またはUnixタイムスタンプを表す数値 |
datetime.time |
時刻 | ISO8601形式の時刻を表す文字列 |
datetime.datetime |
タイムスタンプ(日時) | ISO8601形式の日時を表す文字列、またはUnixタイムスタンプを表す数値 |
datetime.timedelta |
時間間隔 | ISO8601形式の時間間隔を表す文字列、または時間間隔(秒)を表す数値 |
- 型ヒントを使うため、
List[str]
のように要素の型も指定できる- JSON schemaでは
items
のtype
の指定になる
- JSON schemaでは
- また、
Union
やOptional
も使用できる-
Union
の場合、JSON schemaではoneOf
指定になる -
Optional
の場合、JSON schemaではrequired
が指定されない
-
必須チェックとデフォルト値
プロパティの必須チェックには次の4パターンの類型がある。
厳密な必須チェック(プロパティが存在し値がNone
ではない)
from pydantic import BaseModel
class Hoge(BaseModel):
hoge: int
と書けば次の場合にエラーになる
-
hoge
が存在しない -
hoge
が存在してもその値がNone
-
hoge
が整数ではない
緩い必須チェック(プロパティが存在すれば値はNone
でもいい)
from pydantic import BaseModel, Field
class Hoge(BaseModel):
hoge: Optional[int] = Field(...)
と書けば次の場合にエラーになる
-
hoge
が存在しない -
hoge
が整数ではない
必須チェックなし(プロパティが存在しなくてもいい)
from pydantic import BaseModel, Field
class Hoge(BaseModel):
hoge: Optional[int]
と書けば次の場合にエラーになる
-
hoge
が整数ではない
値が必要なオプション(プロパティは存在しなくてもいいが、存在するならnull
ではダメなもの)
冗長な書き方が必要となる。
from pydantic import BaseModel, Field, validator
class Hoge(BaseModel):
hoge: Optional[int]
@validator("hoge") # hogeのバリデーションの登録
def validate_hoge(cls, value): # 関数名はなんでもいい。第1引数はcls固定で使用しない。第2引数はvalueでhogeに設定した値
if value is None: # Noneであれば例外を投げる
raise TypeError("none is not an allowed value")
return value # Noneでない場合はそのままvalueを返す
と書けば次の場合にエラーになる
-
hoge
がNone
-
hoge
が整数ではない
デフォルト値
from pydantic import BaseModel, Field
class Hoge(BaseModel):
hoge: int = 1
# または
fuga: int = Field(2)
と書けばデフォルト値を設定できる。デフォルト値がある場合は値を明示的に設定しなくてもデフォルト値が設定されていることになるので必須チェックにはかからない。
Field()
の第1引数にはデフォルト値を設定するが、デフォルト値が不要な場合は、...
(Ellipsis)を書く。
形式チェック
文字列の長さ
from pydantic import BaseModel, Field
class Hoge(BaseModel):
foo: str = Field(..., min_length=2, max_length=10)
と書けば、文字数が2文字から10文字以外の場合エラーとなる。
また、デフォルト値の設定が...
となっているので、値を設定しない場合は必須エラーとなる。
リストの要素数
from pydantic import BaseModel, Field
class Hoge(BaseModel):
foo: List[str] = Field(..., min_items=2, max_items=10)
と書けば、要素数が2個から10個以外の場合エラーとなる。
正規表現
from pydantic import BaseModel, Field
class Hoge(BaseModel):
foo: str = Field(..., regex=r"[0-9]{2,3}")
と書けば、regex=r"[0-9]{2,3}"
で指定した正規表現にマッチしない場合エラーとなる。
値チェック
数値の範囲
from pydantic import BaseModel, Field
class Hoge(BaseModel):
hoge: int = Field(..., ge=2, le=10)
と書けば、値が2から10の範囲外の場合エラーとなる。
次の4種類が使用できる
-
ge
:>=
greater than or equal 以上 -
gt
:>
greater than 超過 -
le
:<=
less than or equal 以下 -
lt
:<
less than 未満
コード定義
from enum import Enum
from pydantic import BaseModel, Field
class HogeCode(Enum):
AAA = 1
BBB = 2
CCC = 3
class Hoge(BaseModel):
foo: HogeCode
と書けば、値が1, 2, 3以外の場合エラーとなる。
複雑なバリデーション
validator
を実装する。詳しくはValidatorsを参照。
from pydantic import BaseModel, Field, validator
class Hoge(BaseModel):
hoge: Optional[int]
@validator(プロパティ名)
def validate_hoge(cls, value, values):
if ここでチェック:
raise ValueError("何かのメッセージ")
return value
- プロパティ名を@validatorデコレーターに指定するとそのプロパティがチェックされる
-
@validatorデコレーターに
each_item=True
を設定するとチェックしたいプロパティがList
やDict
などのコレクションの場合、要素ひとつひとつについてバリデートできる。 - validatorの関数名は任意
- validatorの関数の引数は第1、第2引数は固定、第3引数以降は任意だが
values
という名前の引数を定義すると、そのプロパティより前にバリデートされたプロパティの辞書(プロパティ名がキーで、プロパティの値が値)が入ってくる。- これを使って相関チェックが実装できる
-
raise
できる例外クラスはTypeError
かValueError
自体かサブクラスで詳細は後述する。-
raise
せずにassert
を使ってAssertionError
を投げても問題ない
-
Pydanticでは実装できないバリデーション
データベースの内容との相関チェックはPydantic単体ではバリデーションできないため、パスオペレーション関数(@app.get(...
とかをつけた関数)の先頭でチェックする。
例えば、リクエストボディが次のような形式だとしてqux
にエラーがあった場合は次のように返却する。
{
"foo": {
"bar": 123,
"baz": {
"qux": "errrrrrr" // これがエラーとする
}
}
}
from fastapi.exceptions import RequestValidationError
from pydantic.error_wrappers import ErrorWrapper
# ...中略...
if エラー判定:
raise RequestValidationError(
[ErrorWrapper(ValueError("エラーメッセージをどうぞ"), ("body", "foo", "baz", "qux"))]
)
RequestValidationError
を利用することで他のバリデーションエラーと同じ形式で出力される。
エラーレスポンスは次のようになる
422 Unprocessable Entity
{
"detail": [
{
"loc": [
"body",
"foo",
"bar",
"qux"
],
"msg": "エラーメッセージをどうぞ",
"type": "value_error"
}
]
}
例外クラスとtype
についての詳細
-
ErrorWrapper
に指定できる例外クラスは@validator
でraise
できるものと同じである -
ValueError
の場合、レスポンスに出力されるtype
はvalue_error
となる -
TypeError
の場合、レスポンスに出力されるtype
はtype_error
となる -
ValueError
のサブクラスを実装しクラス変数としてcode="piyopiyo"
を定義すると、レスポンスに出力されるtype
はvalue_error.piyopiyo
となる。(TypeError
のサブクラスの場合も同様)