Compare commits

...

24 Commits

Author SHA1 Message Date
Sebastián Ramírez
6e1cd45a46 🔖 Release version 0.53.2 2020-03-30 21:49:50 +02:00
Sebastián Ramírez
b86d130eb6 📝 Update release notes 2020-03-30 21:47:11 +02:00
Toan Vuong
90afc72e64 🐛 Fix automatic embedding with dependencies and sub-dependencies (#1079)
* Handle automatic embedding with Depends

* 🐛 Fix body embeds for sub-dependencies and simplify implementation

*  Add/update tests for body embeds in dependencies

* 👷 Trigger Travis

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-03-30 21:44:43 +02:00
Sebastián Ramírez
042c697b6b 📝 Update release notes 2020-03-30 20:49:24 +02:00
amitlissack
02441ff031 🐛 Fix dependency overrides in WebSockets (#1122)
* add tests to test_ws_router to test dependencies and dependency overrides.

* supply dependency_overrides_provider to APIWebSocketRoute upon creation
2020-03-30 20:45:05 +02:00
Sebastián Ramírez
210af1fd3d 📝 Update release notes 2020-03-30 20:42:46 +02:00
Sebastián Ramírez
06eaa32bf0 🔧 Update docs script to make sure languages are always sorted (#1189) 2020-03-30 20:41:50 +02:00
Sebastián Ramírez
4f88a5fddb 📝 Update release notes 2020-03-30 20:26:07 +02:00
Ikkyu
eb6be1d725 💬 Add new language of docs: zh (#1187) 2020-03-30 20:14:58 +02:00
Sebastián Ramírez
1d99681fd4 📝 Update release notes 2020-03-30 12:01:30 +02:00
Sebastián Ramírez
618be44023 🔧 Add .env to git, to simplify VS Code development 2020-03-30 12:00:55 +02:00
Sebastián Ramírez
544afaff97 📝 Add docs for adding example to schema (#1185) 2020-03-30 11:58:48 +02:00
Sebastián Ramírez
e6b3b994be 🔖 Release version 0.53.1 2020-03-29 22:08:54 +02:00
Sebastián Ramírez
67f148ff83 📝 Update release notes 2020-03-29 22:06:02 +02:00
Sebastián Ramírez
6c34600599 🐛 Fix include docs example file (#1182) 2020-03-29 22:05:24 +02:00
John Paton
016a4b7491 📝 Add documentation of example kwarg of Field (#1106)
* Add documentation of example kwarg of Field

* 📝 Update info about schema examples

* 🚚 Move example file to new directory

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-03-29 21:43:31 +02:00
Sebastián Ramírez
be21b74ad5 📝 Update release notes 2020-03-29 19:28:53 +02:00
voegtlel
0f152b4e97 🐛 Check already cloned fields in create_cloned_field to support recursive models (#1164)
* FIX: #894
Include recursion check for create_cloned_field.
Added test for recursive model.

* ♻️ Refactor and format create_cloned_field()

Co-authored-by: Lukas Voegtle <lukas.voegtle@sick.de>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-03-29 19:26:29 +02:00
Sebastián Ramírez
0d165d1efa 📝 Update release notes 2020-03-29 18:52:33 +02:00
YangQuan
9d54215a3a 📝 Add example of Pycharm in tutorial/debugging.md (#1096)
* add example of pycharm in tutorial/debugging.md

* 📝 Update PyCharm debug instructions and screenshot

* 🚚 Move image to new location in docs

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-03-29 18:50:29 +02:00
Sebastián Ramírez
8ab916baed 📝 Update release notes 2020-03-29 17:53:49 +02:00
Paul-Louis NECH
c83c50b27d ✏️ Fix typo (#1148) 2020-03-29 17:51:58 +02:00
Sebastián Ramírez
c2ad214a84 📝 Update release notes 2020-03-29 17:05:03 +02:00
Sebastián Ramírez
459f0e11e5 🏁 Update Windows development environment and tests (#1179)
* 🏁 Fix ./scripts/docs.py encoding for Windows

* 🔥 Remove ujson from tests as it prevents Windows development

It's still tested by Starlette anyway

* 📝 Update development instructions for Windows

* 🎨 Update format for WSGIMiddleware example

*  Update tests to run on Windows
2020-03-29 17:04:04 +02:00
28 changed files with 1061 additions and 100 deletions

1
.env Normal file
View File

@@ -0,0 +1 @@
PYTHONPATH=./docs_src

View File

@@ -12,7 +12,7 @@ Then wrap the WSGI (e.g. Flask) app with the middleware.
And then mount that under a path.
```Python hl_lines="1 3 22"
```Python hl_lines="2 3 22"
{!../../../docs_src/wsgi/tutorial001.py!}
```

View File

@@ -113,13 +113,25 @@ $ flit install --deps develop --symlink
</div>
If you are on Windows, use `--pth-file` instead of `--symlink`:
<div class="termy">
```console
$ flit install --deps develop --pth-file
---> 100%
```
</div>
It will install all the dependencies and your local FastAPI in your local environment.
#### Using your local FastAPI
If you create a Python file that imports and uses FastAPI, and run it with the Python from your local environment, it will use your local FastAPI source code.
And if you update that local FastAPI source code, as it is installed with `--symlink`, when you run that Python file again, it will use the fresh version of FastAPI you just edited.
And if you update that local FastAPI source code, as it is installed with `--symlink` (or `--pth-file` on Windows), when you run that Python file again, it will use the fresh version of FastAPI you just edited.
That way, you don't have to "install" your local version to be able to test every change.
@@ -137,17 +149,7 @@ $ bash scripts/format.sh
It will also auto-sort all your imports.
For it to sort them correctly, you need to have FastAPI installed locally in your environment, with the command in the section above:
<div class="termy">
```console
$ flit install --symlink
---> 100%
```
</div>
For it to sort them correctly, you need to have FastAPI installed locally in your environment, with the command in the section above using `--symlink` (or `--pth-file` on Windows).
### Format imports
@@ -293,7 +295,7 @@ $ python ./scripts/docs.py live es
Now you can go to <a href="http://127.0.0.1:8008" class="external-link" target="_blank">http://127.0.0.1:8008</a> and see your changes live.
If you look at the FastAPI docs website, you will see that every language has all the pages. But some are not translated and have a notification about the the translation is missing.
If you look at the FastAPI docs website, you will see that every language has all the pages. But some pages are not translated and have a notification about the missing translation.
But when you run it locally like this, you will only see the pages that are already translated.
@@ -474,10 +476,10 @@ This command generates a directory `./htmlcov/`, if you open the file `./htmlcov
### Tests in your editor
If you want to use the integrated tests in your editor add `./docs/src` to your `PYTHONPATH` variable.
If you want to use the integrated tests in your editor add `./docs_src` to your `PYTHONPATH` variable.
For example, in VS Code you can create a file `.env` with:
```env
PYTHONPATH=./docs/src
PYTHONPATH=./docs_src
```

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

View File

@@ -2,6 +2,23 @@
## Latest changes
## 0.53.2
* Fix automatic embedding of body fields for dependencies and sub-dependencies. Original PR [#1079](https://github.com/tiangolo/fastapi/pull/1079) by [@Toad2186](https://github.com/Toad2186).
* Fix dependency overrides in WebSocket testing. PR [#1122](https://github.com/tiangolo/fastapi/pull/1122) by [@amitlissack](https://github.com/amitlissack).
* Fix docs script to ensure languages are always sorted. PR [#1189](https://github.com/tiangolo/fastapi/pull/1189).
* Start translations for Chinese. PR [#1187](https://github.com/tiangolo/fastapi/pull/1187) by [@RunningIkkyu](https://github.com/RunningIkkyu).
* Add docs for [Schema Extra - Example](https://fastapi.tiangolo.com/tutorial/schema-extra-example/). PR [#1185](https://github.com/tiangolo/fastapi/pull/1185).
## 0.53.1
* Fix included example after translations refactor. PR [#1182](https://github.com/tiangolo/fastapi/pull/1182).
* Add docs example for `example` in `Field`. Docs at [Body - Fields: JSON Schema extras](https://fastapi.tiangolo.com/tutorial/body-fields/#json-schema-extras). PR [#1106](https://github.com/tiangolo/fastapi/pull/1106) by [@JohnPaton](https://github.com/JohnPaton).
* Fix using recursive models in `response_model`. PR [#1164](https://github.com/tiangolo/fastapi/pull/1164) by [@voegtlel](https://github.com/voegtlel).
* Add docs for [Pycharm Debugging](https://fastapi.tiangolo.com/tutorial/debugging/). PR [#1096](https://github.com/tiangolo/fastapi/pull/1096) by [@youngquan](https://github.com/youngquan).
* Fix typo in docs. PR [#1148](https://github.com/tiangolo/fastapi/pull/1148) by [@PLNech](https://github.com/PLNech).
* Update Windows development environment instructions. PR [#1179](https://github.com/tiangolo/fastapi/pull/1179).
## 0.53.0
* Update test coverage badge. PR [#1175](https://github.com/tiangolo/fastapi/pull/1175).

View File

@@ -35,26 +35,11 @@ You can then use `Field` with model attributes:
!!! tip
Notice how each model's attribute with a type, default value and `Field` has the same structure as a *path operation function's* parameter, with `Field` instead of `Path`, `Query` and `Body`.
## JSON Schema extras
## Add extra information
In `Field`, `Path`, `Query`, `Body` and others you'll see later, you can declare extra parameters apart from those described before.
You can declare extra information in `Field`, `Query`, `Body`, etc. And it will be included in the generated JSON Schema.
Those parameters will be added as-is to the output JSON Schema.
If you know JSON Schema and want to add extra information apart from what we have discussed here, you can pass that as extra keyword arguments.
!!! warning
Have in mind that extra parameters passed won't add any validation, only annotation, for documentation purposes.
For example, you can use that functionality to pass a <a href="http://json-schema.org/latest/json-schema-validation.html#rfc.section.8.5" class="external-link" target="_blank">JSON Schema example</a> field to a body request JSON Schema:
```Python hl_lines="20 21 22 23 24 25"
{!../../../docs_src/body_fields/tutorial002.py!}
```
And it would look in the `/docs` like this:
<img src="/img/tutorial/body-fields/image01.png">
You will learn more about it later to declare examples examples.
## Recap

View File

@@ -95,3 +95,18 @@ It will then start the server with your **FastAPI** code, stop at your breakpoin
Here's how it might look:
<img src="/img/tutorial/debugging/image01.png">
---
If you use Pycharm, you can:
* Open the "Run" menu.
* Select the option "Debug...".
* Then a context menu shows up.
* Select the file to debug (in this case, `main.py`).
It will then start the server with your **FastAPI** code, stop at your breakpoints, etc.
Here's how it might look:
<img src="/img/tutorial/debugging/image02.png">

View File

@@ -0,0 +1,58 @@
# Schema Extra - Example
You can define extra information to go in JSON Schema.
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.
## 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>:
```Python hl_lines="13 14 15 16 17 18 19 20 21"
{!../../../docs_src/schema_extra_example/tutorial001.py!}
```
That extra info will be added as-is to the output JSON Schema.
## `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`:
```Python hl_lines="2 8 9 10 11"
{!../../../docs_src/schema_extra_example/tutorial002.py!}
```
!!! warning
Have in mind that those extra arguments passed won't add any validation, only annotation, for documentation purposes.
## `Body` additional arguments
The same way you can pass extra info to `Field`, you can do the same with `Path`, `Query`, `Body`, etc.
For example, you can pass an `example` for a body request to `Body`:
```Python hl_lines="20 21 22 23 24 25"
{!../../../docs_src/schema_extra_example/tutorial003.py!}
```
## 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">
## Technical Details
About `example` vs `examples`...
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, 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).
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.
## Other info
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.

View File

@@ -54,7 +54,7 @@ You will see something like this:
<img src="/img/tutorial/security/image01.png">
!!! check "Authorize button!"
You already have a shinny new "Authorize" button.
You already have a shiny new "Authorize" button.
And your *path operation* has a little lock in the top-right corner that you can click.

View File

@@ -20,6 +20,7 @@ nav:
- Languages:
- en: /
- es: /es/
- zh: /zh/
- features.md
- python-types.md
- Tutorial - User Guide:
@@ -33,6 +34,7 @@ nav:
- tutorial/body-multiple-params.md
- tutorial/body-fields.md
- tutorial/body-nested-models.md
- tutorial/schema-extra-example.md
- tutorial/extra-data-types.md
- tutorial/cookie-params.md
- tutorial/header-params.md

View File

@@ -20,6 +20,7 @@ nav:
- Languages:
- en: /
- es: /es/
- zh: /zh/
markdown_extensions:
- toc:
permalink: true

441
docs/zh/docs/index.md Normal file
View File

@@ -0,0 +1,441 @@
{!../../../docs/missing-translation.md!}
<p align="center">
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
</p>
<p align="center">
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
</p>
<p align="center">
<a href="https://travis-ci.com/tiangolo/fastapi" target="_blank">
<img src="https://travis-ci.com/tiangolo/fastapi.svg?branch=master" alt="Build Status">
</a>
<a href="https://codecov.io/gh/tiangolo/fastapi" target="_blank">
<img src="https://img.shields.io/codecov/c/github/tiangolo/fastapi" alt="Coverage">
</a>
<a href="https://pypi.org/project/fastapi" target="_blank">
<img src="https://badge.fury.io/py/fastapi.svg" alt="Package version">
</a>
<a href="https://gitter.im/tiangolo/fastapi?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge" target="_blank">
<img src="https://badges.gitter.im/tiangolo/fastapi.svg" alt="Join the chat at https://gitter.im/tiangolo/fastapi">
</a>
</p>
---
**Documentation**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
**Source Code**: <a href="https://github.com/tiangolo/fastapi" target="_blank">https://github.com/tiangolo/fastapi</a>
---
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.6+ based on standard Python type hints.
The key features are:
* **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). [One of the fastest Python frameworks available](#performance).
* **Fast to code**: Increase the speed to develop features by about 200% to 300% *.
* **Fewer bugs**: Reduce about 40% of human (developer) induced errors. *
* **Intuitive**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging.
* **Easy**: Designed to be easy to use and learn. Less time reading docs.
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
* **Robust**: Get production-ready code. With automatic interactive documentation.
* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (previously known as Swagger) and <a href="http://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
<small>* estimation based on tests on an internal development team, building production applications.</small>
## Opinions
"*[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products.*"
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
---
"*Im over the moon excited about **FastAPI**. Its so fun!*"
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
---
"*Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that.*"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="http://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
"*If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]*"
"*We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]*"
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <a href="https://spacy.io" target="_blank">spaCy</a> creators</strong> <a href="https://twitter.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://twitter.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
---
"*We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]*"
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
---
## **Typer**, the FastAPI of CLIs
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
If you are building a <abbr title="Command Line Interface">CLI</abbr> app to be used in the terminal instead of a web API, check out <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
**Typer** is FastAPI's little sibling. And it's intended to be the **FastAPI of CLIs**. ⌨️ 🚀
## Requirements
Python 3.6+
FastAPI stands on the shoulders of giants:
* <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> for the web parts.
* <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> for the data parts.
## Installation
<div class="termy">
```console
$ pip install fastapi
---> 100%
```
</div>
You will also need an ASGI server, for production such as <a href="http://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a> or <a href="https://gitlab.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>.
<div class="termy">
```console
$ pip install uvicorn
---> 100%
```
</div>
## Example
### Create it
* Create a file `main.py` with:
```Python
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
```
<details markdown="1">
<summary>Or use <code>async def</code>...</summary>
If your code uses `async` / `await`, use `async def`:
```Python hl_lines="7 12"
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
```
**Note**:
If you don't know, check the _"In a hurry?"_ section about <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` and `await` in the docs</a>.
</details>
### Run it
Run the server with:
<div class="termy">
```console
$ uvicorn main:app --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
<span style="color: green;">INFO</span>: Started reloader process [28720]
<span style="color: green;">INFO</span>: Started server process [28722]
<span style="color: green;">INFO</span>: Waiting for application startup.
<span style="color: green;">INFO</span>: Application startup complete.
```
</div>
<details markdown="1">
<summary>About the command <code>uvicorn main:app --reload</code>...</summary>
The command `uvicorn main:app` refers to:
* `main`: the file `main.py` (the Python "module").
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
* `--reload`: make the server restart after code changes. Only do this for development.
</details>
### Check it
Open your browser at <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
You will see the JSON response as:
```JSON
{"item_id": 5, "q": "somequery"}
```
You already created an API that:
* Receives HTTP requests in the _paths_ `/` and `/items/{item_id}`.
* Both _paths_ take `GET` <em>operations</em> (also known as HTTP _methods_).
* The _path_ `/items/{item_id}` has a _path parameter_ `item_id` that should be an `int`.
* The _path_ `/items/{item_id}` has an optional `str` _query parameter_ `q`.
### Interactive API docs
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
You will see the automatic interactive API documentation (provided by <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### Alternative API docs
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
You will see the alternative automatic documentation (provided by <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## Example upgrade
Now modify the file `main.py` to receive a body from a `PUT` request.
Declare the body using standard Python types, thanks to Pydantic.
```Python hl_lines="2 7 8 9 10 23 24 25"
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: bool = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
The server should reload automatically (because you added `--reload` to the `uvicorn` command above).
### Interactive API docs upgrade
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
* The interactive API documentation will be automatically updated, including the new body:
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* Click on the button "Try it out", it allows you to fill the parameters and directly interact with the API:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-04-swagger-03.png)
* Then click on the "Execute" button, the user interface will communicate with your API, send the parameters, get the results and show them on the screen:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png)
### Alternative API docs upgrade
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
* The alternative documentation will also reflect the new query parameter and body:
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### Recap
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
You do that with standard modern Python types.
You don't have to learn a new syntax, the methods or classes of a specific library, etc.
Just standard **Python 3.6+**.
For example, for an `int`:
```Python
item_id: int
```
or for a more complex `Item` model:
```Python
item: Item
```
...and with that single declaration you get:
* Editor support, including:
* Completion.
* Type checks.
* Validation of data:
* Automatic and clear errors when the data is invalid.
* Validation even for deeply nested JSON objects.
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of input data: coming from the network to Python data and types. Reading from:
* JSON.
* Path parameters.
* Query parameters.
* Cookies.
* Headers.
* Forms.
* Files.
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of output data: converting from Python data and types to network data (as JSON):
* Convert Python types (`str`, `int`, `float`, `bool`, `list`, etc).
* `datetime` objects.
* `UUID` objects.
* Database models.
* ...and many more.
* Automatic interactive API documentation, including 2 alternative user interfaces:
* Swagger UI.
* ReDoc.
---
Coming back to the previous code example, **FastAPI** will:
* Validate that there is an `item_id` in the path for `GET` and `PUT` requests.
* Validate that the `item_id` is of type `int` for `GET` and `PUT` requests.
* If it is not, the client will see a useful, clear error.
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`) for `GET` requests.
* As the `q` parameter is declared with `= None`, it is optional.
* Without the `None` it would be required (as is the body in the case with `PUT`).
* For `PUT` requests to `/items/{item_id}`, Read the body as JSON:
* Check that it has a required attribute `name` that should be a `str`.
* Check that it has a required attribute `price` that has to be a `float`.
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
* All this would also work for deeply nested JSON objects.
* Convert from and to JSON automatically.
* Document everything with OpenAPI, that can be used by:
* Interactive documentation systems.
* Automatic client code generation systems, for many languages.
* Provide 2 interactive documentation web interfaces directly.
---
We just scratched the surface, but you already get the idea of how it all works.
Try changing the line with:
```Python
return {"item_name": item.name, "item_id": item_id}
```
...from:
```Python
... "item_name": item.name ...
```
...to:
```Python
... "item_price": item.price ...
```
...and see how your editor will auto-complete the attributes and know their types:
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
For a more complete example including more features, see the <a href="https://fastapi.tiangolo.com/tutorial/">Tutorial - User Guide</a>.
**Spoiler alert**: the tutorial - user guide includes:
* Declaration of **parameters** from other different places as: **headers**, **cookies**, **form fields** and **files**.
* How to set **validation constraints** as `maximum_length` or `regex`.
* A very powerful and easy to use **<abbr title="also known as components, resources, providers, services, injectables">Dependency Injection</abbr>** system.
* Security and authentication, including support for **OAuth2** with **JWT tokens** and **HTTP Basic** auth.
* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic).
* Many extra features (thanks to Starlette) as:
* **WebSockets**
* **GraphQL**
* extremely easy tests based on `requests` and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
## Performance
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
To understand more about it, see the section <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
## Optional Dependencies
Used by Pydantic:
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - for faster JSON <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>.
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email_validator</code></a> - for email validation.
Used by Starlette:
* <a href="http://docs.python-requests.org" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
* <a href="https://github.com/Tinche/aiofiles" target="_blank"><code>aiofiles</code></a> - Required if you want to use `FileResponse` or `StaticFiles`.
* <a href="http://jinja.pocoo.org" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Required for Starlette's `SchemaGenerator` support (you probably don't need it with FastAPI).
* <a href="https://graphene-python.org/" target="_blank"><code>graphene</code></a> - Required for `GraphQLApp` support.
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Required if you want to use `UJSONResponse`.
Used by FastAPI / Starlette:
* <a href="http://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - for the server that loads and serves your application.
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Required if you want to use `ORJSONResponse`.
You can install all of these with `pip install fastapi[all]`.
## License
This project is licensed under the terms of the MIT license.

61
docs/zh/mkdocs.yml Normal file
View File

@@ -0,0 +1,61 @@
site_name: FastAPI
site_description: FastAPI framework, high performance, easy to learn, fast to code, ready for production
site_url: https://fastapi.tiangolo.com/zh/
theme:
name: material
palette:
primary: teal
accent: amber
logo: https://fastapi.tiangolo.com/img/icon-white.svg
favicon: https://fastapi.tiangolo.com/img/favicon.png
language: zh
repo_name: tiangolo/fastapi
repo_url: https://github.com/tiangolo/fastapi
edit_uri: ''
google_analytics:
- UA-133183413-1
- auto
nav:
- FastAPI: index.md
- Languages:
- en: /
- es: /es/
- zh: /zh/
markdown_extensions:
- toc:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
base_path: docs
- admonition
- codehilite
- extra
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
extra:
social:
- type: github
link: https://github.com/tiangolo/typer
- type: twitter
link: https://twitter.com/tiangolo
- type: linkedin
link: https://www.linkedin.com/in/tiangolo
- type: rss
link: https://dev.to/tiangolo
- type: medium
link: https://medium.com/@tiangolo
- type: globe
link: https://tiangolo.com
extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js
- https://fastapi.tiangolo.com/js/chat.js
- https://sidecar.gitter.im/dist/sidecar.v1.js

View File

@@ -0,0 +1,27 @@
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
class Config:
schema_extra = {
"example": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
}
@app.put("/items/{item_id}")
async def update_item(*, item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results

View File

@@ -0,0 +1,17 @@
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str = Field(..., example="Foo")
description: str = Field(None, example="A very nice Item")
price: float = Field(..., example=35.4)
tax: float = Field(None, example=3.2)
@app.put("/items/{item_id}")
async def update_item(*, item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results

View File

@@ -1,6 +1,6 @@
from flask import Flask, escape, request
from fastapi import FastAPI
from fastapi.middleware.wsgi import WSGIMiddleware
from flask import Flask, escape, request
flask_app = Flask(__name__)

View File

@@ -1,6 +1,6 @@
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
__version__ = "0.53.0"
__version__ = "0.53.2"
from starlette import status

View File

@@ -704,8 +704,14 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
first_param = flat_dependant.body_params[0]
field_info = get_field_info(first_param)
embed = getattr(field_info, "embed", None)
if len(flat_dependant.body_params) == 1 and not embed:
body_param_names_set = set([param.name for param in flat_dependant.body_params])
if len(body_param_names_set) == 1 and not embed:
return get_schema_compatible_field(field=first_param)
# If one field requires to embed, all have to be embedded
# in case a sub-dependency is evaluated with a single unique body field
# That is combined (embedded) with other body fields
for param in flat_dependant.body_params:
setattr(get_field_info(param), "embed", True)
model_name = "Body_" + name
BodyModel = create_model(model_name)
for f in flat_dependant.body_params:

View File

@@ -498,7 +498,12 @@ class APIRouter(routing.Router):
def add_api_websocket_route(
self, path: str, endpoint: Callable, name: str = None
) -> None:
route = APIWebSocketRoute(path, endpoint=endpoint, name=name)
route = APIWebSocketRoute(
path,
endpoint=endpoint,
name=name,
dependency_overrides_provider=self.dependency_overrides_provider,
)
self.routes.append(route)
def websocket(self, path: str, name: str = None) -> Callable:

View File

@@ -131,17 +131,26 @@ def create_response_field(
)
def create_cloned_field(field: ModelField) -> ModelField:
def create_cloned_field(
field: ModelField, *, cloned_types: Dict[Type[BaseModel], Type[BaseModel]] = None,
) -> ModelField:
# _cloned_types has already cloned types, to support recursive models
if cloned_types is None:
cloned_types = dict()
original_type = field.type_
if is_dataclass(original_type) and hasattr(original_type, "__pydantic_model__"):
original_type = original_type.__pydantic_model__ # type: ignore
use_type = original_type
if lenient_issubclass(original_type, BaseModel):
original_type = cast(Type[BaseModel], original_type)
use_type = create_model(original_type.__name__, __base__=original_type)
for f in original_type.__fields__.values():
use_type.__fields__[f.name] = create_cloned_field(f)
use_type = cloned_types.get(original_type)
if use_type is None:
use_type = create_model(original_type.__name__, __base__=original_type)
cloned_types[original_type] = use_type
for f in original_type.__fields__.values():
use_type.__fields__[f.name] = create_cloned_field(
f, cloned_types=cloned_types
)
new_field = create_response_field(name=field.name, type_=use_type)
new_field.has_alias = field.has_alias
new_field.alias = field.alias
@@ -157,10 +166,13 @@ def create_cloned_field(field: ModelField) -> ModelField:
new_field.validate_always = field.validate_always
if field.sub_fields:
new_field.sub_fields = [
create_cloned_field(sub_field) for sub_field in field.sub_fields
create_cloned_field(sub_field, cloned_types=cloned_types)
for sub_field in field.sub_fields
]
if field.key_field:
new_field.key_field = create_cloned_field(field.key_field)
new_field.key_field = create_cloned_field(
field.key_field, cloned_types=cloned_types
)
new_field.validators = field.validators
if PYDANTIC_1:
new_field.pre_validators = field.pre_validators

View File

@@ -58,7 +58,6 @@ test = [
"async_generator",
"python-multipart",
"aiofiles",
"ujson",
"flask"
]
doc = [

View File

@@ -22,6 +22,10 @@ missing_translation_snippet = """
docs_path = Path("docs")
def get_lang_paths():
return sorted(docs_path.iterdir())
def lang_callback(lang: Optional[str]):
if lang is None:
return
@@ -34,7 +38,7 @@ def lang_callback(lang: Optional[str]):
def complete_existing_lang(incomplete: str):
lang_path: Path
for lang_path in docs_path.iterdir():
for lang_path in get_lang_paths():
if lang_path.is_dir() and lang_path.name.startswith(incomplete):
yield lang_path.name
@@ -53,7 +57,7 @@ def new_lang(lang: str = typer.Argument(..., callback=lang_callback)):
new_path.mkdir()
en_docs_path = Path("docs/en")
en_config_path: Path = en_docs_path / mkdocs_name
en_config: dict = mkdocs.utils.yaml_load(en_config_path.read_text())
en_config: dict = mkdocs.utils.yaml_load(en_config_path.read_text(encoding="utf-8"))
fastapi_url_base = "https://fastapi.tiangolo.com/"
new_config = {}
new_config["site_name"] = en_config["site_name"]
@@ -90,14 +94,16 @@ def new_lang(lang: str = typer.Argument(..., callback=lang_callback)):
extra_js.append(fastapi_url_base + js)
new_config["extra_javascript"] = extra_js
new_config_path: Path = Path(new_path) / mkdocs_name
new_config_path.write_text(yaml.dump(new_config, sort_keys=False, width=200))
new_config_path.write_text(
yaml.dump(new_config, sort_keys=False, width=200), encoding="utf-8"
)
new_config_docs_path: Path = new_path / "docs"
new_config_docs_path.mkdir()
en_index_path: Path = en_docs_path / "docs" / "index.md"
new_index_path: Path = new_config_docs_path / "index.md"
en_index_content = en_index_path.read_text()
en_index_content = en_index_path.read_text(encoding="utf-8")
new_index_content = f"{missing_translation_snippet}\n\n{en_index_content}"
new_index_path.write_text(new_index_content)
new_index_path.write_text(new_index_content, encoding="utf-8")
typer.secho(f"Successfully initialized: {new_path}", color=typer.colors.GREEN)
update_languages(lang=None)
@@ -128,10 +134,12 @@ def build_lang(
shutil.rmtree(build_lang_path, ignore_errors=True)
shutil.copytree(lang_path, build_lang_path)
en_config_path: Path = en_lang_path / mkdocs_name
en_config: dict = mkdocs.utils.yaml_load(en_config_path.read_text())
en_config: dict = mkdocs.utils.yaml_load(en_config_path.read_text(encoding="utf-8"))
nav = en_config["nav"]
lang_config_path: Path = lang_path / mkdocs_name
lang_config: dict = mkdocs.utils.yaml_load(lang_config_path.read_text())
lang_config: dict = mkdocs.utils.yaml_load(
lang_config_path.read_text(encoding="utf-8")
)
lang_nav = lang_config["nav"]
# Exclude first 2 entries FastAPI and Languages, for custom handling
use_nav = nav[2:]
@@ -146,9 +154,9 @@ def build_lang(
en_file_path: Path = en_lang_path / "docs" / file_path
lang_file_path.parent.mkdir(parents=True, exist_ok=True)
if not lang_file_path.is_file():
en_text = en_file_path.read_text()
en_text = en_file_path.read_text(encoding="utf-8")
lang_text = get_text_with_translate_missing(en_text)
lang_file_path.write_text(lang_text)
lang_file_path.write_text(lang_text, encoding="utf-8")
file_key = file_to_nav[file]
use_lang_file_to_nav[file] = file_key
if file_key:
@@ -171,7 +179,7 @@ def build_lang(
lang_config["nav"] = export_lang_nav
build_lang_config_path: Path = build_lang_path / mkdocs_name
build_lang_config_path.write_text(
yaml.dump(lang_config, sort_keys=False, width=200)
yaml.dump(lang_config, sort_keys=False, width=200), encoding="utf-8"
)
current_dir = os.getcwd()
os.chdir(build_lang_path)
@@ -194,7 +202,7 @@ def build_all():
typer.echo(f"Building docs for: en")
mkdocs.commands.build.build(mkdocs.config.load_config(site_dir=str(site_path)))
os.chdir(current_dir)
for lang in docs_path.iterdir():
for lang in get_lang_paths():
if lang == en_build_path or not lang.is_dir():
continue
build_lang(lang.name)
@@ -216,7 +224,7 @@ def update_languages(
mkdocs.yml files (for all the languages).
"""
if lang is None:
for lang_path in docs_path.iterdir():
for lang_path in get_lang_paths():
if lang_path.is_dir():
typer.echo(f"Updating {lang_path.name}")
update_config(lang_path.name)
@@ -272,15 +280,17 @@ def live(
def update_config(lang: str):
lang_path: Path = docs_path / lang
config_path = lang_path / mkdocs_name
config: dict = mkdocs.utils.yaml_load(config_path.read_text())
config: dict = mkdocs.utils.yaml_load(config_path.read_text(encoding="utf-8"))
languages = [{"en": "/"}]
for lang in docs_path.iterdir():
for lang in get_lang_paths():
if lang.name == "en" or not lang.is_dir():
continue
name = lang.name
languages.append({name: f"/{name}/"})
config["nav"][1] = {"Languages": languages}
config_path.write_text(yaml.dump(config, sort_keys=False, width=200))
config_path.write_text(
yaml.dump(config, sort_keys=False, width=200), encoding="utf-8"
)
def get_key_section(

View File

@@ -0,0 +1,232 @@
from typing import List
from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel
app = FastAPI()
client = TestClient(app)
class Item(BaseModel):
data: str
def duplicate_dependency(item: Item):
return item
def dependency(item2: Item):
return item2
def sub_duplicate_dependency(
item: Item, sub_item: Item = Depends(duplicate_dependency)
):
return [item, sub_item]
@app.post("/with-duplicates")
async def with_duplicates(item: Item, item2: Item = Depends(duplicate_dependency)):
return [item, item2]
@app.post("/no-duplicates")
async def no_duplicates(item: Item, item2: Item = Depends(dependency)):
return [item, item2]
@app.post("/with-duplicates-sub")
async def no_duplicates_sub(
item: Item, sub_items: List[Item] = Depends(sub_duplicate_dependency)
):
return [item, sub_items]
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/with-duplicates": {
"post": {
"summary": "With Duplicates",
"operationId": "with_duplicates_with_duplicates_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"
}
}
},
},
},
}
},
"/no-duplicates": {
"post": {
"summary": "No Duplicates",
"operationId": "no_duplicates_no_duplicates_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Body_no_duplicates_no_duplicates_post"
}
}
},
"required": True,
},
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
"/with-duplicates-sub": {
"post": {
"summary": "No Duplicates Sub",
"operationId": "no_duplicates_sub_with_duplicates_sub_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"
}
}
},
},
},
}
},
},
"components": {
"schemas": {
"Body_no_duplicates_no_duplicates_post": {
"title": "Body_no_duplicates_no_duplicates_post",
"required": ["item", "item2"],
"type": "object",
"properties": {
"item": {"$ref": "#/components/schemas/Item"},
"item2": {"$ref": "#/components/schemas/Item"},
},
},
"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"}},
},
"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
assert response.json() == openapi_schema
def test_no_duplicates_invalid():
response = client.post("/no-duplicates", json={"item": {"data": "myitem"}})
assert response.status_code == 422
assert response.json() == {
"detail": [
{
"loc": ["body", "item2"],
"msg": "field required",
"type": "value_error.missing",
}
]
}
def test_no_duplicates():
response = client.post(
"/no-duplicates",
json={"item": {"data": "myitem"}, "item2": {"data": "myitem2"}},
)
assert response.status_code == 200
assert response.json() == [{"data": "myitem"}, {"data": "myitem2"}]
def test_duplicates():
response = client.post("/with-duplicates", json={"data": "myitem"})
assert response.status_code == 200
assert response.json() == [{"data": "myitem"}, {"data": "myitem"}]
def test_sub_duplicates():
response = client.post("/with-duplicates-sub", json={"data": "myitem"})
assert response.status_code == 200
assert response.json() == [
{"data": "myitem"},
[{"data": "myitem"}, {"data": "myitem"}],
]

View File

@@ -15,4 +15,4 @@ def test_get_timed():
response = client.get("/timed")
assert response.json() == {"message": "It's the time of my life"}
assert "X-Response-Time" in response.headers
assert float(response.headers["X-Response-Time"]) > 0
assert float(response.headers["X-Response-Time"]) >= 0

View File

@@ -1,36 +0,0 @@
from fastapi.testclient import TestClient
from custom_response.tutorial001 import app
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Items",
"operationId": "read_items_items__get",
}
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200
assert response.json() == openapi_schema
def test_get_custom_response():
response = client.get("/items/")
assert response.status_code == 200
assert response.json() == [{"item_id": "Foo"}]

View File

@@ -0,0 +1,80 @@
from typing import List
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel
app = FastAPI()
class RecursiveItem(BaseModel):
sub_items: List["RecursiveItem"] = []
name: str
RecursiveItem.update_forward_refs()
class RecursiveSubitemInSubmodel(BaseModel):
sub_items2: List["RecursiveItemViaSubmodel"] = []
name: str
class RecursiveItemViaSubmodel(BaseModel):
sub_items1: List[RecursiveSubitemInSubmodel] = []
name: str
RecursiveSubitemInSubmodel.update_forward_refs()
@app.get("/items/recursive", response_model=RecursiveItem)
def get_recursive():
return {"name": "item", "sub_items": [{"name": "subitem", "sub_items": []}]}
@app.get("/items/recursive-submodel", response_model=RecursiveItemViaSubmodel)
def get_recursive_submodel():
return {
"name": "item",
"sub_items1": [
{
"name": "subitem",
"sub_items2": [
{
"name": "subsubitem",
"sub_items1": [{"name": "subsubsubitem", "sub_items2": []}],
}
],
}
],
}
client = TestClient(app)
def test_recursive():
response = client.get("/items/recursive")
assert response.status_code == 200
assert response.json() == {
"sub_items": [{"name": "subitem", "sub_items": []}],
"name": "item",
}
response = client.get("/items/recursive-submodel")
assert response.status_code == 200
assert response.json() == {
"name": "item",
"sub_items1": [
{
"name": "subitem",
"sub_items2": [
{
"name": "subsubitem",
"sub_items1": [{"name": "subsubsubitem", "sub_items2": []}],
}
],
}
],
}

View File

@@ -1,4 +1,4 @@
from fastapi import APIRouter, FastAPI, WebSocket
from fastapi import APIRouter, Depends, FastAPI, WebSocket
from fastapi.testclient import TestClient
router = APIRouter()
@@ -34,6 +34,19 @@ async def routerindex(websocket: WebSocket):
await websocket.close()
async def ws_dependency():
return "Socket Dependency"
@router.websocket("/router-ws-depends/")
async def router_ws_decorator_depends(
websocket: WebSocket, data=Depends(ws_dependency)
):
await websocket.accept()
await websocket.send_text(data)
await websocket.close()
app.include_router(router)
app.include_router(prefix_router, prefix="/prefix")
@@ -64,3 +77,16 @@ def test_router2():
with client.websocket_connect("/router2") as websocket:
data = websocket.receive_text()
assert data == "Hello, router!"
def test_router_ws_depends():
client = TestClient(app)
with client.websocket_connect("/router-ws-depends/") as websocket:
assert websocket.receive_text() == "Socket Dependency"
def test_router_ws_depends_with_override():
client = TestClient(app)
app.dependency_overrides[ws_dependency] = lambda: "Override"
with client.websocket_connect("/router-ws-depends/") as websocket:
assert websocket.receive_text() == "Override"