コンテンツへスキップ

クエリパラメータと文字列の検証

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

クエリパラメータ qstr | None 型です。これは str 型ですが None にもなり得ることを意味し、実際、デフォルト値は None なので、FastAPI はそれが必須ではないことを認識します。

Note

FastAPI は、デフォルト値 = None のため、q の値が必須ではないことを認識します。

str | None を使用することで、エディタがより良いサポートを提供し、エラーを検出できるようになります。

追加の検証

q がオプションであっても、提供された場合はその長さが50文字を超えないように強制します。

QueryAnnotated をインポートする

これを実現するには、まずインポートします

  • 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

これらの両方のバージョンは同じ意味で、qstr または None になり得るパラメータであり、デフォルトでは None です。

さて、楽しいことに入りましょう。🎉

q パラメータの AnnotatedQuery を追加する

さらに情報 (この場合は追加の検証) を配置できる Annotated があるので、Annotated の中に Query を追加し、パラメータ max_length50 に設定します。

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() を使用しています。後で、Query() と同じ引数を受け入れる Path()Body()Header()Cookie() などの他のものを見るでしょう。

FastAPI はこれから

  • 最大長が50文字であることを確認するデータの検証
  • データが無効な場合にクライアントに明確なエラーを表示する
  • OpenAPI スキーマのパス操作にパラメータを文書化する (これにより自動ドキュメント UI に表示されます)

代替 (旧): デフォルト値としての Query

FastAPI の以前のバージョン ( 0.95.0 以前) では、QueryAnnotated に入れる代わりに、パラメータのデフォルト値として 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 を使用しない) では、関数のデフォルト値 NoneQuery() に置き換える必要があるため、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 を使用する場合、Querydefault パラメータを使用できないことに注意してください。

代わりに、関数パラメータの実際のデフォルト値を使用してください。そうしないと、矛盾が生じます。

例えば、これは許可されません

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 pattern の代わりに regex

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_length3 にし、デフォルト値を "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

Note

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 クエリパラメータの値 (foobar) を、パス操作関数内の 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

Note

この場合、FastAPI はリストの内容をチェックしないことに注意してください。

例えば、list[int] はリストの内容が整数であることをチェックし (そして文書化します)、しかし list だけではチェックしません。

さらにメタデータを宣言する

パラメータに関する詳細情報を追加できます。

この情報は、生成された OpenAPI に含まれ、ドキュメントのユーザーインターフェースや外部ツールで使用されます。

Note

異なるツールは 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

非推奨パラメータ

このパラメータがもう気に入らないとしましょう。

クライアントがまだ使用しているのでしばらく残しておく必要がありますが、ドキュメントには非推奨と明確に表示したいと考えています。

その場合は、パラメータ deprecated=TrueQuery に渡します。

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 スキーマ(したがって、自動ドキュメントシステム)からクエリパラメータを除外するには、Queryinclude_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つの値を変数 idname代入します

したがって、ユーザーがアイテム 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}

まとめ

パラメータの追加の検証とメタデータを宣言できます。

一般的な検証とメタデータ

  • エイリアス
  • title
  • description
  • deprecated

文字列に特化した検証

  • min_length
  • max_length
  • pattern

AfterValidator を使用したカスタム検証。

これらの例では、str 値の検証を宣言する方法を示しました。

数値などの他の型の検証を宣言する方法については、次の章を参照してください。