クエリパラメータと文字列の検証¶
FastAPI では、パラメータに追加情報と検証を宣言できます。
このアプリケーションを例に見てみましょう。
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 その他のバージョンとバリアント
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[str, None] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
クエリパラメータ q
は str | None
型です。これは str
型ですが、None
になる可能性もあります。実際、デフォルト値は None
なので、FastAPI はそれが必須ではないことを認識します。
注
FastAPI は、デフォルト値が = None
であるため、q
の値が必須ではないことを認識します。
str | None
を使用することで、エディタがより良いサポートを提供し、エラーを検出できるようになります。
追加の検証¶
q
はオプションですが、提供される場合は**その長さが50文字を超えない**ように強制します。
Query
と Annotated
をインポートする¶
これを実現するために、まず以下をインポートします。
fastapi
からQuery
typing
からAnnotated
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 その他のバージョンとバリアント
from typing import Union
from fastapi import FastAPI, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(max_length=50)] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
情報
FastAPI はバージョン 0.95.0 で Annotated
のサポートを追加し(そして推奨し始めました)。
古いバージョンを使用している場合、Annotated
を使用しようとするとエラーが発生します。
Annotated
を使用する前に、FastAPI のバージョンを 0.95.1 以上にアップグレードしてください。
q
パラメータの型に Annotated
を使用する¶
Python 型入門で、Annotated
をパラメータにメタデータを追加するために使用できると前にお伝えしました。
FastAPI でそれを使用する時が来ました。🚀
この型アノテーションがありました。
q: str | None = None
q: Union[str, None] = None
これを Annotated
でラップすると、次のようになります。
q: Annotated[str | None] = None
q: Annotated[Union[str, None]] = None
これらの両方のバージョンは同じ意味を持ちます。q
は str
または None
になり得るパラメータで、デフォルトでは None
です。
さあ、楽しい部分に進みましょう。🎉
q
パラメータの Annotated
に Query
を追加する¶
より多くの情報(この場合は追加の検証)を入れられるこの Annotated
ができたので、Annotated
の中に Query
を追加し、パラメータ max_length
を 50
に設定します。
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 その他のバージョンとバリアント
from typing import Union
from fastapi import FastAPI, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(max_length=50)] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
デフォルト値は依然として None
であるため、パラメータは引き続きオプションです。
しかし、Annotated
の中に Query(max_length=50)
を持っていることで、FastAPI にこの値に対して**追加の検証**を行いたい、最大50文字にしたい、と伝えているのです。😎
ヒント
ここでは**クエリパラメータ**なので Query()
を使用しています。後で Path()
, Body()
, Header()
, Cookie()
のような他のものも同じ引数を受け入れることがわかります。
FastAPI は次のことを行います。
- 最大長が50文字であることを確認するためにデータを**検証**します。
- データが無効な場合にクライアントに**明確なエラー**を表示します。
- OpenAPI スキーマの*パス操作*にパラメータを**文書化**します(これにより、**自動ドキュメント UI** に表示されます)。
代替(旧):デフォルト値としての Query
¶
FastAPI の以前のバージョン(0.95.0 以前)では、Annotated
に入れる代わりに、パラメータのデフォルト値として Query
を使用する必要がありました。それを使用しているコードをよく見かける可能性があるので、説明しておきます。
ヒント
新しいコードや可能な限り、上記で説明したように Annotated
を使用してください。いくつかの利点(以下で説明)があり、欠点はありません。🍰
これは、関数のパラメータのデフォルト値として Query()
を使用し、パラメータ max_length
を 50 に設定する方法です。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 その他のバージョンとバリアント
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
from typing import Union
from fastapi import FastAPI, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(max_length=50)] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
この場合(Annotated
を使用しない場合)、関数のデフォルト値 None
を Query()
に置き換える必要があるため、Query(default=None)
というパラメータでデフォルト値を設定する必要があります。これは、そのデフォルト値を定義するという同じ目的を果たします(少なくとも FastAPI の場合)。
したがって、
q: str | None = Query(default=None)
...は、パラメータをオプションにし、デフォルト値を None
にします。これは
q: str | None = None
と同じですが、Query
バージョンはそれをクエリパラメータとして明示的に宣言しています。
次に、Query
にさらにパラメータを渡すことができます。この場合、文字列に適用される max_length
パラメータです。
q: str | None = Query(default=None, max_length=50)
これにより、データが検証され、データが無効な場合には明確なエラーが表示され、OpenAPI スキーマのパス操作にパラメータが文書化されます。
デフォルト値として、または Annotated
内の Query
¶
Annotated
の中で Query
を使用する場合、Query
の default
パラメータは使用できないことに注意してください。
代わりに、関数のパラメータの実際のデフォルト値を使用してください。そうしないと、一貫性がなくなります。
たとえば、これは許可されません。
q: Annotated[str, Query(default="rick")] = "morty"
...なぜなら、デフォルト値が "rick"
と "morty"
のどちらであるべきか明確ではないからです。
したがって、(できれば)次のように使用します。
q: Annotated[str, Query()] = "rick"
...または、古いコードベースでは次のように記述されているのを見かけるでしょう。
q: str = Query(default="rick")
Annotated
の利点¶
関数パラメータのデフォルト値の代わりに、Annotated
を使用することをお勧めします。いくつかの理由から、より優れています。🤓
関数パラメータのデフォルト値が実際のデフォルト値であるため、Python 全体とより直感的です。😌
FastAPI なしで同じ関数を他の場所で呼び出すことができ、期待どおりに機能します。必須パラメータ(デフォルト値なし)がある場合、エディターがエラーで通知します。必須パラメータを渡さずに実行すると、Python も文句を言います。
Annotated
を使用せず、代わりに(古い)デフォルト値のスタイルを使用する場合、FastAPI なしでその関数を他の場所で呼び出すと、正しく機能するように引数を関数に渡すことを**覚えておく**必要があります。そうしないと、値が期待するものと異なります(例: str
の代わりに QueryInfo
など)。そして、エディターもPythonもその関数の実行には文句を言いません。内部の操作がエラーになった場合にのみ文句を言います。
Annotated
は複数のメタデータアノテーションを持つことができるため、Typer のような他のツールでも同じ関数を使用できるようになりました。🚀
さらに検証を追加する¶
min_length
パラメータも追加できます。
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[str | None, Query(min_length=3, max_length=50)] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 その他のバージョンとバリアント
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[Union[str, None], Query(min_length=3, max_length=50)] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
from typing import Union
from fastapi import FastAPI, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[Union[str, None], Query(min_length=3, max_length=50)] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, min_length=3, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Union[str, None] = Query(default=None, min_length=3, max_length=50),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
正規表現を追加する¶
パラメータが一致する必要がある正規表現 pattern
を定義できます。
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[
str | None, Query(min_length=3, max_length=50, pattern="^fixedquery$")
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 その他のバージョンとバリアント
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[
Union[str, None], Query(min_length=3, max_length=50, pattern="^fixedquery$")
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
from typing import Union
from fastapi import FastAPI, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[
Union[str, None], Query(min_length=3, max_length=50, pattern="^fixedquery$")
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: str | None = Query(
default=None, min_length=3, max_length=50, pattern="^fixedquery$"
),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Union[str, None] = Query(
default=None, min_length=3, max_length=50, pattern="^fixedquery$"
),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
この特定の正規表現パターンは、受信したパラメータ値が以下であることを確認します。
^
: 以下の文字で始まる。前に文字がない。fixedquery
: 正確な値fixedquery
を持つ。$
: そこで終わる。fixedquery
の後にこれ以上文字がない。
これらの**「正規表現」**の概念に戸惑うなら、心配しないでください。多くの人にとって難しいトピックです。まだ正規表現が必要なくても、多くのことを行うことができます。
これで、必要なときにいつでも**FastAPI**で正規表現を使用できることがわかりました。
Pydantic v1 regex
の代わりに pattern
¶
Pydantic バージョン 2 および FastAPI 0.100.0 より前は、パラメータは pattern
の代わりに regex
と呼ばれていましたが、現在は非推奨です。
まだそれを使用しているコードを見かけるかもしれません。
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[
str | None, Query(min_length=3, max_length=50, regex="^fixedquery$")
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
しかし、これは非推奨であり、新しいパラメータ pattern
を使用するように更新する必要があることを知っておいてください。🤓
デフォルト値¶
もちろん、None
以外のデフォルト値を使用することもできます。
q
クエリパラメータに min_length
を 3
と宣言し、デフォルト値を "fixedquery"
にしたいとします。
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = "fixedquery"):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 その他のバージョンとバリアント
from fastapi import FastAPI, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = "fixedquery"):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(default="fixedquery", min_length=3)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
注
None
を含む任意の型のデフォルト値を持つことで、パラメータはオプション(必須ではない)になります。
必須パラメータ¶
追加の検証やメタデータを宣言する必要がない場合は、q
クエリパラメータを、次のようにデフォルト値を宣言しないだけで必須にできます。
q: str
代わりに
q: str | None = None
しかし、今回は Query
を使って宣言しています。例えば、
q: Annotated[str | None, Query(min_length=3)] = None
したがって、Query
を使用しながら値を必須として宣言する必要がある場合は、デフォルト値を宣言しないだけで済みます。
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)]):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 その他のバージョンとバリアント
from fastapi import FastAPI, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)]):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(min_length=3)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
必須、None
でも可¶
パラメータが None
を受け入れることを宣言できますが、それでも必須です。これにより、クライアントは、値が None
であっても値を送信することを強制されます。
そのためには、None
が有効な型であることを宣言し、デフォルト値を宣言しないだけです。
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(min_length=3)]):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 その他のバージョンとバリアント
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(min_length=3)]):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
from typing import Union
from fastapi import FastAPI, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(min_length=3)]):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(min_length=3)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[str, None] = Query(min_length=3)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
クエリパラメータのリスト / 複数値¶
Query
でクエリパラメータを明示的に定義すると、値のリストを受け取る、つまり複数の値を受け取るように宣言することもできます。
たとえば、URL に複数回出現する可能性のあるクエリパラメータ q
を宣言するには、次のように記述します。
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[list[str] | None, Query()] = None):
query_items = {"q": q}
return query_items
🤓 その他のバージョンとバリアント
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[Union[list[str], None], Query()] = None):
query_items = {"q": q}
return query_items
from typing import List, Union
from fastapi import FastAPI, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[Union[List[str], None], Query()] = None):
query_items = {"q": q}
return query_items
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: list[str] | None = Query(default=None)):
query_items = {"q": q}
return query_items
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[list[str], None] = Query(default=None)):
query_items = {"q": q}
return query_items
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from typing import List, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[List[str], None] = Query(default=None)):
query_items = {"q": q}
return query_items
その後、次のような URL で
https://:8000/items/?q=foo&q=bar
複数の q
*クエリパラメータ*の値(foo
と bar
)を、*パス操作関数*内の Python list
で、*関数パラメータ* q
に受け取ります。
したがって、そのURLへの応答は次のようになります。
{
"q": [
"foo",
"bar"
]
}
ヒント
上記の例のように、list
型のクエリパラメータを宣言するには、Query
を明示的に使用する必要があります。そうしないと、リクエストボディとして解釈されます。
インタラクティブな API ドキュメントは、複数の値を許可するように適切に更新されます。
クエリパラメータのリスト / デフォルト値を持つ複数値¶
値が指定されていない場合に、デフォルトの list
を定義することもできます。
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[list[str], Query()] = ["foo", "bar"]):
query_items = {"q": q}
return query_items
🤓 その他のバージョンとバリアント
from typing import List
from fastapi import FastAPI, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[List[str], Query()] = ["foo", "bar"]):
query_items = {"q": q}
return query_items
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: list[str] = Query(default=["foo", "bar"])):
query_items = {"q": q}
return query_items
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from typing import List
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: List[str] = Query(default=["foo", "bar"])):
query_items = {"q": q}
return query_items
もし
https://:8000/items/
へアクセスすると、q
のデフォルト値は ["foo", "bar"]
になり、応答は次のようになります。
{
"q": [
"foo",
"bar"
]
}
list
のみを使用する¶
list[str]
の代わりに list
を直接使用することもできます。
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[list, Query()] = []):
query_items = {"q": q}
return query_items
🤓 その他のバージョンとバリアント
from fastapi import FastAPI, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[list, Query()] = []):
query_items = {"q": q}
return query_items
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: list = Query(default=[])):
query_items = {"q": q}
return query_items
注
この場合、FastAPI はリストの内容をチェックしないことに注意してください。
たとえば、list[int]
はリストの内容が整数であることをチェック(およびドキュメント化)しますが、list
単独ではチェックしません。
その他のメタデータを宣言する¶
パラメータに関する追加情報を追加できます。
この情報は生成された OpenAPI に含まれ、ドキュメントのユーザーインターフェースや外部ツールで使用されます。
注
異なるツールは、OpenAPI のサポートレベルが異なる場合があることに注意してください。
一部のツールでは、宣言されたすべての追加情報がまだ表示されない場合がありますが、ほとんどの場合、不足している機能はすでに開発が予定されています。
title
を追加できます。
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[str | None, Query(title="Query string", min_length=3)] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 その他のバージョンとバリアント
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[Union[str, None], Query(title="Query string", min_length=3)] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
from typing import Union
from fastapi import FastAPI, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[Union[str, None], Query(title="Query string", min_length=3)] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: str | None = Query(default=None, title="Query string", min_length=3),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Union[str, None] = Query(default=None, title="Query string", min_length=3),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
そして description
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[
str | None,
Query(
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
),
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 その他のバージョンとバリアント
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[
Union[str, None],
Query(
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
),
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
from typing import Union
from fastapi import FastAPI, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[
Union[str, None],
Query(
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
),
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: str | None = Query(
default=None,
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Union[str, None] = Query(
default=None,
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
エイリアスパラメータ¶
パラメータを item-query
にしたいと想像してください。
次のように
http://127.0.0.1:8000/items/?item-query=foobaritems
しかし、item-query
は有効な Python 変数名ではありません。
最も近いのは item_query
でしょう。
しかし、それでも厳密に item-query
である必要があります...
その場合、alias
を宣言できます。そのエイリアスが、パラメータの値を見つけるために使用されます。
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(alias="item-query")] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 その他のバージョンとバリアント
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(alias="item-query")] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
from typing import Union
from fastapi import FastAPI, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(alias="item-query")] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, alias="item-query")):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, alias="item-query")):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
非推奨のパラメータ¶
さて、このパラメータがもう気に入らないとしましょう。
クライアントが使用しているため、しばらくは残しておく必要がありますが、ドキュメントではそれを非推奨として明確に表示したいと考えています。
次に、Query
にパラメータ deprecated=True
を渡します。
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[
str | None,
Query(
alias="item-query",
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
max_length=50,
pattern="^fixedquery$",
deprecated=True,
),
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 その他のバージョンとバリアント
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[
Union[str, None],
Query(
alias="item-query",
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
max_length=50,
pattern="^fixedquery$",
deprecated=True,
),
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
from typing import Union
from fastapi import FastAPI, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[
Union[str, None],
Query(
alias="item-query",
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
max_length=50,
pattern="^fixedquery$",
deprecated=True,
),
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: str | None = Query(
default=None,
alias="item-query",
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
max_length=50,
pattern="^fixedquery$",
deprecated=True,
),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Union[str, None] = Query(
default=None,
alias="item-query",
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
max_length=50,
pattern="^fixedquery$",
deprecated=True,
),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
ドキュメントには次のように表示されます。
OpenAPI からパラメータを除外する¶
生成された OpenAPI スキーマ (および自動ドキュメントシステム) からクエリパラメータを除外するには、Query
のパラメータ include_in_schema
を False
に設定します。
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
hidden_query: Annotated[str | None, Query(include_in_schema=False)] = None,
):
if hidden_query:
return {"hidden_query": hidden_query}
else:
return {"hidden_query": "Not found"}
🤓 その他のバージョンとバリアント
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
hidden_query: Annotated[Union[str, None], Query(include_in_schema=False)] = None,
):
if hidden_query:
return {"hidden_query": hidden_query}
else:
return {"hidden_query": "Not found"}
from typing import Union
from fastapi import FastAPI, Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(
hidden_query: Annotated[Union[str, None], Query(include_in_schema=False)] = None,
):
if hidden_query:
return {"hidden_query": hidden_query}
else:
return {"hidden_query": "Not found"}
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
hidden_query: str | None = Query(default=None, include_in_schema=False),
):
if hidden_query:
return {"hidden_query": hidden_query}
else:
return {"hidden_query": "Not found"}
ヒント
可能であれば`Annotated`バージョンを使用することをお勧めします。
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
hidden_query: Union[str, None] = Query(default=None, include_in_schema=False),
):
if hidden_query:
return {"hidden_query": hidden_query}
else:
return {"hidden_query": "Not found"}
カスタム検証¶
上記で示したパラメータではできない、いくつかの**カスタム検証**を行う必要がある場合があります。
そのような場合、通常の検証(例:値が str
であることの検証後)の後で適用される**カスタムバリデータ関数**を使用できます。
これは、Annotated
内で Pydantic の AfterValidator
を使用して実現できます。
ヒント
Pydantic には BeforeValidator
などもあります。🤓
たとえば、このカスタムバリデーターは、項目 ID が ISBN の場合は isbn-
で始まるか、IMDB の映画 URL ID の場合は imdb-
で始まることをチェックします。
import random
from typing import Annotated
from fastapi import FastAPI
from pydantic import AfterValidator
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[str | None, AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
🤓 その他のバージョンとバリアント
import random
from typing import Annotated, Union
from fastapi import FastAPI
from pydantic import AfterValidator
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[Union[str, None], AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
import random
from typing import Union
from fastapi import FastAPI
from pydantic import AfterValidator
from typing_extensions import Annotated
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[Union[str, None], AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
情報
これは Pydantic バージョン 2 以降で利用可能です。😎
ヒント
データベースや別のAPIなど、**外部コンポーネント**との通信を必要とするあらゆる種類の検証が必要な場合は、代わりに**FastAPIの依存関係**を使用する必要があります。これらについては後で学習します。
これらのカスタムバリデーターは、リクエストで提供された**同じデータのみ**でチェックできるもの向けです。
コードを理解する¶
重要な点は、**Annotated
内で関数とともに AfterValidator
を使用する**ことだけです。この部分は自由にスキップしてください。🤸
しかし、この特定のコード例に興味があり、まだ楽しんでいるのであれば、いくつかの追加の詳細を説明します。
value.startswith()
を使用する文字列¶
気づきましたか? value.startswith()
を使用する文字列はタプルを受け取ることができ、タプルの各値をチェックします。
# Code above omitted 👆
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
# Code below omitted 👇
👀 ファイル全体のプレビュー
import random
from typing import Annotated
from fastapi import FastAPI
from pydantic import AfterValidator
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[str | None, AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
🤓 その他のバージョンとバリアント
import random
from typing import Annotated, Union
from fastapi import FastAPI
from pydantic import AfterValidator
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[Union[str, None], AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
import random
from typing import Union
from fastapi import FastAPI
from pydantic import AfterValidator
from typing_extensions import Annotated
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[Union[str, None], AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
ランダムなアイテム¶
data.items()
を使用すると、各辞書項目のキーと値を含むタプルを持つイテラブルオブジェクトが取得されます。
このイテラブルオブジェクトを list(data.items())
で適切な list
に変換します。
次に、random.choice()
を使用してリストから**ランダムな値**を取得し、(id, name)
のタプルを取得します。これは ("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")
のようなものになります。
そして、そのタプルの**2つの値を**変数 id
と name
に**割り当てます**。
したがって、ユーザーが項目 ID を指定しなかった場合でも、ランダムな提案が受け取られます。
...これらすべてを**1つのシンプルな行**で行います。🤯 Pythonが好きじゃないですか?🐍
# Code above omitted 👆
@app.get("/items/")
async def read_items(
id: Annotated[str | None, AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
👀 ファイル全体のプレビュー
import random
from typing import Annotated
from fastapi import FastAPI
from pydantic import AfterValidator
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[str | None, AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
🤓 その他のバージョンとバリアント
import random
from typing import Annotated, Union
from fastapi import FastAPI
from pydantic import AfterValidator
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[Union[str, None], AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
import random
from typing import Union
from fastapi import FastAPI
from pydantic import AfterValidator
from typing_extensions import Annotated
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[Union[str, None], AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
まとめ¶
パラメータに追加の検証とメタデータを宣言できます。
一般的な検証とメタデータ
エイリアス
タイトル
説明
非推奨
文字列に特化した検証
min_length
max_length
pattern
AfterValidator
を使用したカスタム検証。
これらの例では、str
値の検証を宣言する方法を示しました。
数値のような他の型の検証を宣言する方法については、次の章を参照してください。