Paramètres de requête et validations de chaînes de caractères¶
🌐 Translation by AI and humans
This translation was made by AI guided by humans. 🤝
It could have mistakes of misunderstanding the original meaning, or looking unnatural, etc. 🤖
You can improve this translation by helping us guide the AI LLM better.
FastAPI vous permet de déclarer des informations et des validations supplémentaires pour vos paramètres.
Prenons cette application comme exemple :
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
🤓 Other versions and variants
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
Le paramètre de requête q est de type str | None, cela signifie qu’il est de type str mais peut aussi être None, et en effet, la valeur par défaut est None, donc FastAPI saura qu’il n’est pas requis.
Remarque
FastAPI saura que la valeur de q n’est pas requise grâce à la valeur par défaut = None.
Avoir str | None permettra à votre éditeur de vous offrir un meilleur support et de détecter les erreurs.
Validation additionnelle¶
Nous allons imposer que, même si q est optionnel, dès qu’il est fourni, sa longueur n’excède pas 50 caractères.
Importer Query et Annotated¶
Pour ce faire, importez d’abord :
QuerydepuisfastapiAnnotateddepuistyping
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
🤓 Other versions and variants
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(max_length=50)] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
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
Tip
Prefer to use the Annotated version if possible.
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
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.
Assurez-vous de mettre à niveau la version de FastAPI vers au moins 0.95.1 avant d’utiliser Annotated.
Utiliser Annotated dans le type pour le paramètre q¶
Vous vous souvenez que je vous ai dit plus tôt que Annotated peut être utilisé pour ajouter des métadonnées à vos paramètres dans l’Introduction aux types Python ?
C’est le moment de l’utiliser avec FastAPI. 🚀
Nous avions cette annotation de type :
q: str | None = None
q: Union[str, None] = None
Ce que nous allons faire, c’est l’englober avec Annotated, de sorte que cela devienne :
q: Annotated[str | None] = None
q: Annotated[Union[str, None]] = None
Les deux versions signifient la même chose, q est un paramètre qui peut être une str ou None, et par défaut, c’est None.
Passons maintenant aux choses amusantes. 🎉
Ajouter Query à Annotated dans le paramètre q¶
Maintenant que nous avons cet Annotated dans lequel nous pouvons mettre plus d’informations (dans ce cas une validation supplémentaire), ajoutez Query à l’intérieur de Annotated, et définissez le paramètre max_length à 50 :
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
🤓 Other versions and variants
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(max_length=50)] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
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
Tip
Prefer to use the Annotated version if possible.
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
Remarquez que la valeur par défaut est toujours None, donc le paramètre est toujours optionnel.
Mais maintenant, avec Query(max_length=50) à l’intérieur de Annotated, nous indiquons à FastAPI que nous voulons une validation supplémentaire pour cette valeur, nous voulons qu’elle ait au maximum 50 caractères. 😎
Astuce
Ici nous utilisons Query() parce qu’il s’agit d’un paramètre de requête. Plus tard nous verrons d’autres comme Path(), Body(), Header() et Cookie(), qui acceptent également les mêmes arguments que Query().
FastAPI va maintenant :
- Valider les données en s’assurant que la longueur maximale est de 50 caractères
- Afficher une erreur claire au client quand les données ne sont pas valides
- Documenter le paramètre dans la chemin d'accès du schéma OpenAPI (il apparaîtra donc dans l’interface de documentation automatique)
Alternative (ancienne) : Query comme valeur par défaut¶
Les versions précédentes de FastAPI (avant 0.95.0) exigeaient d’utiliser Query comme valeur par défaut de votre paramètre, au lieu de le mettre dans Annotated. Il y a de fortes chances que vous voyiez du code qui l’utilise encore, je vais donc vous l’expliquer.
Astuce
Pour du nouveau code et dès que possible, utilisez Annotated comme expliqué ci-dessus. Il y a de multiples avantages (expliqués ci-dessous) et aucun inconvénient. 🍰
Voici comment vous utiliseriez Query() comme valeur par défaut du paramètre de votre fonction, en définissant le paramètre 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
🤓 Other versions and variants
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 Annotated, Union
from fastapi import FastAPI, Query
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
Tip
Prefer to use the Annotated version if possible.
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
Comme, dans ce cas (sans utiliser Annotated), nous devons remplacer la valeur par défaut None dans la fonction par Query(), nous devons maintenant définir la valeur par défaut avec le paramètre Query(default=None), cela sert le même objectif de définir cette valeur par défaut (au moins pour FastAPI).
Donc :
q: str | None = Query(default=None)
... rend le paramètre optionnel, avec une valeur par défaut de None, comme :
q: str | None = None
Mais la version avec Query le déclare explicitement comme étant un paramètre de requête.
Ensuite, nous pouvons passer plus de paramètres à Query. Dans ce cas, le paramètre max_length qui s’applique aux chaînes de caractères :
q: str | None = Query(default=None, max_length=50)
Cela validera les données, affichera une erreur claire lorsque les données ne sont pas valides et documentera le paramètre dans la chemin d'accès du schéma OpenAPI.
Query comme valeur par défaut ou dans Annotated¶
Gardez à l’esprit qu’en utilisant Query à l’intérieur de Annotated, vous ne pouvez pas utiliser le paramètre default de Query.
Utilisez à la place la valeur par défaut réelle du paramètre de fonction. Sinon, ce serait incohérent.
Par exemple, ceci n’est pas autorisé :
q: Annotated[str, Query(default="rick")] = "morty"
... parce qu’il n’est pas clair si la valeur par défaut doit être « rick » ou « morty ».
Donc, vous utiliseriez (de préférence) :
q: Annotated[str, Query()] = "rick"
... ou dans des bases de code plus anciennes, vous trouverez :
q: str = Query(default="rick")
Avantages de Annotated¶
L’utilisation de Annotated est recommandée plutôt que la valeur par défaut dans les paramètres de fonction, c’est mieux pour plusieurs raisons. 🤓
La valeur par défaut du paramètre de fonction est la vraie valeur par défaut, c’est plus intuitif en Python en général. 😌
Vous pouvez appeler cette même fonction dans d’autres endroits sans FastAPI, et elle fonctionnera comme prévu. S’il y a un paramètre requis (sans valeur par défaut), votre éditeur vous le signalera avec une erreur, Python se plaindra aussi si vous l’exécutez sans passer le paramètre requis.
Quand vous n’utilisez pas Annotated et utilisez à la place l’ancienne méthode avec la valeur par défaut, si vous appelez cette fonction sans FastAPI dans d’autres endroits, vous devez penser à passer les arguments à la fonction pour qu’elle fonctionne correctement, sinon les valeurs seront différentes de ce que vous attendez (par ex. QueryInfo ou quelque chose de similaire au lieu d’une str). Et votre éditeur ne se plaindra pas, et Python ne se plaindra pas en exécutant cette fonction, seulement quand les opérations internes échoueront.
Comme Annotated peut avoir plus d’une annotation de métadonnées, vous pouvez maintenant même utiliser la même fonction avec d’autres outils, comme Typer. 🚀
Ajouter plus de validations¶
Vous pouvez également ajouter un paramètre 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
🤓 Other versions and variants
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
Tip
Prefer to use the Annotated version if possible.
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
Tip
Prefer to use the Annotated version if possible.
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
Ajouter des expressions régulières¶
Vous pouvez définir un pattern d’expression régulière auquel le paramètre doit correspondre :
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
🤓 Other versions and variants
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
Tip
Prefer to use the Annotated version if possible.
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
Tip
Prefer to use the Annotated version if possible.
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
Ce pattern d’expression régulière spécifique vérifie que la valeur reçue pour le paramètre :
^: commence avec les caractères qui suivent, n’a pas de caractères avant.fixedquery: a exactement la valeurfixedquery.$: se termine là, n’a pas d’autres caractères aprèsfixedquery.
Si vous vous sentez perdu avec toutes ces idées d’« expression régulière », pas d’inquiétude. C’est un sujet difficile pour beaucoup. Vous pouvez déjà faire beaucoup de choses sans avoir besoin d’expressions régulières.
Désormais, vous savez que, lorsque vous en aurez besoin, vous pourrez les utiliser dans FastAPI.
Valeurs par défaut¶
Vous pouvez, bien sûr, utiliser des valeurs par défaut autres que None.
Disons que vous voulez déclarer le paramètre de requête q avec un min_length de 3, et avec une valeur par défaut de « 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
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
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
Remarque
Avoir une valeur par défaut de n’importe quel type, y compris None, rend le paramètre optionnel (non requis).
Paramètres requis¶
Quand nous n’avons pas besoin de déclarer plus de validations ou de métadonnées, nous pouvons rendre le paramètre de requête q requis en n’indiquant simplement pas de valeur par défaut, comme :
q: str
au lieu de :
q: str | None = None
Mais maintenant nous le déclarons avec Query, par exemple ainsi :
q: Annotated[str | None, Query(min_length=3)] = None
Donc, lorsque vous avez besoin de déclarer une valeur comme requise tout en utilisant Query, vous pouvez simplement ne pas déclarer de valeur par défaut :
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
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
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
Requis, peut valoir None¶
Vous pouvez déclarer qu’un paramètre accepte None, mais qu’il est tout de même requis. Cela obligerait les clients à envoyer une valeur, même si la valeur est None.
Pour ce faire, vous pouvez déclarer que None est un type valide tout en ne déclarant pas de valeur par défaut :
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
🤓 Other versions and variants
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
Tip
Prefer to use the Annotated version if possible.
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
Tip
Prefer to use the Annotated version if possible.
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
Liste de paramètres de requête / valeurs multiples¶
Quand vous définissez un paramètre de requête explicitement avec Query, vous pouvez aussi déclarer qu’il reçoit une liste de valeurs, autrement dit, qu’il reçoit des valeurs multiples.
Par exemple, pour déclarer un paramètre de requête q qui peut apparaître plusieurs fois dans l’URL, vous pouvez écrire :
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
🤓 Other versions and variants
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
Tip
Prefer to use the Annotated version if possible.
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
Tip
Prefer to use the Annotated version if possible.
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
Ensuite, avec une URL comme :
http://localhost:8000/items/?q=foo&q=bar
vous recevriez les valeurs des multiples paramètres de requête q (foo et bar) dans une list Python à l’intérieur de votre fonction de chemin d'accès, dans le paramètre de fonction q.
Donc, la réponse pour cette URL serait :
{
"q": [
"foo",
"bar"
]
}
Astuce
Pour déclarer un paramètre de requête avec un type list, comme dans l’exemple ci-dessus, vous devez explicitement utiliser Query, sinon il serait interprété comme faisant partie du corps de la requête.
L’interface de documentation interactive de l’API sera mise à jour en conséquence, pour autoriser plusieurs valeurs :

