8.5 KiB
ボディ - ネストされたモデル
FastAPI を使用すると、深くネストされた任意のモデルを定義、検証、文書化、使用することができます(Pydanticのおかげです)。
リストのフィールド
属性をサブタイプとして定義することができます。例えば、Pythonのlistは以下のように定義できます:
{* ../../docs_src/body_nested_models/tutorial001.py hl[12] *}
これにより、各項目の型は宣言されていませんが、tagsはある項目のリストになります。
タイプパラメータを持つリストのフィールド
しかし、Pythonには型や「タイプパラメータ」を使ってリストを宣言する方法があります:
typingのListをインポート
まず、Pythonの標準のtypingモジュールからListをインポートします:
{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
タイプパラメータを持つListの宣言
listやdict、tupleのようなタイプパラメータ(内部の型)を持つ型を宣言するには:
typingモジュールからそれらをインストールします。- 角括弧(
[と])を使って「タイプパラメータ」として内部の型を渡します:
from typing import List
my_list: List[str]
型宣言の標準的なPythonの構文はこれだけです。
内部の型を持つモデルの属性にも同じ標準の構文を使用してください。
そのため、以下の例ではtagsを具体的な「文字列のリスト」にすることができます:
{* ../../docs_src/body_nested_models/tutorial002.py hl[14] *}
セット型
しかし、よく考えてみると、タグは繰り返すべきではなく、おそらくユニークな文字列になるのではないかと気付いたとします。
そして、Pythonにはユニークな項目のセットのための特別なデータ型setがあります。
そのため、以下のように、Setをインポートしてstrのsetとしてtagsを宣言することができます:
{* ../../docs_src/body_nested_models/tutorial003.py hl[1,14] *}
これを使えば、データが重複しているリクエストを受けた場合でも、ユニークな項目のセットに変換されます。
そして、そのデータを出力すると、たとえソースに重複があったとしても、固有の項目のセットとして出力されます。
また、それに応じて注釈をつけたり、文書化したりします。
ネストされたモデル
Pydanticモデルの各属性には型があります。
しかし、その型はそれ自体が別のPydanticモデルである可能性があります。
そのため、特定の属性名、型、バリデーションを指定して、深くネストしたJSONobjectを宣言することができます。
すべては、任意のネストにされています。
サブモデルの定義
例えば、Imageモデルを定義することができます:
{* ../../docs_src/body_nested_models/tutorial004.py hl[9,10,11] *}
サブモデルを型として使用
そして、それを属性の型として使用することができます:
{* ../../docs_src/body_nested_models/tutorial004.py hl[20] *}
これは FastAPI が以下のようなボディを期待することを意味します:
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": ["rock", "metal", "bar"],
"image": {
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
}
}
繰り返しになりますが、FastAPI を使用して、その宣言を行うだけで以下のような恩恵を受けられます:
- ネストされたモデルでも対応可能なエディタのサポート(補完など)
- データ変換
- データの検証
- 自動文書化
特殊な型とバリデーション
strやint、floatのような通常の単数型の他にも、strを継承したより複雑な単数型を使うこともできます。
すべてのオプションをみるには、Pydanticのエキゾチック な型のドキュメントを確認してください。次の章でいくつかの例をみることができます。
例えば、Imageモデルのようにurlフィールドがある場合、strの代わりにPydanticのHttpUrlを指定することができます:
{* ../../docs_src/body_nested_models/tutorial005.py hl[4,10] *}
文字列は有効なURLであることが確認され、そのようにJSONスキーマ・OpenAPIで文書化されます。
サブモデルのリストを持つ属性
Pydanticモデルをlistやsetなどのサブタイプとして使用することもできます:
{* ../../docs_src/body_nested_models/tutorial006.py hl[20] *}
これは、次のようなJSONボディを期待します(変換、検証、ドキュメントなど):
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": [
"rock",
"metal",
"bar"
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
},
{
"url": "http://example.com/dave.jpg",
"name": "The Baz"
}
]
}
/// info | 情報
imagesキーが画像オブジェクトのリストを持つようになったことに注目してください。
///
深くネストされたモデル
深くネストされた任意のモデルを定義することができます:
{* ../../docs_src/body_nested_models/tutorial007.py hl[9,14,20,23,27] *}
/// info | 情報
OfferはItemのリストであり、オプションのImageのリストを持っていることに注目してください。
///
純粋なリストのボディ
期待するJSONボディのトップレベルの値がJSONarray(Pythonのlist)であれば、Pydanticモデルと同じように、関数のパラメータで型を宣言することができます:
images: List[Image]
以下のように:
{* ../../docs_src/body_nested_models/tutorial008.py hl[15] *}
あらゆる場所でのエディタサポート
エディタのサポートもどこでも受けることができます。
以下のようにリストの中の項目でも:
Pydanticモデルではなく、dictを直接使用している場合はこのようなエディタのサポートは得られません。
しかし、それらについて心配する必要はありません。入力された辞書は自動的に変換され、出力も自動的にJSONに変換されます。
任意のdictのボディ
また、ある型のキーと別の型の値を持つdictとしてボディを宣言することもできます。
有効なフィールド・属性名を事前に知る必要がありません(Pydanticモデルの場合のように)。
これは、まだ知らないキーを受け取りたいときに便利だと思います。
他にも、intのように他の型のキーを持ちたい場合などに便利です。
それをここで見ていきましょう。
この場合、intのキーとfloatの値を持つものであれば、どんなdictでも受け入れることができます:
{* ../../docs_src/body_nested_models/tutorial009.py hl[15] *}
/// tip | 豆知識
JSONはキーとしてstrしかサポートしていないことに注意してください。
しかしPydanticには自動データ変換機能があります。
これは、APIクライアントがキーとして文字列しか送信できなくても、それらの文字列に純粋な整数が含まれている限り、Pydanticが変換して検証することを意味します。
そして、weightsとして受け取るdictは、実際にはintのキーとfloatの値を持つことになります。
///
まとめ
FastAPI を使用すると、Pydanticモデルが提供する最大限の柔軟性を持ちながら、コードをシンプルに短く、エレガントに保つことができます。
以下のような利点があります:
- エディタのサポート(どこでも補完!)
- データ変換(別名:構文解析・シリアライズ)
- データの検証
- スキーマ文書
- 自動文書化