Aller au contenu

Dépendances

🌐 Traduction par IA et humains

Cette traduction a Ă©tĂ© rĂ©alisĂ©e par une IA guidĂ©e par des humains. đŸ€

Elle peut contenir des erreurs d'interprĂ©tation du sens original, ou paraĂźtre peu naturelle, etc. đŸ€–

Vous pouvez améliorer cette traduction en nous aidant à mieux guider le LLM d'IA.

Version anglaise

FastAPI dispose d’un systĂšme d’Injection de dĂ©pendances trĂšs puissant mais intuitif.

Il est conçu pour ĂȘtre trĂšs simple Ă  utiliser, et pour faciliter l’intĂ©gration d’autres composants Ă  FastAPI pour n’importe quel dĂ©veloppeur.

Qu’est-ce que « l’injection de dĂ©pendances »

L’« injection de dĂ©pendances » signifie, en programmation, qu’il existe un moyen pour votre code (dans ce cas, vos fonctions de chemins d’accĂšs) de dĂ©clarer ce dont il a besoin pour fonctionner et utiliser : « dĂ©pendances ».

Ensuite, ce systÚme (dans ce cas FastAPI) se charge de faire tout le nécessaire pour fournir à votre code ces dépendances requises (« injecter » les dépendances).

C’est trùs utile lorsque vous avez besoin de :

  • Avoir de la logique partagĂ©e (la mĂȘme logique de code encore et encore).
  • Partager des connexions Ă  la base de donnĂ©es.
  • Imposer la sĂ©curitĂ©, l’authentification, des exigences de rĂŽles, etc.
  • Et bien d’autres choses ...

Tout cela, en minimisant la répétition de code.

Premiers pas

Voyons un exemple trùs simple. Il sera tellement simple qu’il n’est pas trùs utile, pour l’instant.

Mais de cette façon nous pouvons nous concentrer sur le fonctionnement du systĂšme d’injection de dĂ©pendances.

Créer une dépendance, ou « dependable »

Concentrons-nous d’abord sur la dĂ©pendance.

C’est simplement une fonction qui peut prendre tous les mĂȘmes paramĂštres qu’une fonction de chemin d’accĂšs peut prendre :

from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
    return commons


@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
    return commons
đŸ€“ Other versions and variants

Tip

Prefer to use the Annotated version if possible.

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    return commons


@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
    return commons

C’est tout.

2 lignes.

Et elle a la mĂȘme forme et structure que toutes vos fonctions de chemins d’accĂšs.

Vous pouvez la considĂ©rer comme une fonction de chemin d’accĂšs sans le « dĂ©corateur » (sans le @app.get("/some-path")).

Et elle peut retourner tout ce que vous voulez.

Dans ce cas, cette dépendance attend :

  • Un paramĂštre de requĂȘte optionnel q qui est une str.
  • Un paramĂštre de requĂȘte optionnel skip qui est un int, et vaut 0 par dĂ©faut.
  • Un paramĂštre de requĂȘte optionnel limit qui est un int, et vaut 100 par dĂ©faut.

Puis elle retourne simplement un dict contenant ces valeurs.

Info

FastAPI a ajouté la prise en charge de Annotated (et a commencé à le recommander) dans la version 0.95.0.

Si vous avez une version plus ancienne, vous obtiendrez des erreurs en essayant d’utiliser Annotated.

Vous devez vous assurer de mettre à niveau la version de FastAPI vers au moins la 0.95.1 avant d’utiliser Annotated.

Importer Depends

from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
    return commons


@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
    return commons
đŸ€“ Other versions and variants

Tip

Prefer to use the Annotated version if possible.

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    return commons


@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
    return commons

Déclarer la dépendance, dans le « dependant »

De la mĂȘme maniĂšre que vous utilisez Body, Query, etc. avec les paramĂštres de votre fonction de chemin d’accĂšs, utilisez Depends avec un nouveau paramĂštre :

from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
    return commons


@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
    return commons
đŸ€“ Other versions and variants

Tip

Prefer to use the Annotated version if possible.

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    return commons


@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
    return commons

MĂȘme si vous utilisez Depends dans les paramĂštres de votre fonction de la mĂȘme façon que Body, Query, etc., Depends fonctionne un peu diffĂ©remment.

Vous ne donnez à Depends qu’un seul paramùtre.

