[PYTHON] Paramètres de chemin du mémo du didacticiel FastAPI Partie 2

introduction

FastAPI Souce: https://github.com/tiangolo/fastapi FastAPI Document: https://fastapi.tiangolo.com

Cible: paramètres de chemin (https://fastapi.tiangolo.com/tutorial/path-params/)

Notes pour le didacticiel FastAPI.

Ceci est la suite de FastAPI Tutorial Note 1. L'installation de FastAPI et le démarrage du serveur sont les mêmes que ci-dessus, veuillez donc consulter l'article ci-dessus.

En gros, je me réfère au tutoriel officiel de Fast API, mais j'oublie certaines parties ou je change l'ordre de mon propre apprentissage. Veuillez vous référer à la documentation officielle pour les informations correctes et détaillées.

Étant donné que je suis un débutant en Web et Python et que je compte sur Google et DeepL pour la traduction, j'apprécierais que vous puissiez signaler les erreurs.

Environnement de développement

Ubuntu 20.04 LTS Python 3.8.2 pipenv 2018.11.26

Cible

--Comprendre le ** paramètre Path ** de FastAPI

procédure

Intro

FastAPI vous permet de déclarer des paramètres et des variables pour les chemins dans la même syntaxe que le format Python. Enregistrez le code suivant sous main.py.

main.py


from fastapi import FastAPI

app = FastAPI()


@app.get("/items/{item_id}")
async def read_item(item_id):
    return {"item_id": item_id}

À ce stade, le ʻitem_id de la fonction de décorateur@app.get ("/ items / {item_id}") ʻest le ** paramètre Path **, qui est passé à l'argument de la fonction comme argument ʻitem_id. Par exemple, après avoir enregistré ce fichier, démarrez le serveur (ʻuvicorn main: app --reload) et ouvrez http://127.0.0.1:8000/items/foo dans votre navigateur. Vous devriez obtenir un texte au format JSON avec la chaîne foo affectée à la variable ʻitem_id` comme indiqué ci-dessous.

{"item_id":"foo"}

Écran réel

qiita_2_1 2020-07-12 22-53-23.png

C'est la base du ** paramètre Path ** dans l'API Fast.

Paramètre de chemin typé

FastAPI vous permet de déclarer le type de ** paramètre Path ** dans une fonction à l'aide d'annotations de type Python standard.

main


~
#Omission
~

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

Notez la ligne ʻasync def read_item (item_id: int): qui déclare la fonction. Ici, l'argument ʻitem_id est déclaré comme ʻint(type entier). Les déclarations de type vous apporteront un support d'édition tel que la vérification des erreurs et la complétion de mots. (Dans le code ci-dessus, si j'ajoute des nombres et des chaînes commefoo = item_id +" bar "`, une erreur par Type Checker est affichée dans mon environnement (PyCharm).)

Si vous ouvrez http://127.0.0.1:8000/items/3 avec le code ci-dessus avec la partie fonction de main.py modifiée, vous verrez ce qui suit.

{"item_id":3}

Il faut noter ici que la valeur reçue par la fonction read_item et affichée comme valeur de retour n'est pas une chaîne mais un entier 3. Par défaut, toutes les entrées de l'URL sont interprétées comme une chaîne, mais la déclaration de type de Python ** analyse ** automatiquement (analyse) la requête et la convertit en un type entier.

Http://127.0.0.1 avec le code (ʻasync def read_item (item_id: int): return to ʻasync def read_item (item_id):) sans la partie qui déclare le type comme un essai Si vous accédez à: 8000 / items / 3, il sera affiché sous la forme de la chaîne «" 3 "».

{"item_id":"3"}

De plus, si vous accédez au paramètre Path déclaré comme type ʻint` avec une URL comme http://127.0.0.1:8000/items/foo, l'erreur Http suivante sera renvoyée.

{
    "detail": [
        {
            "loc": [
                "path",
                "item_id"
            ],
            "msg": "value is not a valid integer",
            "type": "type_error.integer"
        }
    ]
}

Ceci est dû au fait que la fonction ** Validation des données ** de l'API Fast fonctionnait car le paramètre donné (type str" foo ") n'a pas pu être converti en type ʻint. Similaire au type str ci-dessus, si vous donnez un type float` virgule minoritaire flottante (par exemple, http://127.0.0.1:8000/items/4.2), vous obtiendrez une erreur similaire.

De plus, il a été confirmé que si une valeur de type ʻintest donnée à un paramètre déclaré avec le typefloat`, il sera converti.

De ce qui précède, nous pouvons voir que l'API Fast effectue exactement la même validation que la déclaration de type Python.

Notez que le message d'erreur spécifie également le point où la validation n'a pas réussi (dans ce cas, la variable de paramètre " path " " item_id "). Il sera très utile pour développer et déboguer du code qui interagit avec l'API.

La prévention des bogues avec la vérification de type est l'une des fonctionnalités clés de l'API Fast.

Vérifiez le document

Après avoir vérifié le code ci-dessus, vérifiez http://127.0.0.1:8000/docs dans votre navigateur. La documentation API interactive est générée automatiquement, comme indiqué ci-dessous. (Cliquez sur l'onglet bleu intitulé "GET" sous "default" près du centre de l'écran pour le développer.)

qiita_2_2 2020-07-13 19-25-40.png

Il y a un «entier» sous le nom «id_item» de «Paramètres». Vous pouvez voir que la déclaration de type Python est également reflétée dans la documentation interactive (intégrée dans l'interface utilisateur de Swagger). Ce document vous donne de nombreuses informations sur l'API et peut être partagé avec les concepteurs et les personnes de premier plan.

En outre, ce document est basé sur le schéma généré par FastAPI, mais le schéma généré est compatible OpenAPI et divers outils compatibles ont été développés. Comme je l'ai confirmé dans Memo 1, il existe un document alternatif utilisant ReDoc dans Fast API. Essayez de visiter http://127.0.0.1:8000/redoc.

qiita_2_3 2020-07-13 19-40-37.png

C'est une impression personnelle approximative, mais Swagger UI est un document interactif qui peut réellement générer une requête et vérifier le résultat, tandis que ReDoc est un document statique et simple. Il y a peut-être plus de façons de l'utiliser et d'autres outils, mais je ne suis pas sûr du tout en raison du manque d'étude. Je suis désolé. Je voudrais le mettre à jour dès que j'apprends.

Pour l'instant, je pense que l'interface utilisateur de Swagger, qui peut générer des requêtes et voir les résultats, est pratique.

Pydantic Pour revenir au sujet principal, toutes les annotations de type utiles pour une telle génération de documents sont effectuées par une bibliothèque appelée ** Pydantic **. L'article suivant a été utile pour Pydantic. Introduction à Pydantic @ 0622okakyo

** Pydantic ** permet à de nombreux types de données tels que str, float, bool d'être annotés de la même manière.

Problème d'ordre dans l'opération de chemin

Lors de la création d'une opération de chemin, le chemin peut devenir fixe.

À titre d'exemple, supposons que vous ayez un chemin appelé / users / me pour que les utilisateurs obtiennent leurs propres données. À ce stade, supposons qu'il existe également un chemin appelé / users / {user_id} qui récupère des données sur un autre utilisateur en même temps en utilisant ʻuser_id`.

L'opération de chemin dans FastAPI est évaluée dans l'ordre. Par conséquent, le chemin «/ users / me» doit être déclaré avant le chemin «/ users / {id_utilisateur}» comme indiqué ci-dessous.

main


from fastapi import FastAPI

app = FastAPI()


@app.get("/users/me")
async def read_user_me():
    return {"user_id": "the current user"}


@app.get("/users/{user_id}")
async def read_user(user_id: str):
    return {"user_id": user_id}

Si le chemin / users / me est déclaré après le chemin / users / {user_id} ʻ comme indiqué ci-dessous, la valeur me sera transmise à l'API Fast en tant que paramètre ʻuser_id.

main_miss


from fastapi import FastAPI

app = FastAPI()


@app.get("/users/{user_id}")  # <- "/users/me"Si vous pouvez obtenir la méthode GET"me"Est passé
async def read_user(user_id: str):
    return {"user_id": user_id}


@app.get("/users/me")   # <- "/users/me"Cette fonction n'est pas appelée car elle a déjà été évaluée
async def read_user_me():
    return {"user_id": "the current user"}

Valeurs prédéfinies

Si vous avez un opérateur de chemin qui accepte les paramètres de chemin et que vous souhaitez prédéfinir des valeurs valides pour ces paramètres, nous vous recommandons d'utiliser le module standard Python ʻEnum`.

Commençons par créer la classe ʻEnum`.

Importez ʻEnum et créez une sous-classe qui hérite de str et de ʻEnum. En héritant de str, le document API peut savoir à l'avance que la valeur à définir est un type chaîne, et elle sera rendue correctement.

Ensuite, créez un attribut de classe avec une valeur fixe qui est une valeur valide.

main


from enum import Enum   # <-Importer l'énumération

from fastapi import FastAPI


class ModelName(str, Enum): # <-Héritage de classe
    alexnet = "alexnet"     # <-Attributs de classe(Valeur fixe)Création
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()

Nous définirons les paramètres du chemin en utilisant la classe définie.

Utilisez la classe de type d'énumération (ModelName) que vous avez créée pour créer un paramètre de chemin avec des annotations de type.

main


from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/model/{model_name}")
async def get_model(model_name: ModelName): # <-Paramètre de chemin avec annotation de type
    if model_name == ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

Consultez la documentation (http://127.0.0.1:8000/docs). Les valeurs pouvant être utilisées dans les paramètres de chemin sont prédéfinies, de sorte que le document affiche et sélectionne ces valeurs de manière interactive.

  1. Cliquez sur l'onglet «GET», puis sur «Try it out» en haut de l'onglet.
  2. model_name devient sélectionnable.
  3. Vous pouvez soumettre une demande en sélectionnant une valeur et en cliquant sur ʻExecte`.

(La capture d'écran ci-dessous est tirée du Document officiel. Je n'ai pas pu obtenir une bonne capture d'écran de l'écran de sélection dans mon environnement. )

qiita_2_4 2020-07-13 23-00-54.png

À propos du type d'énumération

main


from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/model/{model_name}")
async def get_model(model_name: ModelName):
    if model_name == ModelName.alexnet: # <- ①
        return {"model_name": model_name, "message": "Deep Learning FTW!"}  # <- ③

    if model_name.value == "lenet": # <- ②
        return {"model_name": model_name, "message": "LeCNN all the images"}    # <- ③ 

    return {"model_name": model_name, "message": "Have some residuals"}     # <- ③ 

Pour plus de détails, reportez-vous à Python official document enum --- Support for enumeration types.

Ici, comparez avec le membre de type d'énumération ModelName créé (①), obtenez la valeur du type d'énumération par .value et comparez-la (②), et utilisez le membre de type d'énumération comme valeur de retour (③). Je suis. Même dans le cas d'un corps JSON imbriqué tel que le type de dictionnaire dict, il est possible de renvoyer la valeur de Path Operater en tant que membre enum. Dans ce cas, il est converti en la valeur correspondante (str ci-dessus) et renvoyé au client.

Si vous l'obtenez dans un document interactif ou dans un navigateur, vous obtiendrez la valeur de retour JSON comme celle-ci, par exemple.

{
  "model_name": "alexnet",
  "message": "Deep Learning FTW!"
}

Paramètres du chemin, y compris le chemin

Par exemple, supposons que vous ayez une opération de chemin avec le chemin / files / {file_path}. Mais si file_path lui-même a un chemin comme valeur (comme file_path = home / johndoe / myfile.txt) L'URL ressemblerait à ceci:

http://127.0.0.1:8000/files/home/johndoe/myfile.txt

OpenAPI ne prend pas en charge la déclaration des paramètres de chemin qui ont le chemin ci-dessus comme valeur car cela conduit à la définition de tests et à l'occurrence de scénarios difficiles.

Cependant, en utilisant les outils internes de Starlette, l'API Fast vous permet de déclarer de tels paramètres de chemin. (Cependant, il n'est pas possible de confirmer que le paramètre de chemin lui-même contient le chemin dans le document API qui est conforme à OpenAPI. * Le document lui-même fonctionne)

Déclarez les paramètres de chemin, y compris les chemins, à l'aide d'une URL similaire à la suivante:

/files/{file_path:path}

Dans ce qui précède, le paramètre path est file_path, et la partie: path spécifie que le paramètre path contient le chemin.

L'exemple d'utilisation est le suivant.

main


from fastapi import FastAPI

app = FastAPI()


@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
    return {"file_path": file_path}

À la fin

La prochaine fois, ce sera un paramètre de requête.

Recommended Posts

Paramètres de chemin du mémo du didacticiel FastAPI Partie 2
Paramètres de requête FastAPI Tutorial Memo Part 3
Mémo du didacticiel FastAPI, partie 1
mémo du didacticiel django
Mémo du didacticiel Python OpenCV
Mémo du didacticiel FastAPI, partie 1
notes du didacticiel sur la pyramide (single_file_tasks)
Paramètres de requête FastAPI Tutorial Memo Part 3
Paramètres de chemin du mémo du didacticiel FastAPI Partie 2
mémo du didacticiel django
[Pour mémoire] Linux Partie 2
Liste de rédaction de mémo partie 2
Mémo de base Python - Partie 2
Mémo du didacticiel Python OpenCV
Mémo de base Python - Partie 1
Mémo d'enquête Android HCE (partie 2)
Mémo 1 du manuel standard Linux, partie 2
Mémo de grammaire de base Python (1)
Mémo du manuel standard Linux, partie 6