コンテンツへスキップ

サブ・ディペンデンシー

サブ・ディペンデンシーを持つディペンデンシーを作成できます。

必要なだけ深くすることができます。

FastAPIがそれらの解決を処理します。

最初のディペンデンシー "dependable"

次のように最初のディペンデンシー ("dependable") を作成できます。

from typing import Annotated

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: str | None = None):
    return q


def query_or_cookie_extractor(
    q: Annotated[str, Depends(query_extractor)],
    last_query: Annotated[str | None, Cookie()] = None,
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(
    query_or_default: Annotated[str, Depends(query_or_cookie_extractor)],
):
    return {"q_or_cookie": query_or_default}
🤓 その他のバージョンとバリアント
from typing import Annotated, Union

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: Union[str, None] = None):
    return q


def query_or_cookie_extractor(
    q: Annotated[str, Depends(query_extractor)],
    last_query: Annotated[Union[str, None], Cookie()] = None,
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(
    query_or_default: Annotated[str, Depends(query_or_cookie_extractor)],
):
    return {"q_or_cookie": query_or_default}
from typing import Union

from fastapi import Cookie, Depends, FastAPI
from typing_extensions import Annotated

app = FastAPI()


def query_extractor(q: Union[str, None] = None):
    return q


def query_or_cookie_extractor(
    q: Annotated[str, Depends(query_extractor)],
    last_query: Annotated[Union[str, None], Cookie()] = None,
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(
    query_or_default: Annotated[str, Depends(query_or_cookie_extractor)],
):
    return {"q_or_cookie": query_or_default}

ヒント

可能であれば`Annotated`バージョンを使用することをお勧めします。

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: str | None = None):
    return q


def query_or_cookie_extractor(
    q: str = Depends(query_extractor), last_query: str | None = Cookie(default=None)
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
    return {"q_or_cookie": query_or_default}

ヒント

可能であれば`Annotated`バージョンを使用することをお勧めします。

from typing import Union

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: Union[str, None] = None):
    return q


def query_or_cookie_extractor(
    q: str = Depends(query_extractor),
    last_query: Union[str, None] = Cookie(default=None),
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
    return {"q_or_cookie": query_or_default}

これはオプションのクエリパラメーター qstr として宣言し、それを返すだけです。

これは非常にシンプルで (あまり有用ではありませんが)、サブ・ディペンデンシーがどのように機能するかに焦点を当てるのに役立ちます。

2番目のディペンデンシー、"dependable"と"dependant"

次に、同時に独自のディペンデンシーを宣言する別のディペンデンシー関数 ("dependable") を作成できます (そのため、それも "dependant" です)。

from typing import Annotated

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: str | None = None):
    return q


def query_or_cookie_extractor(
    q: Annotated[str, Depends(query_extractor)],
    last_query: Annotated[str | None, Cookie()] = None,
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(
    query_or_default: Annotated[str, Depends(query_or_cookie_extractor)],
):
    return {"q_or_cookie": query_or_default}
🤓 その他のバージョンとバリアント
from typing import Annotated, Union

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: Union[str, None] = None):
    return q


def query_or_cookie_extractor(
    q: Annotated[str, Depends(query_extractor)],
    last_query: Annotated[Union[str, None], Cookie()] = None,
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(
    query_or_default: Annotated[str, Depends(query_or_cookie_extractor)],
):
    return {"q_or_cookie": query_or_default}
from typing import Union

from fastapi import Cookie, Depends, FastAPI
from typing_extensions import Annotated

app = FastAPI()


def query_extractor(q: Union[str, None] = None):
    return q


def query_or_cookie_extractor(
    q: Annotated[str, Depends(query_extractor)],
    last_query: Annotated[Union[str, None], Cookie()] = None,
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(
    query_or_default: Annotated[str, Depends(query_or_cookie_extractor)],
):
    return {"q_or_cookie": query_or_default}

ヒント

可能であれば`Annotated`バージョンを使用することをお勧めします。

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: str | None = None):
    return q


def query_or_cookie_extractor(
    q: str = Depends(query_extractor), last_query: str | None = Cookie(default=None)
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
    return {"q_or_cookie": query_or_default}

ヒント

可能であれば`Annotated`バージョンを使用することをお勧めします。

from typing import Union

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: Union[str, None] = None):
    return q


def query_or_cookie_extractor(
    q: str = Depends(query_extractor),
    last_query: Union[str, None] = Cookie(default=None),
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
    return {"q_or_cookie": query_or_default}

宣言されたパラメーターに焦点を当てましょう。

  • この関数自体はディペンデンシー ("dependable") ですが、別のディペンデンシーも宣言しています (他のものに "依存" しています)。
    • それは query_extractor に依存し、その関数から返された値をパラメーター q に割り当てます。
  • また、オプションの last_query クッキーを str として宣言します。
    • ユーザーがクエリ q を提供しなかった場合、以前にクッキーに保存した最後のクエリを使用します。

ディペンデンシーを使用する

次に、ディペンデンシーを次のように使用できます。

from typing import Annotated

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: str | None = None):
    return q


def query_or_cookie_extractor(
    q: Annotated[str, Depends(query_extractor)],
    last_query: Annotated[str | None, Cookie()] = None,
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(
    query_or_default: Annotated[str, Depends(query_or_cookie_extractor)],
):
    return {"q_or_cookie": query_or_default}
🤓 その他のバージョンとバリアント
from typing import Annotated, Union

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: Union[str, None] = None):
    return q


def query_or_cookie_extractor(
    q: Annotated[str, Depends(query_extractor)],
    last_query: Annotated[Union[str, None], Cookie()] = None,
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(
    query_or_default: Annotated[str, Depends(query_or_cookie_extractor)],
):
    return {"q_or_cookie": query_or_default}
from typing import Union

from fastapi import Cookie, Depends, FastAPI
from typing_extensions import Annotated

app = FastAPI()


def query_extractor(q: Union[str, None] = None):
    return q


def query_or_cookie_extractor(
    q: Annotated[str, Depends(query_extractor)],
    last_query: Annotated[Union[str, None], Cookie()] = None,
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(
    query_or_default: Annotated[str, Depends(query_or_cookie_extractor)],
):
    return {"q_or_cookie": query_or_default}

ヒント

可能であれば`Annotated`バージョンを使用することをお勧めします。

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: str | None = None):
    return q


def query_or_cookie_extractor(
    q: str = Depends(query_extractor), last_query: str | None = Cookie(default=None)
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
    return {"q_or_cookie": query_or_default}

ヒント

可能であれば`Annotated`バージョンを使用することをお勧めします。

from typing import Union

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: Union[str, None] = None):
    return q


def query_or_cookie_extractor(
    q: str = Depends(query_extractor),
    last_query: Union[str, None] = Cookie(default=None),
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
    return {"q_or_cookie": query_or_default}

情報

パスオペレーション関数で宣言しているディペンデンシーは、query_or_cookie_extractor の1つだけであることに注目してください。

しかし、FastAPIは、query_extractor を最初に解決し、その結果を query_or_cookie_extractor を呼び出すときに渡す必要があることを知っています。

graph TB

query_extractor(["query_extractor"])
query_or_cookie_extractor(["query_or_cookie_extractor"])

read_query["/items/"]

query_extractor --> query_or_cookie_extractor --> read_query

同じディペンデンシーを複数回使用する

同じパスオペレーションに対して、ディペンデンシーの1つが複数回宣言された場合 (たとえば、複数のディペンデンシーが共通のサブ・ディペンデンシーを持つ場合)、FastAPIは、そのサブ・ディペンデンシーをリクエストごとに1回だけ呼び出すことを認識します。

そして、返された値を"キャッシュ"に保存し、その特定のリクエストでそれを必要とするすべての "dependants" に渡します。これにより、同じリクエストに対してディペンデンシーを複数回呼び出すことを避けます。

「キャッシュされた」値を使用する代わりに、同じリクエスト内で各ステップで(場合によっては複数回)ディペンデンシーが呼び出される必要があることを知っている高度なシナリオでは、Dependsを使用する際にuse_cache=Falseパラメータを設定できます。

async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
    return {"fresh_value": fresh_value}

ヒント

可能であれば`Annotated`バージョンを使用することをお勧めします。

async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
    return {"fresh_value": fresh_value}

まとめ

ここで使われている派手な言葉はすべてさておき、Dependency Injectionシステムは非常にシンプルです。

それはパスオペレーション関数と同じように見える単なる関数です。

しかし、それでも非常に強力であり、任意に深くネストされたディペンデンシー "グラフ" (ツリー) を宣言することができます。

ヒント

これらのシンプルな例では、これがそれほど有用であるようには見えないかもしれません。

しかし、セキュリティに関する章で、その有用性を見ることになるでしょう。

そして、それがどれだけのコードを節約してくれるかもわかるでしょう。