Ce paramĂštre doit ĂȘtre quelque chose comme une fonction.

Vous ne l’appelez pas directement (n’ajoutez pas de parenthùses à la fin), vous le passez simplement en paramùtre à Depends().

Et cette fonction prend des paramĂštres de la mĂȘme maniĂšre que les fonctions de chemins d’accĂšs.

Astuce

Vous verrez quelles autres « choses », en plus des fonctions, peuvent ĂȘtre utilisĂ©es comme dĂ©pendances dans le prochain chapitre.

Chaque fois qu’une nouvelle requĂȘte arrive, FastAPI se charge de :

  • Appeler votre fonction de dĂ©pendance (« dependable ») avec les bons paramĂštres.
  • RĂ©cupĂ©rer le rĂ©sultat de votre fonction.
  • Affecter ce rĂ©sultat au paramĂštre dans votre fonction de chemin d’accĂšs.
graph TB

common_parameters(["common_parameters"])
read_items["/items/"]
read_users["/users/"]

common_parameters --> read_items
common_parameters --> read_users

De cette façon vous Ă©crivez le code partagĂ© une seule fois et FastAPI se charge de l’appeler pour vos chemins d’accĂšs.

Vérifications

Notez que vous n’avez pas Ă  crĂ©er une classe spĂ©ciale et Ă  la passer quelque part Ă  FastAPI pour l’« enregistrer » ou quoi que ce soit de similaire.

Vous la passez simplement Ă  Depends et FastAPI sait faire le reste.

Partager des dépendances Annotated

Dans les exemples ci-dessus, vous voyez qu’il y a un tout petit peu de duplication de code.

Lorsque vous devez utiliser la dĂ©pendance common_parameters(), vous devez Ă©crire tout le paramĂštre avec l’annotation de type et Depends() :

commons: Annotated[dict, Depends(common_parameters)]

Mais comme nous utilisons Annotated, nous pouvons stocker cette valeur Annotated dans une variable et l’utiliser à plusieurs endroits :

from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


CommonsDep = Annotated[dict, Depends(common_parameters)]


@app.get("/items/")
async def read_items(commons: CommonsDep):
    return commons


@app.get("/users/")
async def read_users(commons: CommonsDep):
    return commons

Astuce

C’est simplement du Python standard, cela s’appelle un « alias de type », ce n’est en fait pas spĂ©cifique Ă  FastAPI.

Mais comme FastAPI est basĂ© sur les standards Python, y compris Annotated, vous pouvez utiliser cette astuce dans votre code. 😎

Les dĂ©pendances continueront de fonctionner comme prĂ©vu, et la meilleure partie est que l’information de type sera conservĂ©e, ce qui signifie que votre Ă©diteur pourra continuer Ă  vous fournir l’autocomplĂ©tion, des erreurs en ligne, etc. Idem pour d’autres outils comme mypy.

Cela sera particuliĂšrement utile lorsque vous l’utiliserez dans une grande base de code oĂč vous utilisez les mĂȘmes dĂ©pendances encore et encore dans de nombreux chemins d’accĂšs.

Utiliser async ou non

Comme les dĂ©pendances seront aussi appelĂ©es par FastAPI (tout comme vos fonctions de chemins d’accĂšs), les mĂȘmes rĂšgles s’appliquent lors de la dĂ©finition de vos fonctions.

Vous pouvez utiliser async def ou un def normal.

Et vous pouvez dĂ©clarer des dĂ©pendances avec async def Ă  l’intĂ©rieur de fonctions de chemins d’accĂšs def normales, ou des dĂ©pendances def Ă  l’intĂ©rieur de fonctions de chemins d’accĂšs async def, etc.

Peu importe. FastAPI saura quoi faire.

Remarque

Si vous ne savez pas, consultez la section Async : « Pressé ? » à propos de async et await dans la documentation.

Intégrer à OpenAPI

Toutes les dĂ©clarations de requĂȘte, validations et exigences de vos dĂ©pendances (et sous-dĂ©pendances) seront intĂ©grĂ©es dans le mĂȘme schĂ©ma OpenAPI.

Ainsi, la documentation interactive contiendra aussi toutes les informations issues de ces dépendances :

Utilisation simple

