path operations のいずれにおいても、response_model
パラメータを使用して、レスポンスのモデルを宣言することができます:
@app.get()
@app.post()
@app.put()
@app.delete()
- など。
{!../../../docs_src/response_model/tutorial001.py!}
!!! note "備考"
response_model
は「デコレータ」メソッド(get
、post
など)のパラメータであることに注意してください。すべてのパラメータやボディのように、path operation関数 のパラメータではありません。
Pydanticモデルの属性に対して宣言するのと同じ型を受け取るので、Pydanticモデルになることもできますが、例えば、List[Item]
のようなPydanticモデルのlist
になることもできます。
FastAPIはresponse_model
を使って以下のことをします:
- 出力データを型宣言に変換します。
- データを検証します。
- OpenAPIの path operation で、レスポンス用のJSON Schemaを追加します。
- 自動ドキュメントシステムで使用されます。
しかし、最も重要なのは:
- 出力データをモデルのデータに限定します。これがどのように重要なのか以下で見ていきましょう。
!!! note "技術詳細"
レスポンスモデルは、関数の戻り値のアノテーションではなく、このパラメータで宣言されています。なぜなら、パス関数は実際にはそのレスポンスモデルを返すのではなく、dict
やデータベースオブジェクト、あるいは他のモデルを返し、response_model
を使用してフィールドの制限やシリアライズを行うからです。
ここではUserIn
モデルを宣言しています。それには平文のパスワードが含まれています:
{!../../../docs_src/response_model/tutorial002.py!}
そして、このモデルを使用して入力を宣言し、同じモデルを使って出力を宣言しています:
{!../../../docs_src/response_model/tutorial002.py!}
これで、ブラウザがパスワードを使ってユーザーを作成する際に、APIがレスポンスで同じパスワードを返すようになりました。
この場合、ユーザー自身がパスワードを送信しているので問題ないかもしれません。
しかし、同じモデルを別のpath operationに使用すると、すべてのクライアントにユーザーのパスワードを送信してしまうことになります。
!!! danger "危険" ユーザーの平文のパスワードを保存したり、レスポンスで送信したりすることは絶対にしないでください。
代わりに、平文のパスワードを持つ入力モデルと、パスワードを持たない出力モデルを作成することができます:
{!../../../docs_src/response_model/tutorial003.py!}
ここでは、path operation関数がパスワードを含む同じ入力ユーザーを返しているにもかかわらず:
{!../../../docs_src/response_model/tutorial003.py!}
...response_model
をUserOut
と宣言したことで、パスワードが含まれていません:
{!../../../docs_src/response_model/tutorial003.py!}
そのため、FastAPI は出力モデルで宣言されていない全てのデータをフィルタリングしてくれます(Pydanticを使用)。
自動ドキュメントを見ると、入力モデルと出力モデルがそれぞれ独自のJSON Schemaを持っていることが確認できます。
そして、両方のモデルは、対話型のAPIドキュメントに使用されます:
レスポンスモデルにはデフォルト値を設定することができます:
{!../../../docs_src/response_model/tutorial004.py!}
description: str = None
はNone
がデフォルト値です。tax: float = 10.5
は10.5
がデフォルト値です。tags: List[str] = []
は空のリスト([]
)がデフォルト値です。
しかし、実際に保存されていない場合には結果からそれらを省略した方が良いかもしれません。
例えば、NoSQLデータベースに多くのオプション属性を持つモデルがあるが、デフォルト値でいっぱいの非常に長いJSONレスポンスを送信したくない場合です。
path operation デコレータにresponse_model_exclude_unset=True
パラメータを設定することができます:
{!../../../docs_src/response_model/tutorial004.py!}
そして、これらのデフォルト値はレスポンスに含まれず、実際に設定された値のみが含まれます。
そのため、path operationにIDfoo
が設定されたitemのリクエストを送ると、レスポンスは以下のようになります(デフォルト値を含まない):
{
"name": "Foo",
"price": 50.2
}
!!! info "情報"
FastAPIはこれをするために、Pydanticモデルの.dict()
をそのexclude_unset
パラメータで使用しています。
!!! info "情報" 以下も使用することができます:
* `response_model_exclude_defaults=True`
* `response_model_exclude_none=True`
`exclude_defaults`と`exclude_none`については、<a href="https://docs.pydantic.dev/latest/concepts/serialization/#modeldict" class="external-link" target="_blank">Pydanticのドキュメント</a>で説明されている通りです。
しかし、IDbar
のitemのように、デフォルト値が設定されているモデルのフィールドに値が設定されている場合:
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
それらはレスポンスに含まれます。
IDbaz
のitemのようにデフォルト値と同じ値を持つデータの場合:
{
"name": "Baz",
"description": None,
"price": 50.2,
"tax": 10.5,
"tags": []
}
FastAPIは十分に賢いので(実際には、Pydanticが十分に賢い)description
やtax
、tags
はデフォルト値と同じ値を持っているにもかかわらず、明示的に設定されていることを理解しています。(デフォルトから取得するのではなく)
そのため、それらはJSONレスポンスに含まれることになります。
!!! tip "豆知識"
デフォルト値はNone
だけでなく、なんでも良いことに注意してください。
例えば、リスト([]
)や10.5
のfloat
などです。
path operationデコレータとしてresponse_model_include
とresponse_model_exclude
も使用することができます。
属性名を持つstr
のset
を受け取り、含める(残りを省略する)か、除外(残りを含む)します。
これは、Pydanticモデルが1つしかなく、出力からいくつかのデータを削除したい場合のクイックショートカットとして使用することができます。
!!! tip "豆知識" それでも、これらのパラメータではなく、複数のクラスを使用して、上記のようなアイデアを使うことをおすすめします。
これは`response_model_include`や`response_mode_exclude`を使用していくつかの属性を省略しても、アプリケーションのOpenAPI(とドキュメント)で生成されたJSON Schemaが完全なモデルになるからです。
同様に動作する`response_model_by_alias`にも当てはまります。
{!../../../docs_src/response_model/tutorial005.py!}
!!! tip "豆知識"
{"name", "description"}
の構文はこれら2つの値をもつset
を作成します。
これは`set(["name", "description"])`と同等です。
もしset
を使用することを忘れて、代わりにlist
やtuple
を使用しても、FastAPIはそれをset
に変換して正しく動作します:
{!../../../docs_src/response_model/tutorial006.py!}
path operationデコレータのresponse_model
パラメータを使用して、レスポンスモデルを定義し、特にプライベートデータがフィルタリングされていることを保証します。
明示的に設定された値のみを返すには、response_model_exclude_unset
を使用します。