Saltar al contenido

Sub-dependencias

Puedes crear dependencias que tengan sub-dependencias.

Pueden ser tan profundas como necesites.

FastAPI se encargará de resolverlas.

Primera dependencia "dependable"

Podrías crear una primera dependencia ("dependable") así:

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}
🤓 Otras versiones y variantes

Consejo

Preferible usar la versión con Annotated si es posible.

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}

Declara un parámetro de query opcional q como un str, y luego simplemente lo devuelve.

Esto es bastante simple (no muy útil), pero nos ayudará a enfocarnos en cómo funcionan las sub-dependencias.

Segunda dependencia, "dependable" y "dependant"

Luego puedes crear otra función de dependencia (un "dependable") que al mismo tiempo declara una dependencia propia (por lo que también es un "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}
🤓 Otras versiones y variantes

Consejo

Preferible usar la versión con Annotated si es posible.

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}

Enfoquémonos en los parámetros declarados:

  • Even though this function is a dependency ("dependable") itself, it also declares another dependency (it "depends" on something else).
    • Depende de query_extractor, y asigna el valor devuelto por este al parámetro q.
  • It also declares an optional last_query cookie, as a str.
    • Si el usuario no proporcionó ningún query q, usamos el último query utilizado, que guardamos en una cookie anteriormente.

Usar la dependencia

Luego podemos usar la dependencia con:

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}
🤓 Otras versiones y variantes

Consejo

Preferible usar la versión con Annotated si es posible.

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}

Nota

Ten en cuenta que solo estamos declarando una dependencia en la path operation function, el query_or_cookie_extractor.

Pero FastAPI sabrá que tiene que resolver query_extractor primero, para pasar los resultados de eso a query_or_cookie_extractor al llamarlo.

Usando la misma dependencia múltiples veces

Si una de tus dependencias se declara múltiples veces para el mismo path operation, por ejemplo, múltiples dependencias tienen una sub-dependencia en común, FastAPI sabrá llamar a esa sub-dependencia solo una vez por request.

Y guardará el valor devuelto en un "cache" y lo pasará a todos los "dependants" que lo necesiten en ese request específico, en lugar de llamar a la dependencia múltiples veces para el mismo request.

En un escenario avanzado donde sabes que necesitas que la dependencia sea llamada en cada paso (posiblemente múltiples veces) en el mismo request en lugar de usar el valor "cacheado", puedes establecer el parámetro use_cache=False al usar Depends:

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

Consejo

Preferible usar la versión con Annotated si es posible.

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

Resumen

Aparte de todas las palabras fancy usadas aquí, el sistema de Dependency Injection es bastante simple.

Solo funciones que se ven igual que las path operation functions.

Pero aún así, es muy poderoso, y te permite declarar "grafos" (árboles) de dependencias anidadas arbitrariamente profundas.

Consejo

Todo esto podría no parecer útil con estos ejemplos simples.

Pero verás qué tan útil es en los capítulos sobre seguridad.

Y también verás la cantidad de código que te ahorrará.