33 changed files with 2570 additions and 268 deletions
After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 92 KiB |
@ -1,58 +1,109 @@ |
|||
# Schema Extra - Example |
|||
# Declare Request Example Data |
|||
|
|||
You can define extra information to go in JSON Schema. |
|||
You can declare examples of the data your app can receive. |
|||
|
|||
A common use case is to add an `example` that will be shown in the docs. |
|||
|
|||
There are several ways you can declare extra JSON Schema information. |
|||
Here are several ways to do it. |
|||
|
|||
## Pydantic `schema_extra` |
|||
|
|||
You can declare an example for a Pydantic model using `Config` and `schema_extra`, as described in <a href="https://pydantic-docs.helpmanual.io/usage/schema/#schema-customization" class="external-link" target="_blank">Pydantic's docs: Schema customization</a>: |
|||
You can declare an `example` for a Pydantic model using `Config` and `schema_extra`, as described in <a href="https://pydantic-docs.helpmanual.io/usage/schema/#schema-customization" class="external-link" target="_blank">Pydantic's docs: Schema customization</a>: |
|||
|
|||
```Python hl_lines="15-23" |
|||
{!../../../docs_src/schema_extra_example/tutorial001.py!} |
|||
``` |
|||
|
|||
That extra info will be added as-is to the output JSON Schema. |
|||
That extra info will be added as-is to the output **JSON Schema** for that model, and it will be used in the API docs. |
|||
|
|||
!!! tip |
|||
You could use the same technique to extend the JSON Schema and add your own custom extra info. |
|||
|
|||
For example you could use it to add metadata for a frontend user interface, etc. |
|||
|
|||
## `Field` additional arguments |
|||
|
|||
In `Field`, `Path`, `Query`, `Body` and others you'll see later, you can also declare extra info for the JSON Schema by passing any other arbitrary arguments to the function, for example, to add an `example`: |
|||
When using `Field()` with Pydantic models, you can also declare extra info for the **JSON Schema** by passing any other arbitrary arguments to the function. |
|||
|
|||
You can use this to add `example` for each field: |
|||
|
|||
```Python hl_lines="4 10-13" |
|||
{!../../../docs_src/schema_extra_example/tutorial002.py!} |
|||
``` |
|||
|
|||
!!! warning |
|||
Keep in mind that those extra arguments passed won't add any validation, only annotation, for documentation purposes. |
|||
Keep in mind that those extra arguments passed won't add any validation, only extra information, for documentation purposes. |
|||
|
|||
## `example` and `examples` in OpenAPI |
|||
|
|||
When using any of: |
|||
|
|||
## `Body` additional arguments |
|||
* `Path()` |
|||
* `Query()` |
|||
* `Header()` |
|||
* `Cookie()` |
|||
* `Body()` |
|||
* `Form()` |
|||
* `File()` |
|||
|
|||
The same way you can pass extra info to `Field`, you can do the same with `Path`, `Query`, `Body`, etc. |
|||
you can also declare a data `example` or a group of `examples` with additional information that will be added to **OpenAPI**. |
|||
|
|||
For example, you can pass an `example` for a body request to `Body`: |
|||
### `Body` with `example` |
|||
|
|||
Here we pass an `example` of the data expected in `Body()`: |
|||
|
|||
```Python hl_lines="21-26" |
|||
{!../../../docs_src/schema_extra_example/tutorial003.py!} |
|||
``` |
|||
|
|||
## Example in the docs UI |
|||
### Example in the docs UI |
|||
|
|||
With any of the methods above it would look like this in the `/docs`: |
|||
|
|||
<img src="/img/tutorial/body-fields/image01.png"> |
|||
|
|||
### `Body` with multiple `examples` |
|||
|
|||
Alternatively to the single `example`, you can pass `examples` using a `dict` with **multiple examples**, each with extra information that will be added to **OpenAPI** too. |
|||
|
|||
The keys of the `dict` identify each example, and each value is another `dict`. |
|||
|
|||
Each specific example `dict` in the `examples` can contain: |
|||
|
|||
* `summary`: Short description for the example. |
|||
* `description`: A long description that can contain Markdown text. |
|||
* `value`: This is the actual example shown, e.g. a `dict`. |
|||
* `externalValue`: alternative to `value`, a URL pointing to the example. Although this might not be supported by as many tools as `value`. |
|||
|
|||
```Python hl_lines="22-48" |
|||
{!../../../docs_src/schema_extra_example/tutorial004.py!} |
|||
``` |
|||
|
|||
### Examples in the docs UI |
|||
|
|||
With `examples` added to `Body()` the `/docs` would look like: |
|||
|
|||
<img src="/img/tutorial/body-fields/image02.png"> |
|||
|
|||
## Technical Details |
|||
|
|||
About `example` vs `examples`... |
|||
!!! warning |
|||
These are very technical details about the standards **JSON Schema** and **OpenAPI**. |
|||
|
|||
If the ideas above already work for you, that might me enough, and you probably don't need these details, feel free to skip them. |
|||
|
|||
When you add an example inside of a Pydantic model, using `schema_extra` or `Field(example="something")` that example is added to the **JSON Schema** for that Pydantic model. |
|||
|
|||
And that **JSON Schema** of the Pydantic model is included in the **OpenAPI** of your API, and then it's used in the docs UI. |
|||
|
|||
**JSON Schema** doesn't really have a field `example` in the standards. Recent versions of JSON Schema define a field <a href="https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5" class="external-link" target="_blank">`examples`</a>, but OpenAPI 3.0.3 is based on an older version of JSON Schema that didn't have `examples`. |
|||
|
|||
So, OpenAPI 3.0.3 defined its own <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#fixed-fields-20" class="external-link" target="_blank">`example`</a> for the modified version of **JSON Schema** it uses, for the same purpose (but it's a single `example`, not `examples`), and that's what is used by the API docs UI (using Swagger UI). |
|||
|
|||
JSON Schema defines a field <a href="https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5" class="external-link" target="_blank">`examples`</a> in the most recent versions, but OpenAPI is based on an older version of JSON Schema that didn't have `examples`. |
|||
So, although `example` is not part of JSON Schema, it is part of OpenAPI's custom version of JSON Schema, and that's what will be used by the docs UI. |
|||
|
|||
So, OpenAPI defined its own <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#fixed-fields-20" class="external-link" target="_blank">`example`</a> for the same purpose (as `example`, not `examples`), and that's what is used by the docs UI (using Swagger UI). |
|||
But when you use `example` or `examples` with any of the other utilities (`Query()`, `Body()`, etc.) those examples are not added to the JSON Schema that describes that data (not even to OpenAPI's own version of JSON Schema), they are added directly to the *path operation* declaration in OpenAPI (outside the parts of OpenAPI that use JSON Schema). |
|||
|
|||
So, although `example` is not part of JSON Schema, it is part of OpenAPI, and that's what will be used by the docs UI. |
|||
For `Path()`, `Query()`, `Header()`, and `Cookie()`, the `example` or `examples` are added to the <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#parameter-object" class="external-link" target="_blank">OpenAPI definition, to the `Parameter Object` (in the specification)</a>. |
|||
|
|||
## Other info |
|||
And for `Body()`, `File()`, and `Form()`, the `example` or `examples` are equivalently added to the <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#mediaTypeObject" class="external-link" target="_blank">OpenAPI definition, to the `Request Body Object`, in the field `content`, on the `Media Type Object` (in the specification)</a>. |
|||
|
|||
The same way, you could add your own custom extra info that would be added to the JSON Schema for each model, for example to customize a frontend user interface, etc. |
|||
On the other hand, there's a newer version of OpenAPI: **3.1.0**, recently released. It is based on the latest JSON Schema and most of the modifications from OpenAPI's custom version of JSON Schema are removed, in exchange of the features from the recent versions of JSON Schema, so all these small differences are reduced. Nevertheless, Swagger UI currently doesn't support OpenAPI 3.1.0, so, for now, it's better to continue using the ideas above. |
|||
|
@ -1,13 +1,26 @@ |
|||
{% extends "base.html" %} |
|||
|
|||
{% block announce %} |
|||
<a class="announce" href="https://fastapi.tiangolo.com/newsletter/"> |
|||
<div class="announce-wrapper"> |
|||
<div id="announce-left"> |
|||
<div class="item"> |
|||
<a class="announce-link" href="https://fastapi.tiangolo.com/newsletter/"> |
|||
<span class="twemoji"> |
|||
{% include ".icons/material/email.svg" %} |
|||
</span> Subscribe to the <strong>FastAPI and friends</strong> newsletter 🎉 |
|||
</a> |
|||
<!-- <a class="announce" href="https://tripetto.app/run/RXZ6OLDBXX?s=dc" target="_blank"> |
|||
<span class="twemoji"> |
|||
</span>Fill the first-ever <strong>FastAPI user survey</strong> for a chance to win official <strong>FastAPI and Typer stickers</strong> 🎁 |
|||
</a> --> |
|||
</div> |
|||
<div class="item"> |
|||
<iframe style="display: inline-block; vertical-align: middle; border: none; margin-right: 0.5rem;" src="https://github.com/sponsors/tiangolo/button" title="Sponsor tiangolo" height="35" width="116" style="border: 0;"></iframe> <a class="announce-link" target="_blank" href="https://github.com/sponsors/tiangolo">You can now sponsor <strong>FastAPI</strong> 🍰</a> |
|||
</div> |
|||
</div> |
|||
<div id="announce-right" style="position: relative;"> |
|||
<div class="item"> |
|||
<a title="The launchpad for all your (team's) ideas" style="display: block; position: relative;" href="https://www.deta.sh/?ref=fastapi"> |
|||
<span class="sponsor-badge">sponsor</span> |
|||
<img style="display: block;" src="/img/sponsors/deta-banner.svg" /> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{% endblock %} |
|||
|
@ -0,0 +1,135 @@ |
|||
# La communauté FastAPI |
|||
|
|||
FastAPI a une communauté extraordinaire qui accueille des personnes de tous horizons. |
|||
|
|||
## Créateur - Mainteneur |
|||
|
|||
Salut! 👋 |
|||
|
|||
C'est moi : |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.maintainers %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Réponses: {{ user.answers }}</div><div class="count">Pull Requests: {{ user.prs }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
Je suis le créateur et le responsable de **FastAPI**. Vous pouvez en lire plus à ce sujet dans [Aide FastAPI - Obtenir de l'aide - Se rapprocher de l'auteur](help-fastapi.md#connect-with-the-author){.internal-link target=_blank}. |
|||
|
|||
...Mais ici, je veux vous montrer la communauté. |
|||
|
|||
--- |
|||
|
|||
**FastAPI** reçoit beaucoup de soutien de la part de la communauté. Et je tiens à souligner leurs contributions. |
|||
|
|||
Ce sont ces personnes qui : |
|||
|
|||
* [Aident les autres à résoudre des problèmes (questions) dans GitHub](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank}. |
|||
* [Créent des Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}. |
|||
* Review les Pull Requests, [particulièrement important pour les traductions](contributing.md#translations){.internal-link target=_blank}. |
|||
|
|||
Une salve d'applaudissements pour eux. 👏 🙇 |
|||
|
|||
## Utilisateurs les plus actifs le mois dernier |
|||
|
|||
Ce sont les utilisateurs qui ont [aidé le plus les autres avec des problèmes (questions) dans GitHub](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank} au cours du dernier mois. ☕ |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.last_month_active %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Questions répondues: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## Experts |
|||
|
|||
Voici les **Experts FastAPI**. 🤓 |
|||
|
|||
Ce sont les utilisateurs qui ont [aidé le plus les autres avec des problèmes (questions) dans GitHub](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank} depuis *toujours*. |
|||
|
|||
Ils ont prouvé qu'ils étaient des experts en aidant beaucoup d'autres personnes. ✨ |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.experts %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Questions répondues: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## Principaux contributeurs |
|||
|
|||
Ces utilisateurs sont les **Principaux contributeurs**. 👷 |
|||
|
|||
Ces utilisateurs ont [créé le plus grand nombre de demandes Pull Request](help-fastapi.md#create-a-pull-request){.internal-link target=_blank} qui ont été *merged*. |
|||
|
|||
Ils ont contribué au code source, à la documentation, aux traductions, etc. 📦 |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.top_contributors %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Pull Requests: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
Il existe de nombreux autres contributeurs (plus d'une centaine), vous pouvez les voir tous dans la <a href="https://github.com/tiangolo/fastapi/graphs/contributors" class="external-link" target="_blank">Page des contributeurs de FastAPI GitHub</a>. 👷 |
|||
|
|||
## Principaux Reviewers |
|||
|
|||
Ces utilisateurs sont les **Principaux Reviewers**. 🕵️ |
|||
|
|||
### Reviewers des traductions |
|||
|
|||
Je ne parle que quelques langues (et pas très bien 😅). Ainsi, les reviewers sont ceux qui ont le [**pouvoir d'approuver les traductions**](contributing.md#translations){.internal-link target=_blank} de la documentation. Sans eux, il n'y aurait pas de documentation dans plusieurs autres langues. |
|||
|
|||
--- |
|||
|
|||
Les **Principaux Reviewers** 🕵️ ont examiné le plus grand nombre de demandes Pull Request des autres, assurant la qualité du code, de la documentation, et surtout, des **traductions**. |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.top_reviewers %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Reviews: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## Sponsors |
|||
|
|||
Ce sont les **Sponsors**. 😎 |
|||
|
|||
Ils soutiennent mon travail avec **FastAPI** (et d'autres) avec <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub Sponsors</a>. |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.sponsors %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## À propos des données - détails techniques |
|||
|
|||
L'intention de cette page est de souligner l'effort de la communauté pour aider les autres. |
|||
|
|||
Notamment en incluant des efforts qui sont normalement moins visibles, et, dans de nombreux cas, plus difficile, comme aider d'autres personnes à résoudre des problèmes et examiner les Pull Requests de traduction. |
|||
|
|||
Les données sont calculées chaque mois, vous pouvez lire le <a href="https://github.com/tiangolo/fastapi/blob/master/.github/actions/people/app/main.py" class="external-link" target="_blank">code source ici</a>. |
|||
|
|||
Je me réserve également le droit de mettre à jour l'algorithme, les sections, les seuils, etc. (juste au cas où 🤷). |
@ -0,0 +1,201 @@ |
|||
# Fonctionnalités |
|||
|
|||
## Fonctionnalités de FastAPI |
|||
|
|||
**FastAPI** vous offre ceci: |
|||
|
|||
### Basé sur des standards ouverts |
|||
|
|||
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> pour la création d'API, incluant la déclaration de <abbr title="en français: routes. Aussi connu sous le nom anglais endpoints ou routes">path</abbr> <abbr title="Aussi connu sous le nom de méthodes HTTP. À savoir POST, GET, PUT, DELETE">operations</abbr>, paramètres, corps de requêtes, sécurité, etc. |
|||
* Documentation automatique des modèles de données avec <a href="http://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (comme OpenAPI est aussi basée sur JSON Schema). |
|||
* Conçue avec ces standards après une analyse méticuleuse. Plutôt qu'en rajoutant des surcouches après coup. |
|||
* Cela permet d'utiliser de la **génération automatique de code client** dans beaucoup de langages. |
|||
|
|||
### Documentation automatique |
|||
|
|||
Documentation d'API interactive et interface web d'exploration. Comme le framework est basé sur OpenAPI, de nombreuses options sont disponibles. Deux d'entre-elles sont incluses par défaut. |
|||
|
|||
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank"><strong>Swagger UI</strong></a>, propose une documentation interactive. Vous permet de directement tester l'API depuis votre navigateur. |
|||
|
|||
 |
|||
|
|||
* Une autre documentation d'API est fournie par <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank"><strong>ReDoc</strong></a>. |
|||
|
|||
 |
|||
|
|||
### Faite en python moderne |
|||
|
|||
Tout est basé sur la déclaration de type standard de **Python 3.6** (grâce à Pydantic). Pas de nouvelles syntaxes à apprendre. Juste du Python standard et moderne. |
|||
|
|||
Si vous souhaitez un rappel de 2 minutes sur l'utilisation des types en Python (même si vous ne comptez pas utiliser FastAPI), jetez un oeil au tutoriel suivant: [Python Types](python-types.md){.internal-link target=_blank}. |
|||
|
|||
Vous écrivez du python standard avec des annotations de types: |
|||
|
|||
```Python |
|||
from typing import List, Dict |
|||
from datetime import date |
|||
|
|||
from pydantic import BaseModel |
|||
|
|||
# Déclare une variable comme étant une str |
|||
# et profitez de l'aide de votre IDE dans cette fonction |
|||
def main(user_id: str): |
|||
return user_id |
|||
|
|||
|
|||
# Un modèle Pydantic |
|||
class User(BaseModel): |
|||
id: int |
|||
name: str |
|||
joined: date |
|||
``` |
|||
Qui peuvent ensuite être utilisés comme cela: |
|||
|
|||
```Python |
|||
my_user: User = User(id=3, name="John Doe", joined="2018-07-19") |
|||
|
|||
second_user_data = { |
|||
"id": 4, |
|||
"name": "Mary", |
|||
"joined": "2018-11-30", |
|||
} |
|||
|
|||
my_second_user: User = User(**second_user_data) |
|||
``` |
|||
|
|||
!!! info |
|||
`**second_user_data` signifie: |
|||
|
|||
Utilise les clés et valeurs du dictionnaire `second_user_data` directement comme des arguments clé-valeur. C'est équivalent à: `User(id=4, name="Mary", joined="2018-11-30")` |
|||
|
|||
### Support d'éditeurs |
|||
|
|||
Tout le framework a été conçu pour être facile et intuitif d'utilisation, toutes les décisions de design ont été testées sur de nombreux éditeurs avant même de commencer le développement final afin d'assurer la meilleure expérience de développement possible. |
|||
|
|||
Dans le dernier sondage effectué auprès de développeurs python il était clair que <a href="https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features" class="external-link" target="_blank">la fonctionnalité la plus utilisée est "l'autocomplètion"</a>. |
|||
|
|||
Tout le framwork **FastAPI** a été conçu avec cela en tête. L'autocomplétion fonctionne partout. |
|||
|
|||
Vous devrez rarement revenir à la documentation. |
|||
|
|||
Voici comment votre éditeur peut vous aider: |
|||
|
|||
* dans <a href="https://code.visualstudio.com/" class="external-link" target="_blank">Visual Studio Code</a>: |
|||
|
|||
 |
|||
|
|||
* dans <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>: |
|||
|
|||
 |
|||
|
|||
Vous aurez des propositions de complétion que vous n'auriez jamais imaginées. Par exemple la clé `prix` dans le corps d'un document JSON (qui est peut-être imbriqué) venant d'une requête. |
|||
|
|||
Plus jamais vous ne vous tromperez en tapant le nom d'une clé, vous ne ferez des aller-retour entre votre code et la documentation ou vous ne scrollerez de haut en bas afin d'enfin savoir si vous devez taper `username` ou `user_name`. |
|||
|
|||
### Court |
|||
|
|||
Des **valeurs par défaut** sont définies pour tout, des configurations optionnelles sont présentent partout. Tous ces paramètres peuvent être ajustés afin de faire ce que vous voulez et définir l'API dont vous avez besoin. |
|||
|
|||
Mais, **tout fonctionne** par défaut. |
|||
|
|||
### Validation |
|||
|
|||
* Validation pour la plupart (ou tous?) les **types de données** Python incluant: |
|||
* objets JSON (`dict`). |
|||
* listes JSON (`list`) définissant des types d'éléments. |
|||
* Champs String (`str`), définition de longueur minimum ou maximale. |
|||
* Nombres (`int`, `float`) avec valeur minimale and maximale, etc. |
|||
|
|||
* Validation pour des types plus exotiques, tel que: |
|||
* URL. |
|||
* Email. |
|||
* UUID. |
|||
* ...et autres. |
|||
|
|||
Toutes les validations sont gérées par le bien établi et robuste **Pydantic**. |
|||
|
|||
### Sécurité et authentification |
|||
|
|||
La sécurité et l'authentification sont intégrées. Sans aucun compromis avec les bases de données ou les modèles de données. |
|||
|
|||
Tous les protocoles de sécurités sont définis dans OpenAPI, incluant: |
|||
|
|||
* HTTP Basic. |
|||
* **OAuth2** (aussi avec **JWT tokens**). Jetez un oeil au tutoriel [OAuth2 avec JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. |
|||
* Clés d'API dans: |
|||
* Le header. |
|||
* Les paramètres de requêtes. |
|||
* Les cookies, etc. |
|||
|
|||
Plus toutes les fonctionnalités de sécurités venant de Starlette (incluant les **cookies de sessions**). |
|||
|
|||
Le tout conçu en composant réutilisable facilement intégrable à vos systèmes, data stores, base de données relationnelle ou NoSQL, etc. |
|||
|
|||
### Injection de dépendances |
|||
|
|||
FastAPI contient un système simple mais extrêmement puissant d'<abbr title='aussi connus sous le nom de "composants", "ressources", "services", "providers"'><strong>Injection de Dépendances</strong></abbr>. |
|||
|
|||
* Même les dépendances peuvent avoir des dépendances, créant une hiérarchie ou un **"graph" de dépendances** |
|||
* Tout est **automatiquement géré** par le framework |
|||
* Toutes les dépendances peuvent éxiger des données d'une requêtes et **Augmenter les contraintes d'un path operation** et de la documentation automatique. |
|||
* **Validation automatique** même pour les paramètres de *path operation* définis dans les dépendances. |
|||
* Supporte les systèmes d'authentification d'utilisateurs complexes, les **connexions de base de données**, etc. |
|||
* **Aucun compromis** avec les bases de données, les frontends, etc. Mais une intégration facile avec n'importe lequel d'entre eux. |
|||
|
|||
### "Plug-ins" illimités |
|||
|
|||
Ou, en d'autres termes, pas besoin d'eux, importez le code que vous voulez et utilisez le. |
|||
|
|||
Tout intégration est conçue pour être si simple à utiliser (avec des dépendances) que vous pouvez créer un "plug-in" pour votre application en deux lignes de code utilisant la même syntaxe que celle de vos *path operations* |
|||
|
|||
### Testé |
|||
|
|||
* 100% <abbr title="La quantité de code qui est testé automatiquement">de couverture de test</abbr>. |
|||
* 100% <abbr title="Annotation de types Python, avec cela votre éditeur et autres outils externes peuvent vous fournir un meilleur support">d'annotations de type</abbr> dans le code. |
|||
* Utilisé dans des applications mises en production. |
|||
|
|||
## Fonctionnalités de Starlette |
|||
|
|||
**FastAPI** est complètement compatible (et basé sur) <a href="https://www.starlette.io/" class="external-link" target="_blank"><strong>Starlette</strong></a>. Le code utilisant Starlette que vous ajouterez fonctionnera donc aussi. |
|||
|
|||
En fait, `FastAPI` est un sous compposant de `Starlette`. Donc, si vous savez déjà comment utiliser Starlette, la plupart des fonctionnalités fonctionneront de la même manière. |
|||
|
|||
Avec **FastAPI** vous aurez toutes les fonctionnalités de **Starlette** (FastAPI est juste Starlette sous stéroïdes): |
|||
|
|||
* Des performances vraiments impressionnantes. C'est l'<a href="https://github.com/encode/starlette#performance" class="external-link" target="_blank">un des framework Python les plus rapide, à égalité avec **NodeJS** et **GO**</a>. |
|||
* Le support des **WebSockets**. |
|||
* Le support de **GraphQL**. |
|||
* Les <abbr title="En anglais: In-process background tasks">tâches d'arrière-plan.</abbr> |
|||
* Des évènements de démarrages et d'arrêt. |
|||
* Un client de test basé sur `request` |
|||
* **CORS**, GZip, Static Files, Streaming responses. |
|||
* Le support des **Sessions et Cookies**. |
|||
* Une couverture de test à 100 %. |
|||
* 100 % de la base de code avec des annotations de type. |
|||
|
|||
## Fonctionnalités de Pydantic |
|||
|
|||
**FastAPI** est totalement compatible avec (et basé sur) <a href="https://pydantic-docs.helpmanual.io" class="external-link" target="_blank"><strong>Pydantic</strong></a>. Le code utilisant Pydantic que vous ajouterez fonctionnera donc aussi. |
|||
|
|||
Inclus des librairies externes basées, aussi, sur Pydantic, servent d'<abbr title="Object-Relational Mapper">ORM</abbr>s, <abbr title="Object-Document Mapper">ODM</abbr>s pour les bases de données. |
|||
|
|||
Cela signifie aussi que, dans la plupart des cas, vous pouvez fournir l'objet reçu d'une requête **directement à la base de données**, comme tout est validé automatiquement. |
|||
|
|||
Inversément, dans la plupart des cas vous pourrez juste envoyer l'objet récupéré de la base de données **directement au client** |
|||
|
|||
Avec **FastAPI** vous aurez toutes les fonctionnalités de **Pydantic** (comme FastAPI est basé sur Pydantic pour toutes les manipulations de données): |
|||
|
|||
* **Pas de prise de tête**: |
|||
* Pas de nouveau langage de définition de schéma à apprendre. |
|||
* Si vous connaissez le typage en python vous savez comment utiliser Pydantic. |
|||
* Aide votre **<abbr title="Integrated Development Environment, il s'agit de votre éditeur de code">IDE</abbr>/<abbr title="Programme qui analyse le code à la recherche d'erreurs">linter</abbr>/cerveau**: |
|||
* Parce que les structures de données de pydantic consistent seulement en une instance de classe que vous définissez; l'auto-complétion, le linting, mypy et votre intuition devrait être largement suffisante pour valider vos données. |
|||
* **Rapide**: |
|||
* Dans les <a href="https://pydantic-docs.helpmanual.io/#benchmarks-tag" class="external-link" target="_blank">benchmarks</a> Pydantic est plus rapide que toutes les autres librairies testées. |
|||
* Valide les **structures complexes**: |
|||
* Utilise les modèles hiérarchique de Pydantic, le `typage` Python pour les `Lists`, `Dict`, etc. |
|||
* Et les validateurs permettent aux schémas de données complexes d'être clairement et facilement définis, validés et documentés sous forme d'un schéma JSON. |
|||
* Vous pouvez avoir des objets **JSON fortements imbriqués** tout en ayant, pour chacun, de la validation et des annotations. |
|||
* **Renouvelable**: |
|||
* Pydantic permet de définir de nouveaux types de données ou vous pouvez étendre la validation avec des méthodes sur un modèle décoré avec le<abbr title="en anglais: validator decorator"> décorateur de validation</abbr> |
|||
* 100% de couverture de test. |
@ -0,0 +1,94 @@ |
|||
# Tâches d'arrière-plan |
|||
|
|||
Vous pouvez définir des tâches d'arrière-plan qui seront exécutées après avoir retourné une réponse. |
|||
|
|||
Ceci est utile pour les opérations qui doivent avoir lieu après une requête, mais où le client n'a pas réellement besoin d'attendre que l'opération soit terminée pour recevoir une réponse. |
|||
|
|||
Cela comprend, par exemple : |
|||
|
|||
* Les notifications par email envoyées après l'exécution d'une action : |
|||
* Étant donné que se connecter à un serveur et envoyer un email a tendance à être «lent» (plusieurs secondes), vous pouvez retourner la réponse directement et envoyer la notification en arrière-plan. |
|||
* Traiter des données : |
|||
* Par exemple, si vous recevez un fichier qui doit passer par un traitement lent, vous pouvez retourner une réponse «Accepted» (HTTP 202) puis faire le traitement en arrière-plan. |
|||
|
|||
|
|||
## Utiliser `BackgroundTasks` |
|||
|
|||
Pour commencer, importez `BackgroundTasks` et définissez un paramètre dans votre *fonction de chemin* avec `BackgroundTasks` comme type déclaré. |
|||
|
|||
```Python hl_lines="1 13" |
|||
{!../../../docs_src/background_tasks/tutorial001.py!} |
|||
``` |
|||
|
|||
**FastAPI** créera l'objet de type `BackgroundTasks` pour vous et le passera comme paramètre. |
|||
|
|||
## Créer une fonction de tâche |
|||
|
|||
Une fonction à exécuter comme tâche d'arrière-plan est juste une fonction standard qui peut recevoir des paramètres. |
|||
|
|||
Elle peut être une fonction asynchrone (`async def`) ou une fonction normale (`def`), **FastAPI** saura la gérer correctement. |
|||
|
|||
Dans cet exemple, la fonction de tâche écrira dans un fichier (afin de simuler un envoi d'email). |
|||
|
|||
L'opération d'écriture n'utilisant ni `async` ni `await`, on définit la fonction avec un `def` normal. |
|||
|
|||
```Python hl_lines="6-9" |
|||
{!../../../docs_src/background_tasks/tutorial001.py!} |
|||
``` |
|||
|
|||
## Ajouter une tâche d'arrière-plan |
|||
|
|||
Dans votre *fonction de chemin*, passez votre fonction de tâche à l'objet de type `BackgroundTasks` (`background_tasks` ici) grâce à la méthode `.add_task()` : |
|||
|
|||
|
|||
```Python hl_lines="14" |
|||
{!../../../docs_src/background_tasks/tutorial001.py!} |
|||
``` |
|||
|
|||
`.add_task()` reçoit comme arguments : |
|||
|
|||
* Une fonction de tâche à exécuter en arrière-plan (`write_notification`). |
|||
* Les arguments positionnels à passer à la fonction de tâche dans l'ordre (`email`). |
|||
* Les arguments nommés à passer à la fonction de tâche (`message="some notification"`). |
|||
|
|||
## Injection de dépendances |
|||
|
|||
Utiliser `BackgroundTasks` fonctionne aussi avec le système d'injection de dépendances. Vous pouvez déclarer un paramètre de type `BackgroundTasks` à différents niveaux : dans une *fonction de chemin*, dans une dépendance, dans une sous-dépendance... |
|||
|
|||
**FastAPI** sait quoi faire dans chaque cas et comment réutiliser le même objet, afin que tous les paramètres de type `BackgroundTasks` soient fusionnés et que les tâches soient exécutées en arrière-plan : |
|||
|
|||
```Python hl_lines="13 15 22 25" |
|||
{!../../../docs_src/background_tasks/tutorial002.py!} |
|||
``` |
|||
|
|||
Dans cet exemple, les messages seront écrits dans le fichier `log.txt` après que la réponse soit envoyée. |
|||
|
|||
S'il y avait une `query` (paramètre nommé `q`) dans la requête, alors elle sera écrite dans `log.txt` via une tâche d'arrière-plan. |
|||
|
|||
Et ensuite une autre tâche d'arrière-plan (générée dans les paramètres de la *la fonction de chemin*) écrira un message dans `log.txt` comprenant le paramètre de chemin `email`. |
|||
|
|||
## Détails techniques |
|||
|
|||
La classe `BackgroundTasks` provient directement de <a href="https://www.starlette.io/background/" class="external-link" target="_blank">`starlette.background`</a>. |
|||
|
|||
Elle est importée/incluse directement dans **FastAPI** pour que vous puissiez l'importer depuis `fastapi` et éviter d'importer accidentellement `BackgroundTask` (sans `s` à la fin) depuis `starlette.background`. |
|||
|
|||
En utilisant seulement `BackgroundTasks` (et non `BackgroundTask`), il est possible de l'utiliser en tant que paramètre de *fonction de chemin* et de laisser **FastAPI** gérer le reste pour vous, comme en utilisant l'objet `Request` directement. |
|||
|
|||
Il est tout de même possible d'utiliser `BackgroundTask` seul dans **FastAPI**, mais dans ce cas il faut créer l'objet dans le code et renvoyer une `Response` Starlette l'incluant. |
|||
|
|||
Plus de détails sont disponibles dans <a href="https://www.starlette.io/background/" class="external-link" target="_blank">la documentation officielle de Starlette sur les tâches d'arrière-plan</a> (via leurs classes `BackgroundTasks`et `BackgroundTask`). |
|||
|
|||
## Avertissement |
|||
|
|||
Si vous avez besoin de réaliser des traitements lourds en tâche d'arrière-plan et que vous n'avez pas besoin que ces traitements aient lieu dans le même process (par exemple, pas besoin de partager la mémoire, les variables, etc.), il peut s'avérer profitable d'utiliser des outils plus importants tels que <a href="https://docs.celeryproject.org" class="external-link" target="_blank">Celery</a>. |
|||
|
|||
Ces outils nécessitent généralement des configurations plus complexes ainsi qu'un gestionnaire de queue de message, comme RabbitMQ ou Redis, mais ils permettent d'exécuter des tâches d'arrière-plan dans différents process, et potentiellement, sur plusieurs serveurs. |
|||
|
|||
Pour voir un exemple, allez voir les [Générateurs de projets](../project-generation.md){.internal-link target=_blank}, ils incluent tous Celery déjà configuré. |
|||
|
|||
Mais si vous avez besoin d'accéder aux variables et objets de la même application **FastAPI**, ou si vous avez besoin d'effectuer de petites tâches d'arrière-plan (comme envoyer des notifications par email), vous pouvez simplement vous contenter d'utiliser `BackgroundTasks`. |
|||
|
|||
## Résumé |
|||
|
|||
Importez et utilisez `BackgroundTasks` grâce aux paramètres de *fonction de chemin* et les dépendances pour ajouter des tâches d'arrière-plan. |
@ -0,0 +1,244 @@ |
|||
# 경로 매개변수 |
|||
|
|||
파이썬 포맷 문자열이 사용하는 동일한 문법으로 "매개변수" 또는 "변수"를 경로에 선언할 수 있습니다: |
|||
|
|||
```Python hl_lines="6-7" |
|||
{!../../../docs_src/path_params/tutorial001.py!} |
|||
``` |
|||
|
|||
경로 매개변수 `item_id`의 값은 함수의 `item_id` 인자로 전달됩니다. |
|||
|
|||
그래서 이 예제를 실행하고 <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>로 이동하면, 다음 응답을 볼 수 있습니다: |
|||
|
|||
```JSON |
|||
{"item_id":"foo"} |
|||
``` |
|||
|
|||
## 타입이 있는 매개변수 |
|||
|
|||
파이썬 표준 타입 어노테이션을 사용하여 함수에 있는 경로 매개변수의 타입을 선언할 수 있습니다: |
|||
|
|||
```Python hl_lines="7" |
|||
{!../../../docs_src/path_params/tutorial002.py!} |
|||
``` |
|||
|
|||
지금과 같은 경우, `item_id`는 `int`로 선언 되었습니다. |
|||
|
|||
!!! check "확인" |
|||
이 기능은 함수 내에서 오류 검사, 자동완성 등을 편집기를 지원합니다 |
|||
|
|||
## 데이터 <abbr title="다음으로도 알려져 있습니다: 직렬화, 파싱, 마샬링">변환</abbr> |
|||
|
|||
이 예제를 실행하고 <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a>을 열면, 다음 응답을 볼 수 있습니다: |
|||
|
|||
```JSON |
|||
{"item_id":3} |
|||
``` |
|||
|
|||
!!! check "확인" |
|||
함수가 받은(반환도 하는) 값은 문자열 `"3"`이 아니라 파이썬 `int` 형인 `3`입니다. |
|||
|
|||
즉, 타입 선언을 하면 **FastAPI**는 자동으로 요청을 <abbr title="HTTP 요청에서 전달되는 문자열을 파이썬 데이터로 변환">"파싱"</abbr>합니다. |
|||
|
|||
## 데이터 검증 |
|||
|
|||
하지만 브라우저에서 <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>로 이동하면, 멋진 HTTP 오류를 볼 수 있습니다: |
|||
|
|||
```JSON |
|||
{ |
|||
"detail": [ |
|||
{ |
|||
"loc": [ |
|||
"path", |
|||
"item_id" |
|||
], |
|||
"msg": "value is not a valid integer", |
|||
"type": "type_error.integer" |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
경로 매개변수 `item_id`는 `int`가 아닌 `"foo"` 값이기 때문입니다. |
|||
|
|||
`int` 대신 `float`을 전달하면 동일한 오류가 나타납니다: <a href="http://127.0.0.1:8000/items/4.2" class="external-link" target="_blank">http://127.0.0.1:8000/items/4.2</a> |
|||
|
|||
!!! check "확인" |
|||
즉, 파이썬 타입 선언을 하면 **FastAPI**는 데이터 검증을 합니다. |
|||
|
|||
오류는 검증을 통과하지 못한 지점도 정확하게 명시합니다. |
|||
|
|||
이는 API와 상호 작용하는 코드를 개발하고 디버깅하는 데 매우 유용합니다. |
|||
|
|||
## 문서화 |
|||
|
|||
그리고 브라우저에서 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>를 열면, 다음과 같이 자동 대화식 API 문서를 볼 수 있습니다: |
|||
|
|||
<img src="/img/tutorial/path-params/image01.png"> |
|||
|
|||
!!! check "확인" |
|||
다시 한번, 그저 파이썬 타입 선언을 하기만 하면 **FastAPI**는 자동 대화식 API 문서(Swagger UI 통합)를 제공합니다. |
|||
|
|||
경로 매개변수는 정수형으로 선언됐음을 주목하세요. |
|||
|
|||
## 표준 기반의 이점, 대체 문서화 |
|||
|
|||
그리고 생성된 스키마는 <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md" class="external-link" target="_blank">OpenAPI</a> 표준에서 나온 것이기 때문에 호환되는 도구가 많이 있습니다. |
|||
|
|||
이 덕분에 **FastAPI**는 <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>로 접속할 수 있는 (ReDoc을 사용하는) 대체 API 문서를 제공합니다: |
|||
|
|||
<img src="/img/tutorial/path-params/image02.png"> |
|||
|
|||
이와 마찬가지로 호환되는 도구가 많이 있습니다. 다양한 언어에 대한 코드 생성 도구를 포함합니다. |
|||
|
|||
## Pydantic |
|||
|
|||
모든 데이터 검증은 <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a>에 의해 내부적으로 수행되므로 이로 인한 모든 이점을 얻을 수 있습니다. 여러분은 관리를 잘 받고 있음을 느낄 수 있습니다. |
|||
|
|||
`str`, `float`, `bool`과 다른 복잡한 데이터 타입 선언을 할 수 있습니다. |
|||
|
|||
이 중 몇 가지는 자습서의 다음 장에서 살펴봅니다. |
|||
|
|||
## 순서 문제 |
|||
|
|||
*경로 동작*을 만들때 고정 경로를 갖고 있는 상황들을 맞닦뜨릴 수 있습니다. |
|||
|
|||
`/users/me`처럼, 현재 사용자의 데이터를 가져온다고 합시다. |
|||
|
|||
사용자 ID를 이용해 특정 사용자의 정보를 가져오는 경로 `/users/{user_id}`도 있습니다. |
|||
|
|||
*경로 동작*은 순차적으로 평가되기 때문에 `/users/{user_id}` 이전에 `/users/me`를 먼저 선언해야 합니다: |
|||
|
|||
```Python hl_lines="6 11" |
|||
{!../../../docs_src/path_params/tutorial003.py!} |
|||
``` |
|||
|
|||
그렇지 않으면 `/users/{user_id}`는 매개변수 `user_id`의 값을 `"me"`라고 "생각하여" `/users/me`도 연결합니다. |
|||
|
|||
## 사전정의 값 |
|||
|
|||
만약 *경로 매개변수*를 받는 *경로 동작*이 있지만, 유효하고 미리 정의할 수 있는 *경로 매개변수* 값을 원한다면 파이썬 표준 <abbr title="열거형(Enumeration)">`Enum`</abbr>을 사용할 수 있습니다. |
|||
|
|||
### `Enum` 클래스 생성 |
|||
|
|||
`Enum`을 임포트하고 `str`과 `Enum`을 상속하는 서브 클래스를 만듭니다. |
|||
|
|||
`str`을 상속함으로써 API 문서는 값이 `string` 형이어야 하는 것을 알게 되고 제대로 렌더링 할 수 있게 됩니다. |
|||
|
|||
고정값으로 사용할 수 있는 유효한 클래스 어트리뷰트를 만듭니다: |
|||
|
|||
```Python hl_lines="1 6-9" |
|||
{!../../../docs_src/path_params/tutorial005.py!} |
|||
``` |
|||
|
|||
!!! info "정보" |
|||
<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">열거형(또는 enums)</a>은 파이썬 버전 3.4 이후로 사용가능합니다. |
|||
|
|||
!!! tip "팁" |
|||
혹시 헷갈린다면, "AlexNet", "ResNet", 그리고 "LeNet"은 그저 기계 학습 <abbr title="기술적으로 정확히는 딥 러닝 모델 구조">모델</abbr>들의 이름입니다. |
|||
|
|||
### *경로 매개변수* 선언 |
|||
|
|||
생성한 열거형 클래스(`ModelName`)를 사용하는 타입 어노테이션으로 *경로 매개변수*를 만듭니다: |
|||
|
|||
```Python hl_lines="16" |
|||
{!../../../docs_src/path_params/tutorial005.py!} |
|||
``` |
|||
|
|||
### 문서 확인 |
|||
|
|||
*경로 매개변수*에 사용할 수 있는 값은 미리 정의되어 있으므로 대화형 문서에서 멋지게 표시됩니다: |
|||
|
|||
<img src="/img/tutorial/path-params/image03.png"> |
|||
|
|||
### 파이썬 *열거형*으로 작업하기 |
|||
|
|||
*경로 매개변수*의 값은 *열거형 멤버*가 됩니다. |
|||
|
|||
#### *열거형 멤버* 비교 |
|||
|
|||
열거체 `ModelName`의 *열거형 멤버*를 비교할 수 있습니다: |
|||
|
|||
```Python hl_lines="17" |
|||
{!../../../docs_src/path_params/tutorial005.py!} |
|||
``` |
|||
|
|||
#### *열거형 값* 가져오기 |
|||
|
|||
`model_name.value` 또는 일반적으로 `your_enum_member.value`를 이용하여 실제값(지금의 경우 `str`)을 가져올 수 있습니다: |
|||
|
|||
```Python hl_lines="20" |
|||
{!../../../docs_src/path_params/tutorial005.py!} |
|||
``` |
|||
|
|||
!!! tip "팁" |
|||
`ModelName.lenet.value`로도 값 `"lenet"`에 접근할 수 있습니다. |
|||
|
|||
#### *열거형 멤버* 반환 |
|||
|
|||
*경로 동작*에서 중첩 JSON 본문(예: `dict`) 역시 *열거형 멤버*를 반환할 수 있습니다. |
|||
|
|||
클라이언트에 반환하기 전에 해당 값(이 경우 문자열)으로 변환됩니다: |
|||
|
|||
```Python hl_lines="18 21 23" |
|||
{!../../../docs_src/path_params/tutorial005.py!} |
|||
``` |
|||
|
|||
클라이언트는 아래의 JSON 응답을 얻습니다: |
|||
|
|||
```JSON |
|||
{ |
|||
"model_name": "alexnet", |
|||
"message": "Deep Learning FTW!" |
|||
} |
|||
``` |
|||
|
|||
## 경로를 포함하는 경로 매개변수 |
|||
|
|||
`/files/{file_path}`가 있는 *경로 동작*이 있다고 해봅시다. |
|||
|
|||
그런데 여러분은 `home/johndoe/myfile.txt`처럼 *path*에 들어있는 `file_path` 자체가 필요합니다. |
|||
|
|||
따라서 해당 파일의 URL은 다음처럼 됩니다: `/files/home/johndoe/myfile.txt`. |
|||
|
|||
### OpenAPI 지원 |
|||
|
|||
테스트와 정의가 어려운 시나리오로 이어질 수 있으므로 OpenAPI는 *경로*를 포함하는 *경로 매개변수*를 내부에 선언하는 방법을 지원하지 않습니다. |
|||
|
|||
그럼에도 Starlette의 내부 도구중 하나를 사용하여 **FastAPI**에서는 할 수 있습니다. |
|||
|
|||
매개변수에 경로가 포함되어야 한다는 문서를 추가하지 않아도 문서는 계속 작동합니다. |
|||
|
|||
### 경로 변환기 |
|||
|
|||
Starlette에서 직접 옵션을 사용하면 다음과 같은 URL을 사용하여 *path*를 포함하는 *경로 매개변수*를 선언 할 수 있습니다: |
|||
|
|||
``` |
|||
/files/{file_path:path} |
|||
``` |
|||
|
|||
이러한 경우 매개변수의 이름은 `file_path`이고 마지막 부분 `:path`는 매개변수가 *경로*와 일치해야함을 알려줍니다. |
|||
|
|||
그러므로 다음과 같이 사용할 수 있습니다: |
|||
|
|||
```Python hl_lines="6" |
|||
{!../../../docs_src/path_params/tutorial004.py!} |
|||
``` |
|||
|
|||
!!! tip "팁" |
|||
매개변수가 `/home/johndoe/myfile.txt`를 갖고 있어 슬래시로 시작(`/`)해야 할 수 있습니다. |
|||
|
|||
이 경우 URL은: `/files//home/johndoe/myfile.txt`이며 `files`과 `home` 사이에 이중 슬래시(`//`)가 생깁니다. |
|||
|
|||
## 요약 |
|||
|
|||
**FastAPI**과 함께라면 짧고 직관적인 표준 파이썬 타입 선언을 사용하여 다음을 얻을 수 있습니다: |
|||
|
|||
* 편집기 지원: 오류 검사, 자동완성 등 |
|||
* 데이터 "<abbr title="HTTP 요청에서 전달되는 문자열을 파이썬 데이터로 변환">파싱</abbr>" |
|||
* 데이터 검증 |
|||
* API 주석(Annotation)과 자동 문서 |
|||
|
|||
위 사항들을 그저 한번에 선언하면 됩니다. |
|||
|
|||
이는 (원래 성능과는 별개로) 대체 프레임워크와 비교했을 때 **FastAPI**의 주요 가시적 장점일 것입니다. |
@ -0,0 +1,198 @@ |
|||
# 쿼리 매개변수 |
|||
|
|||
경로 매개변수의 일부가 아닌 다른 함수 매개변수를 선언할 때, "쿼리" 매개변수로 자동 해석합니다. |
|||
|
|||
```Python hl_lines="9" |
|||
{!../../../docs_src/query_params/tutorial001.py!} |
|||
``` |
|||
|
|||
쿼리는 URL에서 `?` 후에 나오고 `&`으로 구분되는 키-값 쌍의 집합입니다. |
|||
|
|||
예를 들어, URL에서: |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/?skip=0&limit=10 |
|||
``` |
|||
|
|||
...쿼리 매개변수는: |
|||
|
|||
* `skip`: 값 `0`을 가집니다. |
|||
* `limit`: 값 `10`을 가집니다. |
|||
|
|||
URL의 일부이므로 "자연스럽게" 문자열입니다. |
|||
|
|||
하지만 파이썬 타입과 함께 선언할 경우(위 예에서 `int`), 해당 타입으로 변환되고 이에 대해 검증합니다. |
|||
|
|||
경로 매개변수에 적용된 동일한 프로세스가 쿼리 매개변수에도 적용됩니다: |
|||
|
|||
* (당연히) 편집기 지원 |
|||
* 데이터 <abbr title="HTTP 요청에서 전달되는 문자열을 파이썬 데이터로 변환">"파싱"</abbr> |
|||
* 데이터 검증 |
|||
* 자동 문서화 |
|||
|
|||
## 기본값 |
|||
|
|||
쿼리 매개변수는 경로에서 고정된 부분이 아니기 때문에 선택적일 수 있고 기본값을 가질 수 있습니다. |
|||
|
|||
위 예에서 `skip=0`과 `limit=10`은 기본값을 갖고 있습니다. |
|||
|
|||
그러므로 URL로 이동하면: |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/ |
|||
``` |
|||
|
|||
아래로 이동한 것과 같습니다: |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/?skip=0&limit=10 |
|||
``` |
|||
|
|||
하지만 가령 아래로 이동한 경우: |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/?skip=20 |
|||
``` |
|||
|
|||
함수의 매개변수 값은 아래가 됩니다: |
|||
|
|||
* `skip=20`: URL에서 지정했기 때문입니다 |
|||
* `limit=10`: 기본값이기 때문입니다 |
|||
|
|||
## 선택적 매개변수 |
|||
|
|||
같은 방법으로 기본값을 `None`으로 설정하여 선택적 매개변수를 선언할 수 있습니다: |
|||
|
|||
```Python hl_lines="9" |
|||
{!../../../docs_src/query_params/tutorial002.py!} |
|||
``` |
|||
|
|||
이 경우 함수 매개변수 `q`는 선택적이며 기본값으로 `None` 값이 됩니다. |
|||
|
|||
!!! check "확인" |
|||
**FastAPI**는 `item_id`가 경로 매개변수이고 `q`는 경로 매개변수가 아닌 쿼리 매개변수라는 것을 알 정도로 충분히 똑똑합니다. |
|||
|
|||
!!! note "참고" |
|||
FastAPI는 `q`가 `= None`이므로 선택적이라는 것을 인지합니다. |
|||
|
|||
`Optional[str]`에 있는 `Optional`은 FastAPI(FastAPI는 `str` 부분만 사용합니다)가 사용하는게 아니지만, `Optional[str]`은 편집기에게 코드에서 오류를 찾아낼 수 있게 도와줍니다. |
|||
|
|||
## 쿼리 매개변수 형변환 |
|||
|
|||
`bool` 형으로 선언할 수도 있고, 아래처럼 변환됩니다: |
|||
|
|||
```Python hl_lines="9" |
|||
{!../../../docs_src/query_params/tutorial003.py!} |
|||
``` |
|||
|
|||
이 경우, 아래로 이동하면: |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/foo?short=1 |
|||
``` |
|||
|
|||
또는 |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/foo?short=True |
|||
``` |
|||
|
|||
또는 |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/foo?short=true |
|||
``` |
|||
|
|||
또는 |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/foo?short=on |
|||
``` |
|||
|
|||
또는 |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/foo?short=yes |
|||
``` |
|||
|
|||
또는 다른 어떤 변형(대문자, 첫글자만 대문자 등)이더라도 함수는 매개변수 `bool`형을 가진 `short`의 값이 `True`임을 압니다. 그렇지 않은 경우 `False`입니다. |
|||
|
|||
|
|||
## 여러 경로/쿼리 매개변수 |
|||
|
|||
여러 경로 매개변수와 쿼리 매개변수를 동시에 선언할 수 있으며 **FastAPI**는 어느 것이 무엇인지 알고 있습니다. |
|||
|
|||
그리고 특정 순서로 선언할 필요가 없습니다. |
|||
|
|||
매개변수들은 이름으로 감지됩니다: |
|||
|
|||
```Python hl_lines="8 10" |
|||
{!../../../docs_src/query_params/tutorial004.py!} |
|||
``` |
|||
|
|||
## 필수 쿼리 매개변수 |
|||
|
|||
경로가 아닌 매개변수에 대한 기본값을 선언할 때(지금은 쿼리 매개변수만 보았습니다), 해당 매개변수는 필수적(Required)이지 않았습니다. |
|||
|
|||
특정값을 추가하지 않고 선택적으로 만들기 위해선 기본값을 `None`으로 설정하면 됩니다. |
|||
|
|||
그러나 쿼리 매개변수를 필수로 만들려면 기본값을 선언할 수 없습니다: |
|||
|
|||
```Python hl_lines="6-7" |
|||
{!../../../docs_src/query_params/tutorial005.py!} |
|||
``` |
|||
|
|||
여기 쿼리 매개변수 `needy`는 `str`형인 필수 쿼리 매개변수입니다. |
|||
|
|||
브라우저에서 URL을 아래처럼 연다면: |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/foo-item |
|||
``` |
|||
|
|||
...필수 매개변수 `needy`를 넣지 않았기 때문에 아래와 같은 오류를 보게 됩니다: |
|||
|
|||
```JSON |
|||
{ |
|||
"detail": [ |
|||
{ |
|||
"loc": [ |
|||
"query", |
|||
"needy" |
|||
], |
|||
"msg": "field required", |
|||
"type": "value_error.missing" |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
`needy`는 필수 매개변수이므로 URL에 반드시 설정해줘야 합니다: |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy |
|||
``` |
|||
|
|||
...아래처럼 작동합니다: |
|||
|
|||
```JSON |
|||
{ |
|||
"item_id": "foo-item", |
|||
"needy": "sooooneedy" |
|||
} |
|||
``` |
|||
|
|||
그리고 물론, 일부 매개변수는 필수로, 다른 일부는 기본값을, 또 다른 일부는 선택적으로 선언할 수 있습니다: |
|||
|
|||
```Python hl_lines="10" |
|||
{!../../../docs_src/query_params/tutorial006.py!} |
|||
``` |
|||
|
|||
이 경우 3가지 쿼리 매개변수가 있습니다: |
|||
|
|||
* `needy`, 필수적인 `str`. |
|||
* `skip`, 기본값이 `0`인 `int`. |
|||
* `limit`, 선택적인 `int`. |
|||
|
|||
!!! tip "팁" |
|||
[경로 매개변수](path-params.md#predefined-values){.internal-link target=_blank}와 마찬가지로 `Enum`을 사용할 수 있습니다. |
@ -0,0 +1,52 @@ |
|||
from typing import Optional |
|||
|
|||
from fastapi import Body, FastAPI |
|||
from pydantic import BaseModel |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
class Item(BaseModel): |
|||
name: str |
|||
description: Optional[str] = None |
|||
price: float |
|||
tax: Optional[float] = None |
|||
|
|||
|
|||
@app.put("/items/{item_id}") |
|||
async def update_item( |
|||
*, |
|||
item_id: int, |
|||
item: Item = Body( |
|||
..., |
|||
examples={ |
|||
"normal": { |
|||
"summary": "A normal example", |
|||
"description": "A **normal** item works correctly.", |
|||
"value": { |
|||
"name": "Foo", |
|||
"description": "A very nice Item", |
|||
"price": 35.4, |
|||
"tax": 3.2, |
|||
}, |
|||
}, |
|||
"converted": { |
|||
"summary": "An example with converted data", |
|||
"description": "FastAPI can convert price `strings` to actual `numbers` automatically", |
|||
"value": { |
|||
"name": "Bar", |
|||
"price": "35.4", |
|||
}, |
|||
}, |
|||
"invalid": { |
|||
"summary": "Invalid data is rejected with an error", |
|||
"value": { |
|||
"name": "Baz", |
|||
"price": "thirty five point four", |
|||
}, |
|||
}, |
|||
}, |
|||
), |
|||
): |
|||
results = {"item_id": item_id, "item": item} |
|||
return results |
@ -0,0 +1,889 @@ |
|||
from fastapi import Body, Cookie, FastAPI, Header, Path, Query |
|||
from fastapi.testclient import TestClient |
|||
from pydantic import BaseModel |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
class Item(BaseModel): |
|||
data: str |
|||
|
|||
class Config: |
|||
schema_extra = {"example": {"data": "Data in schema_extra"}} |
|||
|
|||
|
|||
@app.post("/schema_extra/") |
|||
def schema_extra(item: Item): |
|||
return item |
|||
|
|||
|
|||
@app.post("/example/") |
|||
def example(item: Item = Body(..., example={"data": "Data in Body example"})): |
|||
return item |
|||
|
|||
|
|||
@app.post("/examples/") |
|||
def examples( |
|||
item: Item = Body( |
|||
..., |
|||
examples={ |
|||
"example1": { |
|||
"summary": "example1 summary", |
|||
"value": {"data": "Data in Body examples, example1"}, |
|||
}, |
|||
"example2": {"value": {"data": "Data in Body examples, example2"}}, |
|||
}, |
|||
) |
|||
): |
|||
return item |
|||
|
|||
|
|||
@app.post("/example_examples/") |
|||
def example_examples( |
|||
item: Item = Body( |
|||
..., |
|||
example={"data": "Overriden example"}, |
|||
examples={ |
|||
"example1": {"value": {"data": "examples example_examples 1"}}, |
|||
"example2": {"value": {"data": "examples example_examples 2"}}, |
|||
}, |
|||
) |
|||
): |
|||
return item |
|||
|
|||
|
|||
# TODO: enable these tests once/if Form(embed=False) is supported |
|||
# TODO: In that case, define if File() should support example/examples too |
|||
# @app.post("/form_example") |
|||
# def form_example(firstname: str = Form(..., example="John")): |
|||
# return firstname |
|||
|
|||
|
|||
# @app.post("/form_examples") |
|||
# def form_examples( |
|||
# lastname: str = Form( |
|||
# ..., |
|||
# examples={ |
|||
# "example1": {"summary": "last name summary", "value": "Doe"}, |
|||
# "example2": {"value": "Doesn't"}, |
|||
# }, |
|||
# ), |
|||
# ): |
|||
# return lastname |
|||
|
|||
|
|||
# @app.post("/form_example_examples") |
|||
# def form_example_examples( |
|||
# lastname: str = Form( |
|||
# ..., |
|||
# example="Doe overriden", |
|||
# examples={ |
|||
# "example1": {"summary": "last name summary", "value": "Doe"}, |
|||
# "example2": {"value": "Doesn't"}, |
|||
# }, |
|||
# ), |
|||
# ): |
|||
# return lastname |
|||
|
|||
|
|||
@app.get("/path_example/{item_id}") |
|||
def path_example( |
|||
item_id: str = Path( |
|||
..., |
|||
example="item_1", |
|||
), |
|||
): |
|||
return item_id |
|||
|
|||
|
|||
@app.get("/path_examples/{item_id}") |
|||
def path_examples( |
|||
item_id: str = Path( |
|||
..., |
|||
examples={ |
|||
"example1": {"summary": "item ID summary", "value": "item_1"}, |
|||
"example2": {"value": "item_2"}, |
|||
}, |
|||
), |
|||
): |
|||
return item_id |
|||
|
|||
|
|||
@app.get("/path_example_examples/{item_id}") |
|||
def path_example_examples( |
|||
item_id: str = Path( |
|||
..., |
|||
example="item_overriden", |
|||
examples={ |
|||
"example1": {"summary": "item ID summary", "value": "item_1"}, |
|||
"example2": {"value": "item_2"}, |
|||
}, |
|||
), |
|||
): |
|||
return item_id |
|||
|
|||
|
|||
@app.get("/query_example/") |
|||
def query_example( |
|||
data: str = Query( |
|||
None, |
|||
example="query1", |
|||
), |
|||
): |
|||
return data |
|||
|
|||
|
|||
@app.get("/query_examples/") |
|||
def query_examples( |
|||
data: str = Query( |
|||
None, |
|||
examples={ |
|||
"example1": {"summary": "Query example 1", "value": "query1"}, |
|||
"example2": {"value": "query2"}, |
|||
}, |
|||
), |
|||
): |
|||
return data |
|||
|
|||
|
|||
@app.get("/query_example_examples/") |
|||
def query_example_examples( |
|||
data: str = Query( |
|||
None, |
|||
example="query_overriden", |
|||
examples={ |
|||
"example1": {"summary": "Qeury example 1", "value": "query1"}, |
|||
"example2": {"value": "query2"}, |
|||
}, |
|||
), |
|||
): |
|||
return data |
|||
|
|||
|
|||
@app.get("/header_example/") |
|||
def header_example( |
|||
data: str = Header( |
|||
None, |
|||
example="header1", |
|||
), |
|||
): |
|||
return data |
|||
|
|||
|
|||
@app.get("/header_examples/") |
|||
def header_examples( |
|||
data: str = Header( |
|||
None, |
|||
examples={ |
|||
"example1": {"summary": "header example 1", "value": "header1"}, |
|||
"example2": {"value": "header2"}, |
|||
}, |
|||
), |
|||
): |
|||
return data |
|||
|
|||
|
|||
@app.get("/header_example_examples/") |
|||
def header_example_examples( |
|||
data: str = Header( |
|||
None, |
|||
example="header_overriden", |
|||
examples={ |
|||
"example1": {"summary": "Qeury example 1", "value": "header1"}, |
|||
"example2": {"value": "header2"}, |
|||
}, |
|||
), |
|||
): |
|||
return data |
|||
|
|||
|
|||
@app.get("/cookie_example/") |
|||
def cookie_example( |
|||
data: str = Cookie( |
|||
None, |
|||
example="cookie1", |
|||
), |
|||
): |
|||
return data |
|||
|
|||
|
|||
@app.get("/cookie_examples/") |
|||
def cookie_examples( |
|||
data: str = Cookie( |
|||
None, |
|||
examples={ |
|||
"example1": {"summary": "cookie example 1", "value": "cookie1"}, |
|||
"example2": {"value": "cookie2"}, |
|||
}, |
|||
), |
|||
): |
|||
return data |
|||
|
|||
|
|||
@app.get("/cookie_example_examples/") |
|||
def cookie_example_examples( |
|||
data: str = Cookie( |
|||
None, |
|||
example="cookie_overriden", |
|||
examples={ |
|||
"example1": {"summary": "Qeury example 1", "value": "cookie1"}, |
|||
"example2": {"value": "cookie2"}, |
|||
}, |
|||
), |
|||
): |
|||
return data |
|||
|
|||
|
|||
client = TestClient(app) |
|||
|
|||
|
|||
openapi_schema = { |
|||
"openapi": "3.0.2", |
|||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|||
"paths": { |
|||
"/schema_extra/": { |
|||
"post": { |
|||
"summary": "Schema Extra", |
|||
"operationId": "schema_extra_schema_extra__post", |
|||
"requestBody": { |
|||
"content": { |
|||
"application/json": { |
|||
"schema": {"$ref": "#/components/schemas/Item"} |
|||
} |
|||
}, |
|||
"required": True, |
|||
}, |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/example/": { |
|||
"post": { |
|||
"summary": "Example", |
|||
"operationId": "example_example__post", |
|||
"requestBody": { |
|||
"content": { |
|||
"application/json": { |
|||
"schema": {"$ref": "#/components/schemas/Item"}, |
|||
"example": {"data": "Data in Body example"}, |
|||
} |
|||
}, |
|||
"required": True, |
|||
}, |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/examples/": { |
|||
"post": { |
|||
"summary": "Examples", |
|||
"operationId": "examples_examples__post", |
|||
"requestBody": { |
|||
"content": { |
|||
"application/json": { |
|||
"schema": {"$ref": "#/components/schemas/Item"}, |
|||
"examples": { |
|||
"example1": { |
|||
"summary": "example1 summary", |
|||
"value": { |
|||
"data": "Data in Body examples, example1" |
|||
}, |
|||
}, |
|||
"example2": { |
|||
"value": {"data": "Data in Body examples, example2"} |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"required": True, |
|||
}, |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/example_examples/": { |
|||
"post": { |
|||
"summary": "Example Examples", |
|||
"operationId": "example_examples_example_examples__post", |
|||
"requestBody": { |
|||
"content": { |
|||
"application/json": { |
|||
"schema": {"$ref": "#/components/schemas/Item"}, |
|||
"examples": { |
|||
"example1": { |
|||
"value": {"data": "examples example_examples 1"} |
|||
}, |
|||
"example2": { |
|||
"value": {"data": "examples example_examples 2"} |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"required": True, |
|||
}, |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/path_example/{item_id}": { |
|||
"get": { |
|||
"summary": "Path Example", |
|||
"operationId": "path_example_path_example__item_id__get", |
|||
"parameters": [ |
|||
{ |
|||
"required": True, |
|||
"schema": {"title": "Item Id", "type": "string"}, |
|||
"example": "item_1", |
|||
"name": "item_id", |
|||
"in": "path", |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/path_examples/{item_id}": { |
|||
"get": { |
|||
"summary": "Path Examples", |
|||
"operationId": "path_examples_path_examples__item_id__get", |
|||
"parameters": [ |
|||
{ |
|||
"required": True, |
|||
"schema": {"title": "Item Id", "type": "string"}, |
|||
"examples": { |
|||
"example1": { |
|||
"summary": "item ID summary", |
|||
"value": "item_1", |
|||
}, |
|||
"example2": {"value": "item_2"}, |
|||
}, |
|||
"name": "item_id", |
|||
"in": "path", |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/path_example_examples/{item_id}": { |
|||
"get": { |
|||
"summary": "Path Example Examples", |
|||
"operationId": "path_example_examples_path_example_examples__item_id__get", |
|||
"parameters": [ |
|||
{ |
|||
"required": True, |
|||
"schema": {"title": "Item Id", "type": "string"}, |
|||
"examples": { |
|||
"example1": { |
|||
"summary": "item ID summary", |
|||
"value": "item_1", |
|||
}, |
|||
"example2": {"value": "item_2"}, |
|||
}, |
|||
"name": "item_id", |
|||
"in": "path", |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/query_example/": { |
|||
"get": { |
|||
"summary": "Query Example", |
|||
"operationId": "query_example_query_example__get", |
|||
"parameters": [ |
|||
{ |
|||
"required": False, |
|||
"schema": {"title": "Data", "type": "string"}, |
|||
"example": "query1", |
|||
"name": "data", |
|||
"in": "query", |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/query_examples/": { |
|||
"get": { |
|||
"summary": "Query Examples", |
|||
"operationId": "query_examples_query_examples__get", |
|||
"parameters": [ |
|||
{ |
|||
"required": False, |
|||
"schema": {"title": "Data", "type": "string"}, |
|||
"examples": { |
|||
"example1": { |
|||
"summary": "Query example 1", |
|||
"value": "query1", |
|||
}, |
|||
"example2": {"value": "query2"}, |
|||
}, |
|||
"name": "data", |
|||
"in": "query", |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/query_example_examples/": { |
|||
"get": { |
|||
"summary": "Query Example Examples", |
|||
"operationId": "query_example_examples_query_example_examples__get", |
|||
"parameters": [ |
|||
{ |
|||
"required": False, |
|||
"schema": {"title": "Data", "type": "string"}, |
|||
"examples": { |
|||
"example1": { |
|||
"summary": "Qeury example 1", |
|||
"value": "query1", |
|||
}, |
|||
"example2": {"value": "query2"}, |
|||
}, |
|||
"name": "data", |
|||
"in": "query", |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/header_example/": { |
|||
"get": { |
|||
"summary": "Header Example", |
|||
"operationId": "header_example_header_example__get", |
|||
"parameters": [ |
|||
{ |
|||
"required": False, |
|||
"schema": {"title": "Data", "type": "string"}, |
|||
"example": "header1", |
|||
"name": "data", |
|||
"in": "header", |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/header_examples/": { |
|||
"get": { |
|||
"summary": "Header Examples", |
|||
"operationId": "header_examples_header_examples__get", |
|||
"parameters": [ |
|||
{ |
|||
"required": False, |
|||
"schema": {"title": "Data", "type": "string"}, |
|||
"examples": { |
|||
"example1": { |
|||
"summary": "header example 1", |
|||
"value": "header1", |
|||
}, |
|||
"example2": {"value": "header2"}, |
|||
}, |
|||
"name": "data", |
|||
"in": "header", |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/header_example_examples/": { |
|||
"get": { |
|||
"summary": "Header Example Examples", |
|||
"operationId": "header_example_examples_header_example_examples__get", |
|||
"parameters": [ |
|||
{ |
|||
"required": False, |
|||
"schema": {"title": "Data", "type": "string"}, |
|||
"examples": { |
|||
"example1": { |
|||
"summary": "Qeury example 1", |
|||
"value": "header1", |
|||
}, |
|||
"example2": {"value": "header2"}, |
|||
}, |
|||
"name": "data", |
|||
"in": "header", |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/cookie_example/": { |
|||
"get": { |
|||
"summary": "Cookie Example", |
|||
"operationId": "cookie_example_cookie_example__get", |
|||
"parameters": [ |
|||
{ |
|||
"required": False, |
|||
"schema": {"title": "Data", "type": "string"}, |
|||
"example": "cookie1", |
|||
"name": "data", |
|||
"in": "cookie", |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/cookie_examples/": { |
|||
"get": { |
|||
"summary": "Cookie Examples", |
|||
"operationId": "cookie_examples_cookie_examples__get", |
|||
"parameters": [ |
|||
{ |
|||
"required": False, |
|||
"schema": {"title": "Data", "type": "string"}, |
|||
"examples": { |
|||
"example1": { |
|||
"summary": "cookie example 1", |
|||
"value": "cookie1", |
|||
}, |
|||
"example2": {"value": "cookie2"}, |
|||
}, |
|||
"name": "data", |
|||
"in": "cookie", |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"/cookie_example_examples/": { |
|||
"get": { |
|||
"summary": "Cookie Example Examples", |
|||
"operationId": "cookie_example_examples_cookie_example_examples__get", |
|||
"parameters": [ |
|||
{ |
|||
"required": False, |
|||
"schema": {"title": "Data", "type": "string"}, |
|||
"examples": { |
|||
"example1": { |
|||
"summary": "Qeury example 1", |
|||
"value": "cookie1", |
|||
}, |
|||
"example2": {"value": "cookie2"}, |
|||
}, |
|||
"name": "data", |
|||
"in": "cookie", |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
}, |
|||
"components": { |
|||
"schemas": { |
|||
"HTTPValidationError": { |
|||
"title": "HTTPValidationError", |
|||
"type": "object", |
|||
"properties": { |
|||
"detail": { |
|||
"title": "Detail", |
|||
"type": "array", |
|||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|||
} |
|||
}, |
|||
}, |
|||
"Item": { |
|||
"title": "Item", |
|||
"required": ["data"], |
|||
"type": "object", |
|||
"properties": {"data": {"title": "Data", "type": "string"}}, |
|||
"example": {"data": "Data in schema_extra"}, |
|||
}, |
|||
"ValidationError": { |
|||
"title": "ValidationError", |
|||
"required": ["loc", "msg", "type"], |
|||
"type": "object", |
|||
"properties": { |
|||
"loc": { |
|||
"title": "Location", |
|||
"type": "array", |
|||
"items": {"type": "string"}, |
|||
}, |
|||
"msg": {"title": "Message", "type": "string"}, |
|||
"type": {"title": "Error Type", "type": "string"}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
} |
|||
|
|||
|
|||
def test_openapi_schema(): |
|||
""" |
|||
Test that example overrides work: |
|||
|
|||
* pydantic model schema_extra is included |
|||
* Body(example={}) overrides schema_extra in pydantic model |
|||
* Body(examples{}) overrides Body(example={}) and schema_extra in pydantic model |
|||
""" |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == openapi_schema |
|||
|
|||
|
|||
def test_call_api(): |
|||
response = client.post("/schema_extra/", json={"data": "Foo"}) |
|||
assert response.status_code == 200, response.text |
|||
response = client.post("/example/", json={"data": "Foo"}) |
|||
assert response.status_code == 200, response.text |
|||
response = client.post("/examples/", json={"data": "Foo"}) |
|||
assert response.status_code == 200, response.text |
|||
response = client.post("/example_examples/", json={"data": "Foo"}) |
|||
assert response.status_code == 200, response.text |
|||
response = client.get("/path_example/foo") |
|||
assert response.status_code == 200, response.text |
|||
response = client.get("/path_examples/foo") |
|||
assert response.status_code == 200, response.text |
|||
response = client.get("/path_example_examples/foo") |
|||
assert response.status_code == 200, response.text |
|||
response = client.get("/query_example/") |
|||
assert response.status_code == 200, response.text |
|||
response = client.get("/query_examples/") |
|||
assert response.status_code == 200, response.text |
|||
response = client.get("/query_example_examples/") |
|||
assert response.status_code == 200, response.text |
|||
response = client.get("/header_example/") |
|||
assert response.status_code == 200, response.text |
|||
response = client.get("/header_examples/") |
|||
assert response.status_code == 200, response.text |
|||
response = client.get("/header_example_examples/") |
|||
assert response.status_code == 200, response.text |
|||
response = client.get("/cookie_example/") |
|||
assert response.status_code == 200, response.text |
|||
response = client.get("/cookie_examples/") |
|||
assert response.status_code == 200, response.text |
|||
response = client.get("/cookie_example_examples/") |
|||
assert response.status_code == 200, response.text |
@ -0,0 +1,134 @@ |
|||
from fastapi.testclient import TestClient |
|||
|
|||
from docs_src.schema_extra_example.tutorial004 import app |
|||
|
|||
client = TestClient(app) |
|||
|
|||
openapi_schema = { |
|||
"openapi": "3.0.2", |
|||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|||
"paths": { |
|||
"/items/{item_id}": { |
|||
"put": { |
|||
"summary": "Update Item", |
|||
"operationId": "update_item_items__item_id__put", |
|||
"parameters": [ |
|||
{ |
|||
"required": True, |
|||
"schema": {"title": "Item Id", "type": "integer"}, |
|||
"name": "item_id", |
|||
"in": "path", |
|||
} |
|||
], |
|||
"requestBody": { |
|||
"content": { |
|||
"application/json": { |
|||
"schema": {"$ref": "#/components/schemas/Item"}, |
|||
"examples": { |
|||
"normal": { |
|||
"summary": "A normal example", |
|||
"description": "A **normal** item works correctly.", |
|||
"value": { |
|||
"name": "Foo", |
|||
"description": "A very nice Item", |
|||
"price": 35.4, |
|||
"tax": 3.2, |
|||
}, |
|||
}, |
|||
"converted": { |
|||
"summary": "An example with converted data", |
|||
"description": "FastAPI can convert price `strings` to actual `numbers` automatically", |
|||
"value": {"name": "Bar", "price": "35.4"}, |
|||
}, |
|||
"invalid": { |
|||
"summary": "Invalid data is rejected with an error", |
|||
"value": { |
|||
"name": "Baz", |
|||
"price": "thirty five point four", |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
"required": True, |
|||
}, |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
} |
|||
}, |
|||
"components": { |
|||
"schemas": { |
|||
"HTTPValidationError": { |
|||
"title": "HTTPValidationError", |
|||
"type": "object", |
|||
"properties": { |
|||
"detail": { |
|||
"title": "Detail", |
|||
"type": "array", |
|||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|||
} |
|||
}, |
|||
}, |
|||
"Item": { |
|||
"title": "Item", |
|||
"required": ["name", "price"], |
|||
"type": "object", |
|||
"properties": { |
|||
"name": {"title": "Name", "type": "string"}, |
|||
"description": {"title": "Description", "type": "string"}, |
|||
"price": {"title": "Price", "type": "number"}, |
|||
"tax": {"title": "Tax", "type": "number"}, |
|||
}, |
|||
}, |
|||
"ValidationError": { |
|||
"title": "ValidationError", |
|||
"required": ["loc", "msg", "type"], |
|||
"type": "object", |
|||
"properties": { |
|||
"loc": { |
|||
"title": "Location", |
|||
"type": "array", |
|||
"items": {"type": "string"}, |
|||
}, |
|||
"msg": {"title": "Message", "type": "string"}, |
|||
"type": {"title": "Error Type", "type": "string"}, |
|||
}, |
|||
}, |
|||
} |
|||
}, |
|||
} |
|||
|
|||
|
|||
def test_openapi_schema(): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == openapi_schema |
|||
|
|||
|
|||
# Test required and embedded body parameters with no bodies sent |
|||
def test_post_body_example(): |
|||
response = client.put( |
|||
"/items/5", |
|||
json={ |
|||
"name": "Foo", |
|||
"description": "A very nice Item", |
|||
"price": 35.4, |
|||
"tax": 3.2, |
|||
}, |
|||
) |
|||
assert response.status_code == 200 |
Loading…
Reference in new issue