Si vous y regardez de prĂšs, les fonctions de chemins d’accĂšs sont dĂ©clarĂ©es pour ĂȘtre utilisĂ©es chaque fois qu’un « chemin » et une « opĂ©ration » correspondent, puis FastAPI se charge d’appeler la fonction avec les bons paramĂštres, en extrayant les donnĂ©es de la requĂȘte.

En réalité, tous (ou la plupart) des frameworks web fonctionnent de cette maniÚre.

Vous n’appelez jamais ces fonctions directement. Elles sont appelĂ©es par votre framework (dans ce cas, FastAPI).

Avec le systĂšme d’injection de dĂ©pendances, vous pouvez aussi indiquer Ă  FastAPI que votre fonction de chemin d’accĂšs « dĂ©pend » Ă©galement d’autre chose qui doit ĂȘtre exĂ©cutĂ© avant votre fonction de chemin d’accĂšs, et FastAPI se chargera de l’exĂ©cuter et d’« injecter » les rĂ©sultats.

D’autres termes courants pour cette mĂȘme idĂ©e « d’injection de dĂ©pendances » sont :

  • ressources
  • fournisseurs
  • services
  • injectables
  • composants

Plug-ins FastAPI

Les intĂ©grations et « plug-ins » peuvent ĂȘtre construits en utilisant le systĂšme d’injection de dĂ©pendances. Mais en rĂ©alitĂ©, il n’y a pas besoin de crĂ©er des « plug-ins », car en utilisant des dĂ©pendances il est possible de dĂ©clarer un nombre infini d’intĂ©grations et d’interactions qui deviennent disponibles pour vos fonctions de chemins d’accĂšs.

Et les dĂ©pendances peuvent ĂȘtre créées de maniĂšre trĂšs simple et intuitive, ce qui vous permet d’importer juste les packages Python dont vous avez besoin, et de les intĂ©grer Ă  vos fonctions d’API en quelques lignes de code, littĂ©ralement.

Vous verrez des exemples de cela dans les prochains chapitres, à propos des bases de données relationnelles et NoSQL, de la sécurité, etc.

Compatibilité FastAPI

La simplicitĂ© du systĂšme d’injection de dĂ©pendances rend FastAPI compatible avec :

  • toutes les bases de donnĂ©es relationnelles
  • les bases de donnĂ©es NoSQL
  • les packages externes
  • les API externes
  • les systĂšmes d’authentification et d’autorisation
  • les systĂšmes de supervision d’usage d’API
  • les systĂšmes d’injection de donnĂ©es de rĂ©ponse
  • etc.

Simple et puissant

Bien que le systĂšme hiĂ©rarchique d’injection de dĂ©pendances soit trĂšs simple Ă  dĂ©finir et Ă  utiliser, il reste trĂšs puissant.

Vous pouvez définir des dépendances qui, à leur tour, peuvent définir leurs propres dépendances.

Au final, un arbre hiĂ©rarchique de dĂ©pendances est construit, et le systĂšme d’injection de dĂ©pendances se charge de rĂ©soudre toutes ces dĂ©pendances pour vous (et leurs sous-dĂ©pendances) et de fournir (injecter) les rĂ©sultats Ă  chaque Ă©tape.

Par exemple, supposons que vous ayez 4 endpoints d’API (chemins d’accùs) :

  • /items/public/
  • /items/private/
  • /users/{user_id}/activate
  • /items/pro/

alors vous pourriez ajouter diffĂ©rentes exigences d’autorisations pour chacun d’eux uniquement avec des dĂ©pendances et des sous-dĂ©pendances :

graph TB

current_user(["current_user"])
active_user(["active_user"])
admin_user(["admin_user"])
paying_user(["paying_user"])

public["/items/public/"]
private["/items/private/"]
activate_user["/users/{user_id}/activate"]
pro_items["/items/pro/"]

current_user --> active_user
active_user --> admin_user
active_user --> paying_user

current_user --> public
active_user --> private
admin_user --> activate_user
paying_user --> pro_items

Intégrer à OpenAPI

Toutes ces dĂ©pendances, tout en dĂ©clarant leurs exigences, ajoutent Ă©galement des paramĂštres, des validations, etc. Ă  vos chemins d’accĂšs.

FastAPI se chargera d’ajouter le tout au schĂ©ma OpenAPI, afin que cela apparaisse dans les systĂšmes de documentation interactive.