Liste de paramètres de requête / valeurs multiples avec valeurs par défaut¶
Vous pouvez également définir une list de valeurs par défaut si aucune n’est fournie :
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
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
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
Si vous allez à :
http://localhost:8000/items/
la valeur par défaut de q sera : ["foo", "bar"] et votre réponse sera :
{
"q": [
"foo",
"bar"
]
}
Utiliser simplement list¶
Vous pouvez aussi utiliser list directement au lieu de list[str] :
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
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
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
Remarque
Gardez à l’esprit que dans ce cas, FastAPI ne vérifiera pas le contenu de la liste.
Par exemple, list[int] vérifierait (et documenterait) que le contenu de la liste est composé d’entiers. Mais un simple list ne le ferait pas.
Déclarer plus de métadonnées¶
Vous pouvez ajouter plus d’informations à propos du paramètre.
Ces informations seront incluses dans l’OpenAPI généré et utilisées par les interfaces de documentation et les outils externes.
Remarque
Gardez à l’esprit que différents outils peuvent avoir des niveaux de prise en charge d’OpenAPI différents.
Certains d’entre eux pourraient ne pas encore afficher toutes les informations supplémentaires déclarées, bien que, dans la plupart des cas, la fonctionnalité manquante soit déjà prévue au développement.
Vous pouvez ajouter un 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
🤓 Other versions and variants
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
Tip
Prefer to use the Annotated version if possible.
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
Tip
Prefer to use the Annotated version if possible.
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
Et une 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
🤓 Other versions and variants
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
Tip
Prefer to use the Annotated version if possible.
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
Tip
Prefer to use the Annotated version if possible.
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
Paramètres avec alias¶
Imaginez que vous vouliez que le paramètre soit item-query.
Comme dans :
http://127.0.0.1:8000/items/?item-query=foobaritems
Mais item-query n’est pas un nom de variable Python valide.
Le plus proche serait item_query.
Mais vous avez quand même besoin que ce soit exactement item-query ...
Vous pouvez alors déclarer un alias, et cet alias sera utilisé pour trouver la valeur du paramètre :
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
🤓 Other versions and variants
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
Tip
Prefer to use the Annotated version if possible.
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
Tip
Prefer to use the Annotated version if possible.
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
Déprécier des paramètres¶
Disons que vous n’aimez plus ce paramètre.
Vous devez le laisser là quelque temps car des clients l’utilisent, mais vous voulez que les documents l’affichent clairement comme déprécié.
Passez alors le paramètre deprecated=True à Query :
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
🤓 Other versions and variants
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
Tip
Prefer to use the Annotated version if possible.
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
Tip
Prefer to use the Annotated version if possible.
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
Les documents l’afficheront ainsi :

Exclure des paramètres d’OpenAPI¶
Pour exclure un paramètre de requête du schéma OpenAPI généré (et donc, des systèmes de documentation automatiques), définissez le paramètre include_in_schema de Query à 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"}
🤓 Other versions and variants
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"}
Tip
Prefer to use the Annotated version if possible.
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"}
Tip
Prefer to use the Annotated version if possible.
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"}
Validation personnalisée¶
Il peut y avoir des cas où vous devez faire une validation personnalisée qui ne peut pas être réalisée avec les paramètres montrés ci-dessus.
Dans ces cas, vous pouvez utiliser une fonction de validation personnalisée qui est appliquée après la validation normale (par ex. après avoir validé que la valeur est une str).
Vous pouvez y parvenir en utilisant AfterValidator de Pydantic à l’intérieur de Annotated.
Astuce
Pydantic a aussi BeforeValidator et d’autres. 🤓
Par exemple, ce validateur personnalisé vérifie que l’ID d’item commence par isbn- pour un numéro de livre ISBN ou par imdb- pour un ID d’URL de film 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}
🤓 Other versions and variants
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}
Info
C’est disponible avec Pydantic version 2 ou supérieure. 😎
Astuce
Si vous devez faire un type de validation qui nécessite de communiquer avec un composant externe, comme une base de données ou une autre API, vous devez plutôt utiliser les Dépendances de FastAPI, vous en apprendrez davantage plus tard.
Ces validateurs personnalisés sont destinés aux éléments qui peuvent être vérifiés uniquement avec les mêmes données fournies dans la requête.
Comprendre ce code¶
Le point important est simplement d’utiliser AfterValidator avec une fonction à l’intérieur de Annotated. N’hésitez pas à passer cette partie. 🤸
Mais si vous êtes curieux de cet exemple de code spécifique et que vous êtes toujours partant, voici quelques détails supplémentaires.
Chaîne avec value.startswith()¶
Avez-vous remarqué ? Une chaîne utilisant value.startswith() peut prendre un tuple, et elle vérifiera chaque valeur du tuple :
# 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 👇
👀 Full file preview
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}
🤓 Other versions and variants
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}
Un élément aléatoire¶
Avec data.items() nous obtenons un objet itérable avec des tuples contenant la clé et la valeur pour chaque élément du dictionnaire.
Nous convertissons cet objet itérable en une list propre avec list(data.items()).
Ensuite, avec random.choice() nous pouvons obtenir une valeur aléatoire depuis la liste, nous obtenons donc un tuple (id, name). Ce sera quelque chose comme ("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy").
Puis nous affectons ces deux valeurs du tuple aux variables id et name.
Ainsi, si l’utilisateur n’a pas fourni d’ID d’item, il recevra quand même une suggestion aléatoire.
... nous faisons tout cela en une seule ligne simple. 🤯 Vous n’adorez pas 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}
👀 Full file preview
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}
🤓 Other versions and variants
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}
Récapitulatif¶
Vous pouvez déclarer des validations et des métadonnées supplémentaires pour vos paramètres.
Validations et métadonnées génériques :
aliastitledescriptiondeprecated
Validations spécifiques aux chaînes :
min_lengthmax_lengthpattern
Validations personnalisées avec AfterValidator.
Dans ces exemples, vous avez vu comment déclarer des validations pour des valeurs str.
Voyez les prochains chapitres pour apprendre à déclarer des validations pour d’autres types, comme les nombres.