mirror of
https://github.com/fastapi/fastapi.git
synced 2025-12-28 08:40:21 -05:00
Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b87072bc12 | ||
|
|
04e2bfafbc | ||
|
|
181a32236a | ||
|
|
1f54a8e0a1 | ||
|
|
d63475bb7d | ||
|
|
5a3c5f1523 | ||
|
|
12bc9285f7 | ||
|
|
31df2ea940 | ||
|
|
50b90dd6a4 | ||
|
|
7dd881334d | ||
|
|
530fc8ff3f | ||
|
|
ef460b4d23 | ||
|
|
b591de2ace | ||
|
|
34c857b7cb | ||
|
|
c78bc0c82d | ||
|
|
194446e51a | ||
|
|
777e2151e6 | ||
|
|
5ce5bdba0b | ||
|
|
e4300769ac | ||
|
|
c6dd627bdd | ||
|
|
6576f724bb | ||
|
|
91a6736d0e | ||
|
|
8fb755703d | ||
|
|
748bedd37c | ||
|
|
bf58788f29 | ||
|
|
5f78ba4a31 | ||
|
|
db9f827263 | ||
|
|
dd9e94cf21 | ||
|
|
e482d74241 | ||
|
|
bd2acbcabb | ||
|
|
f913d469a8 | ||
|
|
66cb266641 | ||
|
|
74954894c5 | ||
|
|
ceedfccde0 | ||
|
|
2ee0eedf23 | ||
|
|
c0f3019764 | ||
|
|
dd6d0cb23c | ||
|
|
fe15620df3 | ||
|
|
6af857f206 | ||
|
|
7ce756f9dd | ||
|
|
c0b1fddb31 | ||
|
|
9aea85a84e | ||
|
|
fddd1c12de | ||
|
|
b13a4baf32 | ||
|
|
5ffa18f10f | ||
|
|
828915baf5 | ||
|
|
a071ddf3cd | ||
|
|
3651b8a30f | ||
|
|
0d73b9ff1c | ||
|
|
43235cf236 | ||
|
|
269a155583 | ||
|
|
12433d51dd | ||
|
|
3699e17212 | ||
|
|
8231fbede4 | ||
|
|
50bc14b835 | ||
|
|
4310c89c83 | ||
|
|
d39dd06a22 | ||
|
|
a0ab47e89e | ||
|
|
5cbcb9a965 | ||
|
|
801ceaec80 | ||
|
|
c7334ae9f8 | ||
|
|
d737599a2c | ||
|
|
d2d72a8e4a | ||
|
|
7895c12fa1 | ||
|
|
5f6a14c413 | ||
|
|
2b4e88fa98 | ||
|
|
11723bca27 | ||
|
|
b49517a64f | ||
|
|
f910e0c96c | ||
|
|
c1ba2a3127 | ||
|
|
28396173c7 | ||
|
|
69974b792e | ||
|
|
352412a3cb | ||
|
|
745ab48d65 | ||
|
|
4a5cda0d77 | ||
|
|
b90bf2da9e | ||
|
|
a552cbdf59 | ||
|
|
2351fb5623 | ||
|
|
807522c616 | ||
|
|
81a529c251 | ||
|
|
7efc15aeef | ||
|
|
d66d8379c0 | ||
|
|
5a00467951 | ||
|
|
434d32b891 | ||
|
|
535247ffc4 | ||
|
|
7e2518350a | ||
|
|
1b2a7546af | ||
|
|
2d9bb64047 |
@@ -203,6 +203,21 @@ File responses will include appropriate `Content-Length`, `Last-Modified` and `E
|
||||
{!../../../docs_src/custom_response/tutorial009.py!}
|
||||
```
|
||||
|
||||
## Default response class
|
||||
|
||||
When creating a **FastAPI** class instance or an `APIRouter` you can specify which response class to use by default.
|
||||
|
||||
The parameter that defines this is `default_response_class`.
|
||||
|
||||
In the example below, **FastAPI** will use `ORJSONResponse` by default, in all *path operations*, instead of `JSONResponse`.
|
||||
|
||||
```Python hl_lines="2 4"
|
||||
{!../../../docs_src/custom_response/tutorial010.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
You can still override `response_class` in *path operations* as before.
|
||||
|
||||
## Additional documentation
|
||||
|
||||
You can also declare the media type and many other details in OpenAPI using `responses`: [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||
|
||||
@@ -4,6 +4,9 @@ You can define event handlers (functions) that need to be executed before the ap
|
||||
|
||||
These functions can be declared with `async def` or normal `def`.
|
||||
|
||||
!!! warning
|
||||
Only event handlers for the main application will be executed, not for [Sub Applications - Mounts](./sub-applications.md){.internal-link target=_blank}.
|
||||
|
||||
## `startup` event
|
||||
|
||||
To add a function that should be run before the application starts, declare it with the event `"startup"`:
|
||||
@@ -41,4 +44,4 @@ Here, the `shutdown` event handler function will write a text line `"Application
|
||||
So, we declare the event handler function with standard `def` instead of `async def`.
|
||||
|
||||
!!! info
|
||||
You can read more about these event handlers in <a href="https://www.starlette.io/events/" class="external-link" target="_blank">Starlette's Events' docs</a>.
|
||||
You can read more about these event handlers in <a href="https://www.starlette.io/events/" class="external-link" target="_blank">Starlette's Events' docs</a>.
|
||||
|
||||
@@ -51,38 +51,7 @@ In your WebSocket route you can `await` for messages and send messages.
|
||||
|
||||
You can receive and send binary, text, and JSON data.
|
||||
|
||||
## Using `Depends` and others
|
||||
|
||||
In WebSocket endpoints you can import from `fastapi` and use:
|
||||
|
||||
* `Depends`
|
||||
* `Security`
|
||||
* `Cookie`
|
||||
* `Header`
|
||||
* `Path`
|
||||
* `Query`
|
||||
|
||||
They work the same way as for other FastAPI endpoints/*path operations*:
|
||||
|
||||
```Python hl_lines="53 54 55 56 57 58 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76"
|
||||
{!../../../docs_src/websockets/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
In a WebSocket it doesn't really make sense to raise an `HTTPException`. So it's better to close the WebSocket connection directly.
|
||||
|
||||
You can use a closing code from the <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">valid codes defined in the specification</a>.
|
||||
|
||||
In the future, there will be a `WebSocketException` that you will be able to `raise` from anywhere, and add exception handlers for it. It depends on the <a href="https://github.com/encode/starlette/pull/527" class="external-link" target="_blank">PR #527</a> in Starlette.
|
||||
|
||||
## More info
|
||||
|
||||
To learn more about the options, check Starlette's documentation for:
|
||||
|
||||
* <a href="https://www.starlette.io/websockets/" class="external-link" target="_blank">The `WebSocket` class</a>.
|
||||
* <a href="https://www.starlette.io/endpoints/#websocketendpoint" class="external-link" target="_blank">Class-based WebSocket handling</a>.
|
||||
|
||||
## Test it
|
||||
## Try it
|
||||
|
||||
If your file is named `main.py`, run your application with:
|
||||
|
||||
@@ -115,3 +84,62 @@ You can send (and receive) many messages:
|
||||
<img src="/img/tutorial/websockets/image04.png">
|
||||
|
||||
And all of them will use the same WebSocket connection.
|
||||
|
||||
## Using `Depends` and others
|
||||
|
||||
In WebSocket endpoints you can import from `fastapi` and use:
|
||||
|
||||
* `Depends`
|
||||
* `Security`
|
||||
* `Cookie`
|
||||
* `Header`
|
||||
* `Path`
|
||||
* `Query`
|
||||
|
||||
They work the same way as for other FastAPI endpoints/*path operations*:
|
||||
|
||||
```Python hl_lines="56 57 58 59 60 61 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79"
|
||||
{!../../../docs_src/websockets/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
In a WebSocket it doesn't really make sense to raise an `HTTPException`. So it's better to close the WebSocket connection directly.
|
||||
|
||||
You can use a closing code from the <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">valid codes defined in the specification</a>.
|
||||
|
||||
In the future, there will be a `WebSocketException` that you will be able to `raise` from anywhere, and add exception handlers for it. It depends on the <a href="https://github.com/encode/starlette/pull/527" class="external-link" target="_blank">PR #527</a> in Starlette.
|
||||
|
||||
### Try the WebSockets with dependencies
|
||||
|
||||
If your file is named `main.py`, run your application 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)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Open your browser at <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
|
||||
|
||||
There you can set:
|
||||
|
||||
* The "Item ID", used in the path.
|
||||
* The "Token" used as a query parameter.
|
||||
|
||||
!!! tip
|
||||
Notice that the query `token` will be handled by a dependency.
|
||||
|
||||
With that you can connect the WebSocket and then send and receive messages:
|
||||
|
||||
<img src="/img/tutorial/websockets/image05.png">
|
||||
|
||||
## More info
|
||||
|
||||
To learn more about the options, check Starlette's documentation for:
|
||||
|
||||
* <a href="https://www.starlette.io/websockets/" class="external-link" target="_blank">The `WebSocket` class</a>.
|
||||
* <a href="https://www.starlette.io/endpoints/#websocketendpoint" class="external-link" target="_blank">Class-based WebSocket handling</a>.
|
||||
|
||||
@@ -25,8 +25,6 @@ Here's an incomplete list of some of them.
|
||||
|
||||
* <a href="https://medium.com/data-rebels/fastapi-authentication-revisited-enabling-api-key-authentication-122dc5975680" class="external-link" target="_blank">FastAPI authentication revisited: Enabling API key authentication</a> by <a href="https://medium.com/@nils_29588" class="external-link" target="_blank">Nils de Bruin</a>.
|
||||
|
||||
* <a href="https://blog.bartab.fr/fastapi-logging-on-the-fly/" class="external-link" target="_blank">FastAPI, a simple use case on logging</a> by <a href="https://blog.bartab.fr/" class="external-link" target="_blank">@euri10</a>.
|
||||
|
||||
* <a href="https://medium.com/@nico.axtmann95/deploying-a-scikit-learn-model-with-onnx-und-fastapi-1af398268915" class="external-link" target="_blank">Deploying a scikit-learn model with ONNX and FastAPI</a> by <a href="https://www.linkedin.com/in/nico-axtmann" class="external-link" target="_blank">Nico Axtmann</a>.
|
||||
|
||||
* <a href="https://geekflare.com/python-asynchronous-web-frameworks/" class="external-link" target="_blank">Top 5 Asynchronous Web Frameworks for Python</a> by <a href="https://geekflare.com/author/ankush/" class="external-link" target="_blank">Ankush Thakur</a> on <a href="https://geekflare.com" class="external-link" target="_blank">GeekFlare</a>.
|
||||
@@ -104,12 +102,6 @@ Here's an incomplete list of some of them.
|
||||
|
||||
* <a href="https://qiita.com/bee2/items/75d9c0d7ba20e7a4a0e9" class="external-link" target="_blank">[FastAPI] Python製のASGI Web フレームワーク FastAPIに入門する</a> by <a href="https://qiita.com/bee2" class="external-link" target="_blank">@bee2</a>.
|
||||
|
||||
### Chinese
|
||||
|
||||
* <a href="https://cloud.tencent.com/developer/article/1431448" class="external-link" target="_blank">使用FastAPI框架快速构建高性能的api服务</a> by <a href="https://cloud.tencent.com/developer/user/5471722" class="external-link" target="_blank">逍遥散人</a>.
|
||||
|
||||
* <a href="https://wxq0309.github.io/" class="external-link" target="_blank">FastAPI框架中文文档</a> by <a href="https://wxq0309.github.io/" class="external-link" target="_blank">何大仙</a>.
|
||||
|
||||
### Vietnamese
|
||||
|
||||
* <a href="https://fullstackstation.com/fastapi-trien-khai-bang-docker/" class="external-link" target="_blank">FASTAPI: TRIỂN KHAI BẰNG DOCKER</a> by <a href="https://fullstackstation.com/author/figonking/" class="external-link" target="_blank">Nguyễn Nhân</a>.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 71 KiB |
BIN
docs/en/docs/img/tutorial/metadata/image02.png
Normal file
BIN
docs/en/docs/img/tutorial/metadata/image02.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
BIN
docs/en/docs/img/tutorial/websockets/image05.png
Normal file
BIN
docs/en/docs/img/tutorial/websockets/image05.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
@@ -69,3 +69,16 @@ You can read more about it in the docs for the repo.
|
||||
## Full Stack FastAPI MongoDB
|
||||
|
||||
...might come later, depending on my time availability and other factors. 😅 🎉
|
||||
|
||||
## Machine Learning models with spaCy and FastAPI
|
||||
|
||||
GitHub: <a href="https://github.com/microsoft/cookiecutter-spacy-fastapi" class="external-link" target="_blank">https://github.com/microsoft/cookiecutter-spacy-fastapi</a>
|
||||
|
||||
### Machine Learning models with spaCy and FastAPI - Features
|
||||
|
||||
* **spaCy** NER model integration.
|
||||
* **Azure Cognitive Search** request format built in.
|
||||
* **Production ready** Python web server using Uvicorn and Gunicorn.
|
||||
* **Azure DevOps** Kubernetes (AKS) CI/CD deployment built in.
|
||||
* **Multilingual** Easily choose one of spaCy's built in languages during project setup.
|
||||
* **Easily extensible** to other model frameworks (Pytorch, Tensorflow), not just spaCy.
|
||||
|
||||
@@ -144,15 +144,15 @@ You can use, for example:
|
||||
{!../../../docs_src/python_types/tutorial005.py!}
|
||||
```
|
||||
|
||||
### Types with subtypes
|
||||
### Generic types with type parameters
|
||||
|
||||
There are some data structures that can contain other values, like `dict`, `list`, `set` and `tuple`. And the internal values can have their own type too.
|
||||
|
||||
To declare those types and the subtypes, you can use the standard Python module `typing`.
|
||||
To declare those types and the internal types, you can use the standard Python module `typing`.
|
||||
|
||||
It exists specifically to support these type hints.
|
||||
|
||||
#### Lists
|
||||
#### `List`
|
||||
|
||||
For example, let's define a variable to be a `list` of `str`.
|
||||
|
||||
@@ -166,25 +166,30 @@ Declare the variable, with the same colon (`:`) syntax.
|
||||
|
||||
As the type, put the `List`.
|
||||
|
||||
As the list is a type that takes a "subtype", you put the subtype in square brackets:
|
||||
As the list is a type that contains some internal types, you put them in square brackets:
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!../../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Those internal types in the square brackets are called "type parameters".
|
||||
|
||||
In this case, `str` is the type parameter passed to `List`.
|
||||
|
||||
That means: "the variable `items` is a `list`, and each of the items in this list is a `str`".
|
||||
|
||||
By doing that, your editor can provide support even while processing items from the list.
|
||||
|
||||
Without types, that's almost impossible to achieve:
|
||||
By doing that, your editor can provide support even while processing items from the list:
|
||||
|
||||
<img src="/img/python-types/image05.png">
|
||||
|
||||
Without types, that's almost impossible to achieve.
|
||||
|
||||
Notice that the variable `item` is one of the elements in the list `items`.
|
||||
|
||||
And still, the editor knows it is a `str`, and provides support for that.
|
||||
|
||||
#### Tuples and Sets
|
||||
#### `Tuple` and `Set`
|
||||
|
||||
You would do the same to declare `tuple`s and `set`s:
|
||||
|
||||
@@ -197,13 +202,13 @@ This means:
|
||||
* The variable `items_t` is a `tuple` with 3 items, an `int`, another `int`, and a `str`.
|
||||
* The variable `items_s` is a `set`, and each of its items is of type `bytes`.
|
||||
|
||||
#### Dicts
|
||||
#### `Dict`
|
||||
|
||||
To define a `dict`, you pass 2 subtypes, separated by commas.
|
||||
To define a `dict`, you pass 2 type parameters, separated by commas.
|
||||
|
||||
The first subtype is for the keys of the `dict`.
|
||||
The first type parameter is for the keys of the `dict`.
|
||||
|
||||
The second subtype is for the values of the `dict`:
|
||||
The second type parameter is for the values of the `dict`:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial008.py!}
|
||||
@@ -215,6 +220,29 @@ This means:
|
||||
* The keys of this `dict` are of type `str` (let's say, the name of each item).
|
||||
* The values of this `dict` are of type `float` (let's say, the price of each item).
|
||||
|
||||
#### `Optional`
|
||||
|
||||
You can also use `Optional` to declare that a variable has a type, like `str`, but that it is "optional", which means that it could also be `None`:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
Using `Optional[str]` instead of just `str` will let the editor help you detecting errors where you could be assuming that a value is always a `str`, when it could actually be `None` too.
|
||||
|
||||
#### Generic types
|
||||
|
||||
These types that take type parameters in square brackets, like:
|
||||
|
||||
* `List`
|
||||
* `Tuple`
|
||||
* `Set`
|
||||
* `Dict`
|
||||
* `Optional`
|
||||
* ...and others.
|
||||
|
||||
are called **Generic types** or **Generics**.
|
||||
|
||||
### Classes as types
|
||||
|
||||
You can also declare a class as the type of a variable.
|
||||
@@ -222,13 +250,13 @@ You can also declare a class as the type of a variable.
|
||||
Let's say you have a class `Person`, with a name:
|
||||
|
||||
```Python hl_lines="1 2 3"
|
||||
{!../../../docs_src/python_types/tutorial009.py!}
|
||||
{!../../../docs_src/python_types/tutorial010.py!}
|
||||
```
|
||||
|
||||
Then you can declare a variable to be of type `Person`:
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!../../../docs_src/python_types/tutorial009.py!}
|
||||
{!../../../docs_src/python_types/tutorial010.py!}
|
||||
```
|
||||
|
||||
And then, again, you get all the editor support:
|
||||
@@ -250,7 +278,7 @@ And you get all the editor support with that resulting object.
|
||||
Taken from the official Pydantic docs:
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/python_types/tutorial010.py!}
|
||||
{!../../../docs_src/python_types/tutorial011.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
||||
@@ -2,6 +2,57 @@
|
||||
|
||||
## Latest changes
|
||||
|
||||
## 0.58.0
|
||||
|
||||
* Deep merge OpenAPI responses to preserve all the additional metadata. PR [#1577](https://github.com/tiangolo/fastapi/pull/1577).
|
||||
* Mention in docs that only main app events are run (not sub-apps). PR [#1554](https://github.com/tiangolo/fastapi/pull/1554) by [@amacfie](https://github.com/amacfie).
|
||||
* Fix body validation error response, do not include body variable when it is not embedded. PR [#1553](https://github.com/tiangolo/fastapi/pull/1553) by [@amacfie](https://github.com/amacfie).
|
||||
* Fix testing OAuth2 security scopes when using dependency overrides. PR [#1549](https://github.com/tiangolo/fastapi/pull/1549) by [@amacfie](https://github.com/amacfie).
|
||||
* Fix Model for JSON Schema keyword `not` as a JSON Schema instead of a list. PR [#1548](https://github.com/tiangolo/fastapi/pull/1548) by [@v-do](https://github.com/v-do).
|
||||
* Add support for OpenAPI `servers`. PR [#1547](https://github.com/tiangolo/fastapi/pull/1547) by [@mikaello](https://github.com/mikaello).
|
||||
|
||||
## 0.57.0
|
||||
|
||||
* Remove broken link from "External Links". PR [#1565](https://github.com/tiangolo/fastapi/pull/1565) by [@victorphoenix3](https://github.com/victorphoenix3).
|
||||
* Update/fix docs for [WebSockets with dependencies](https://fastapi.tiangolo.com/advanced/websockets/#using-depends-and-others). Original PR [#1540](https://github.com/tiangolo/fastapi/pull/1540) by [@ChihSeanHsu](https://github.com/ChihSeanHsu).
|
||||
* Add support for Python's `http.HTTPStatus` in `status_code` parameters. PR [#1534](https://github.com/tiangolo/fastapi/pull/1534) by [@retnikt](https://github.com/retnikt).
|
||||
* When using Pydantic models with `__root__`, use the internal value in `jsonable_encoder`. PR [#1524](https://github.com/tiangolo/fastapi/pull/1524) by [@patrickkwang](https://github.com/patrickkwang).
|
||||
* Update docs for path parameters. PR [#1521](https://github.com/tiangolo/fastapi/pull/1521) by [@yankeexe](https://github.com/yankeexe).
|
||||
* Update docs for first steps, links and rewording. PR [#1518](https://github.com/tiangolo/fastapi/pull/1518) by [@yankeexe](https://github.com/yankeexe).
|
||||
* Enable `showCommonExtensions` in Swagger UI to show additional validations like `maxLength`, etc. PR [#1466](https://github.com/tiangolo/fastapi/pull/1466) by [@TiewKH](https://github.com/TiewKH).
|
||||
* Make `OAuth2PasswordRequestFormStrict` importable directly from `fastapi.security`. PR [#1462](https://github.com/tiangolo/fastapi/pull/1462) by [@RichardHoekstra](https://github.com/RichardHoekstra).
|
||||
* Add docs about [Default response class](https://fastapi.tiangolo.com/advanced/custom-response/#default-response-class). PR [#1455](https://github.com/tiangolo/fastapi/pull/1455) by [@TezRomacH](https://github.com/TezRomacH).
|
||||
* Add note in docs about additional parameters `response_model_exclude_defaults` and `response_model_exclude_none` in [Response Model](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). PR [#1427](https://github.com/tiangolo/fastapi/pull/1427) by [@wshayes](https://github.com/wshayes).
|
||||
* Add note about [PyCharm Pydantic plugin](https://github.com/koxudaxi/pydantic-pycharm-plugin) to docs. PR [#1420](https://github.com/tiangolo/fastapi/pull/1420) by [@koxudaxi](https://github.com/koxudaxi).
|
||||
* Update and clarify testing function name. PR [#1395](https://github.com/tiangolo/fastapi/pull/1395) by [@chenl](https://github.com/chenl).
|
||||
* Fix duplicated headers created by indirect dependencies that use the request directly. PR [#1386](https://github.com/tiangolo/fastapi/pull/1386) by [@obataku](https://github.com/obataku) from tests by [@scottsmith2gmail](https://github.com/scottsmith2gmail).
|
||||
* Upgrade Starlette version to `0.13.4`. PR [#1361](https://github.com/tiangolo/fastapi/pull/1361) by [@rushton](https://github.com/rushton).
|
||||
* Improve error handling and feedback for requests with invalid JSON. PR [#1354](https://github.com/tiangolo/fastapi/pull/1354) by [@aviramha](https://github.com/aviramha).
|
||||
* Add support for declaring metadata for tags in OpenAPI. New docs at [Tutorial - Metadata and Docs URLs - Metadata for tags](https://fastapi.tiangolo.com/tutorial/metadata/#metadata-for-tags). PR [#1348](https://github.com/tiangolo/fastapi/pull/1348) by [@thomas-maschler](https://github.com/thomas-maschler).
|
||||
* Add basic setup for Russian translations. PR [#1566](https://github.com/tiangolo/fastapi/pull/1566).
|
||||
* Remove obsolete Chinese articles after adding official community translations. PR [#1510](https://github.com/tiangolo/fastapi/pull/1510) by [@waynerv](https://github.com/waynerv).
|
||||
* Add `__repr__` for *path operation function* parameter helpers (like `Query`, `Depends`, etc) to simplify debugging. PR [#1560](https://github.com/tiangolo/fastapi/pull/1560) by [@rkbeatss](https://github.com/rkbeatss) and [@victorphoenix3](https://github.com/victorphoenix3).
|
||||
|
||||
## 0.56.1
|
||||
|
||||
* Add link to advanced docs from tutorial. PR [#1512](https://github.com/tiangolo/fastapi/pull/1512) by [@kx-chen](https://github.com/kx-chen).
|
||||
* Remove internal unnecessary f-strings. PR [#1526](https://github.com/tiangolo/fastapi/pull/1526) by [@kotamatsuoka](https://github.com/kotamatsuoka).
|
||||
* Add translation to Chinese for [Query Parameters and String Validations - 查询参数和字符串校验](https://fastapi.tiangolo.com/zh/tutorial/query-params-str-validations/). PR [#1500](https://github.com/tiangolo/fastapi/pull/1500) by [@waynerv](https://github.com/waynerv).
|
||||
* Add translation to Chinese for [Request Body - 请求体](https://fastapi.tiangolo.com/zh/tutorial/body/). PR [#1492](https://github.com/tiangolo/fastapi/pull/1492) by [@waynerv](https://github.com/waynerv).
|
||||
* Add translation to Chinese for [Help FastAPI - Get Help - 帮助 FastAPI - 获取帮助](https://fastapi.tiangolo.com/zh/help-fastapi/). PR [#1465](https://github.com/tiangolo/fastapi/pull/1465) by [@waynerv](https://github.com/waynerv).
|
||||
* Add translation to Chinese for [Query Parameters - 查询参数](https://fastapi.tiangolo.com/zh/tutorial/query-params/). PR [#1454](https://github.com/tiangolo/fastapi/pull/1454) by [@waynerv](https://github.com/waynerv).
|
||||
* Add translation to Chinese for [Contributing - 开发 - 贡献](https://fastapi.tiangolo.com/zh/contributing/). PR [#1460](https://github.com/tiangolo/fastapi/pull/1460) by [@waynerv](https://github.com/waynerv).
|
||||
* Add translation to Chinese for [Path Parameters - 路径参数](https://fastapi.tiangolo.com/zh/tutorial/path-params/). PR [#1453](https://github.com/tiangolo/fastapi/pull/1453) by [@waynerv](https://github.com/waynerv).
|
||||
* Add official Microsoft project generator for [serving spaCy with FastAPI and Azure Cognitive Skills](https://github.com/microsoft/cookiecutter-spacy-fastapi) to [Project Generators](https://fastapi.tiangolo.com/project-generation/). PR [#1390](https://github.com/tiangolo/fastapi/pull/1390) by [@kabirkhan](https://github.com/kabirkhan).
|
||||
* Update docs in [Python Types Intro](https://fastapi.tiangolo.com/python-types/) to include info about `Optional`. Original PR [#1377](https://github.com/tiangolo/fastapi/pull/1377) by [@yaegassy](https://github.com/yaegassy).
|
||||
* Fix support for callable class dependencies with `yield`. PR [#1365](https://github.com/tiangolo/fastapi/pull/1365) by [@mrosales](https://github.com/mrosales).
|
||||
* Fix/remove incorrect error logging when a client sends invalid payloads. PR [#1351](https://github.com/tiangolo/fastapi/pull/1351) by [@dbanty](https://github.com/dbanty).
|
||||
* Add translation to Chinese for [First Steps - 第一步](https://fastapi.tiangolo.com/zh/tutorial/first-steps/). PR [#1323](https://github.com/tiangolo/fastapi/pull/1323) by [@waynerv](https://github.com/waynerv).
|
||||
* Fix generating OpenAPI for apps using callbacks with routers including Pydantic models. PR [#1322](https://github.com/tiangolo/fastapi/pull/1322) by [@nsidnev](https://github.com/nsidnev).
|
||||
* Optimize internal regex performance in `get_path_param_names()`. PR [#1243](https://github.com/tiangolo/fastapi/pull/1243) by [@heckad](https://github.com/heckad).
|
||||
* Remove `*,` from functions in docs where it's not needed. PR [#1239](https://github.com/tiangolo/fastapi/pull/1239) by [@pankaj-giri](https://github.com/pankaj-giri).
|
||||
* Start translations for Italian. PR [#1557](https://github.com/tiangolo/fastapi/pull/1557) by [@csr](https://github.com/csr).
|
||||
|
||||
## 0.56.0
|
||||
|
||||
* Add support for ASGI `root_path`:
|
||||
|
||||
@@ -108,6 +108,17 @@ But you would get the same editor support with <a href="https://www.jetbrains.co
|
||||
|
||||
<img src="/img/tutorial/body/image05.png">
|
||||
|
||||
!!! tip
|
||||
If you use <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> as your editor, you can use the <a href="https://github.com/koxudaxi/pydantic-pycharm-plugin/" class="external-link" target="_blank">Pydantic PyCharm Plugin</a>.
|
||||
|
||||
It improves editor support for Pydantic models, with:
|
||||
|
||||
* auto-completion
|
||||
* type checks
|
||||
* refactoring
|
||||
* searching
|
||||
* inspections
|
||||
|
||||
## Use the model
|
||||
|
||||
Inside of the function, you can access all the attributes of the model object directly:
|
||||
|
||||
@@ -71,13 +71,13 @@ You will see the alternative automatic documentation (provided by <a href="https
|
||||
|
||||
#### "Schema"
|
||||
|
||||
A "schema" is a definition or description of something. Not the code that implements it, but just the abstract description.
|
||||
A "schema" is a definition or description of something. Not the code that implements it, but just an abstract description.
|
||||
|
||||
#### API "schema"
|
||||
|
||||
In this case, OpenAPI is a specification that dictates how to define a schema of your API.
|
||||
In this case, <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> is a specification that dictates how to define a schema of your API.
|
||||
|
||||
This OpenAPI schema would include your API paths, the possible parameters they take, etc.
|
||||
This schema definition includes your API paths, the possible parameters they take, etc.
|
||||
|
||||
#### Data "schema"
|
||||
|
||||
@@ -91,7 +91,7 @@ OpenAPI defines an API schema for your API. And that schema includes definitions
|
||||
|
||||
#### Check the `openapi.json`
|
||||
|
||||
If you are curious about how the raw OpenAPI schema looks like, it is just an automatically generated JSON with the descriptions of all your API.
|
||||
If you are curious about how the raw OpenAPI schema looks like, FastAPI automatically generates a JSON (schema) with the descriptions of all your API.
|
||||
|
||||
You can see it directly at: <a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a>.
|
||||
|
||||
@@ -120,7 +120,7 @@ It will show a JSON starting with something like:
|
||||
|
||||
#### What is OpenAPI for
|
||||
|
||||
This OpenAPI schema is what powers the 2 interactive documentation systems included.
|
||||
The OpenAPI schema is what powers the two interactive documentation systems included.
|
||||
|
||||
And there are dozens of alternatives, all based on OpenAPI. You could easily add any of those alternatives to your application built with **FastAPI**.
|
||||
|
||||
@@ -139,7 +139,7 @@ You could also use it to generate code automatically, for clients that communica
|
||||
!!! note "Technical Details"
|
||||
`FastAPI` is a class that inherits directly from `Starlette`.
|
||||
|
||||
You can use all the Starlette functionality with `FastAPI` too.
|
||||
You can use all the <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> functionality with `FastAPI` too.
|
||||
|
||||
### Step 2: create a `FastAPI` "instance"
|
||||
|
||||
@@ -202,7 +202,7 @@ https://example.com/items/foo
|
||||
!!! info
|
||||
A "path" is also commonly called an "endpoint" or a "route".
|
||||
|
||||
Building an API, the "path" is the main way to separate "concerns" and "resources".
|
||||
While building an API, the "path" is the main way to separate "concerns" and "resources".
|
||||
|
||||
#### Operation
|
||||
|
||||
@@ -281,7 +281,7 @@ And the more exotic ones:
|
||||
|
||||
The information here is presented as a guideline, not a requirement.
|
||||
|
||||
For example, when using GraphQL you normally perform all the actions using only `post`.
|
||||
For example, when using GraphQL you normally perform all the actions using only `POST` operations.
|
||||
|
||||
### Step 4: define the **path operation function**
|
||||
|
||||
@@ -297,7 +297,7 @@ This is our "**path operation function**":
|
||||
|
||||
This is a Python function.
|
||||
|
||||
It will be called by **FastAPI** whenever it receives a request to the URL "`/`" using `GET`.
|
||||
It will be called by **FastAPI** whenever it receives a request to the URL "`/`" using a `GET` operation.
|
||||
|
||||
In this case, it is an `async` function.
|
||||
|
||||
|
||||
@@ -215,7 +215,6 @@ You will receive a response telling you that the data is invalid containing the
|
||||
{
|
||||
"loc": [
|
||||
"body",
|
||||
"item",
|
||||
"size"
|
||||
],
|
||||
"msg": "value is not a valid integer",
|
||||
|
||||
@@ -21,6 +21,58 @@ With this configuration, the automatic API docs would look like:
|
||||
|
||||
<img src="/img/tutorial/metadata/image01.png">
|
||||
|
||||
## Metadata for tags
|
||||
|
||||
You can also add additional metadata for the different tags used to group your path operations with the parameter `openapi_tags`.
|
||||
|
||||
It takes a list containing one dictionary for each tag.
|
||||
|
||||
Each dictionary can contain:
|
||||
|
||||
* `name` (**required**): a `str` with the same tag name you use in the `tags` parameter in your *path operations* and `APIRouter`s.
|
||||
* `description`: a `str` with a short description for the tag. It can have Markdown and will be shown in the docs UI.
|
||||
* `externalDocs`: a `dict` describing external documentation with:
|
||||
* `description`: a `str` with a short description for the external docs.
|
||||
* `url` (**required**): a `str` with the URL for the external documentation.
|
||||
|
||||
### Create metadata for tags
|
||||
|
||||
Let's try that in an example with tags for `users` and `items`.
|
||||
|
||||
Create metadata for your tags and pass it to the `openapi_tags` parameter:
|
||||
|
||||
```Python hl_lines="3 4 5 6 7 8 9 10 11 12 13 14 15 16 18"
|
||||
{!../../../docs_src/metadata/tutorial004.py!}
|
||||
```
|
||||
|
||||
Notice that you can use Markdown inside of the descriptions, for example "login" will be shown in bold (**login**) and "fancy" will be shown in italics (_fancy_).
|
||||
|
||||
!!! tip
|
||||
You don't have to add metadata for all the tags that you use.
|
||||
|
||||
### Use your tags
|
||||
|
||||
Use the `tags` parameter with your *path operations* (and `APIRouter`s) to assign them to different tags:
|
||||
|
||||
```Python hl_lines="21 26"
|
||||
{!../../../docs_src/metadata/tutorial004.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
Read more about tags in [Path Operation Configuration](../path-operation-configuration/#tags){.internal-link target=_blank}.
|
||||
|
||||
### Check the docs
|
||||
|
||||
Now, if you check the docs, they will show all the additional metadata:
|
||||
|
||||
<img src="/img/tutorial/metadata/image02.png">
|
||||
|
||||
### Order of tags
|
||||
|
||||
The order of each tag metadata dictionary also defines the order shown in the docs UI.
|
||||
|
||||
For example, even though `users` would go after `items` in alphabetical order, it is shown before them, because we added their metadata as the first dictionary in the list.
|
||||
|
||||
## OpenAPI URL
|
||||
|
||||
By default, the OpenAPI schema is served at `/openapi.json`.
|
||||
|
||||
@@ -85,7 +85,7 @@ And when you open your browser at <a href="http://127.0.0.1:8000/docs" class="ex
|
||||
|
||||
And because the generated schema is from the <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md" class="external-link" target="_blank">OpenAPI</a> standard, there are many compatible tools.
|
||||
|
||||
Because of this, **FastAPI** itself provides an alternative API documentation (using ReDoc):
|
||||
Because of this, **FastAPI** itself provides an alternative API documentation (using ReDoc), which you can access at <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>:
|
||||
|
||||
<img src="/img/tutorial/path-params/image02.png">
|
||||
|
||||
@@ -125,7 +125,7 @@ Import `Enum` and create a sub-class that inherits from `str` and from `Enum`.
|
||||
|
||||
By inheriting from `str` the API docs will be able to know that the values must be of type `string` and will be able to render correctly.
|
||||
|
||||
And create class attributes with fixed values, those fixed values will be the available valid values:
|
||||
Then create class attributes with fixed values, which will be the available valid values:
|
||||
|
||||
```Python hl_lines="1 6 7 8 9"
|
||||
{!../../../docs_src/path_params/tutorial005.py!}
|
||||
@@ -147,7 +147,7 @@ Then create a *path parameter* with a type annotation using the enum class you c
|
||||
|
||||
### Check the docs
|
||||
|
||||
Because the available values for the *path parameter* are specified, the interactive docs can show them nicely:
|
||||
Because the available values for the *path parameter* are predefined, the interactive docs can show them nicely:
|
||||
|
||||
<img src="/img/tutorial/path-params/image03.png">
|
||||
|
||||
@@ -167,7 +167,7 @@ You can compare it with the *enumeration member* in your created enum `ModelName
|
||||
|
||||
You can get the actual value (a `str` in this case) using `model_name.value`, or in general, `your_enum_member.value`:
|
||||
|
||||
```Python hl_lines="19"
|
||||
```Python hl_lines="20"
|
||||
{!../../../docs_src/path_params/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -178,12 +178,21 @@ You can get the actual value (a `str` in this case) using `model_name.value`, or
|
||||
|
||||
You can return *enum members* from your *path operation*, even nested in a JSON body (e.g. a `dict`).
|
||||
|
||||
They will be converted to their corresponding values before returning them to the client:
|
||||
They will be converted to their corresponding values (strings in this case) before returning them to the client:
|
||||
|
||||
```Python hl_lines="18 20 21"
|
||||
```Python hl_lines="18 21 23"
|
||||
{!../../../docs_src/path_params/tutorial005.py!}
|
||||
```
|
||||
|
||||
In your client you will get a JSON response like:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"model_name": "alexnet",
|
||||
"message": "Deep Learning FTW!"
|
||||
}
|
||||
```
|
||||
|
||||
## Path parameters containing paths
|
||||
|
||||
Let's say you have a *path operation* with a path `/files/{file_path}`.
|
||||
|
||||
@@ -124,6 +124,14 @@ So, if you send a request to that *path operation* for the item with ID `foo`, t
|
||||
!!! info
|
||||
FastAPI uses Pydantic model's `.dict()` with <a href="https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict" class="external-link" target="_blank">its `exclude_unset` parameter</a> to achieve this.
|
||||
|
||||
!!! info
|
||||
You can also use:
|
||||
|
||||
* `response_model_exclude_defaults=True`
|
||||
* `response_model_exclude_none=True`
|
||||
|
||||
as described in <a href="https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict" class="external-link" target="_blank">the Pydantic docs</a> for `exclude_defaults` and `exclude_none`.
|
||||
|
||||
#### Data with values for fields with defaults
|
||||
|
||||
But if your data has values for the model's fields with default values, like the item with ID `bar`:
|
||||
|
||||
@@ -17,6 +17,9 @@ The same way you can specify a response model, you can also declare the HTTP sta
|
||||
|
||||
The `status_code` parameter receives a number with the HTTP status code.
|
||||
|
||||
!!! info
|
||||
`status_code` can alternatively also receive an `IntEnum`, such as Python's <a href="https://docs.python.org/3/library/http.html#http.HTTPStatus" class="external-link" target="_blank">`http.HTTPStatus`</a>.
|
||||
|
||||
It will:
|
||||
|
||||
* Return that status code in the response.
|
||||
@@ -83,4 +86,4 @@ They are just a convenience, they hold the same number, but that way you can use
|
||||
|
||||
## Changing the default
|
||||
|
||||
Later, in the **Advanced User Guide**, you will see how to return a different status code than the default you are declaring here.
|
||||
Later, in the [Advanced User Guide](../advanced/response-change-status-code.md){.internal-link target=_blank}, you will see how to return a different status code than the default you are declaring here.
|
||||
|
||||
@@ -33,7 +33,7 @@ The same way you can pass extra info to `Field`, you can do the same with `Path`
|
||||
|
||||
For example, you can pass an `example` for a body request to `Body`:
|
||||
|
||||
```Python hl_lines="20 21 22 23 24 25"
|
||||
```Python hl_lines="19 20 21 22 23 24"
|
||||
{!../../../docs_src/schema_extra_example/tutorial003.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ nav:
|
||||
- Languages:
|
||||
- en: /
|
||||
- es: /es/
|
||||
- it: /it/
|
||||
- pt: /pt/
|
||||
- ru: /ru/
|
||||
- zh: /zh/
|
||||
- features.md
|
||||
- python-types.md
|
||||
|
||||
@@ -22,7 +22,9 @@ nav:
|
||||
- Languages:
|
||||
- en: /
|
||||
- es: /es/
|
||||
- it: /it/
|
||||
- pt: /pt/
|
||||
- ru: /ru/
|
||||
- zh: /zh/
|
||||
- features.md
|
||||
- python-types.md
|
||||
|
||||
447
docs/it/docs/index.md
Normal file
447
docs/it/docs/index.md
Normal file
@@ -0,0 +1,447 @@
|
||||
|
||||
{!../../../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>
|
||||
|
||||
---
|
||||
|
||||
"_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>
|
||||
|
||||
---
|
||||
|
||||
"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_I’m over the moon excited about **FastAPI**. It’s 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>
|
||||
|
||||
---
|
||||
|
||||
## **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
|
||||
|
||||
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
INFO: Started reloader process [28720]
|
||||
INFO: Started server process [28722]
|
||||
INFO: Waiting for application startup.
|
||||
INFO: 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>):
|
||||
|
||||

|
||||
|
||||
### 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>):
|
||||
|
||||

|
||||
|
||||
## 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:
|
||||
|
||||

|
||||
|
||||
* Click on the button "Try it out", it allows you to fill the parameters and directly interact with the API:
|
||||
|
||||

|
||||
|
||||
* 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:
|
||||
|
||||

|
||||
|
||||
### 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:
|
||||
|
||||

|
||||
|
||||
### 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:
|
||||
|
||||

|
||||
|
||||
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.
|
||||
67
docs/it/mkdocs.yml
Normal file
67
docs/it/mkdocs.yml
Normal file
@@ -0,0 +1,67 @@
|
||||
site_name: FastAPI
|
||||
site_description: FastAPI framework, high performance, easy to learn, fast to code, ready for production
|
||||
site_url: https://fastapi.tiangolo.com/it/
|
||||
theme:
|
||||
name: material
|
||||
palette:
|
||||
primary: teal
|
||||
accent: amber
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
favicon: https://fastapi.tiangolo.com/img/favicon.png
|
||||
language: it
|
||||
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/
|
||||
- it: /it/
|
||||
- pt: /pt/
|
||||
- ru: /ru/
|
||||
- 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 ''
|
||||
- pymdownx.tabbed
|
||||
extra:
|
||||
social:
|
||||
- icon: fontawesome/brands/github-alt
|
||||
link: https://github.com/tiangolo/typer
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/tiangolo
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/in/tiangolo
|
||||
- icon: fontawesome/brands/dev
|
||||
link: https://dev.to/tiangolo
|
||||
- icon: fontawesome/brands/medium
|
||||
link: https://medium.com/@tiangolo
|
||||
- icon: fontawesome/solid/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
|
||||
@@ -22,7 +22,9 @@ nav:
|
||||
- Languages:
|
||||
- en: /
|
||||
- es: /es/
|
||||
- it: /it/
|
||||
- pt: /pt/
|
||||
- ru: /ru/
|
||||
- zh: /zh/
|
||||
- features.md
|
||||
- Tutorial - Guia de Usuário:
|
||||
|
||||
447
docs/ru/docs/index.md
Normal file
447
docs/ru/docs/index.md
Normal file
@@ -0,0 +1,447 @@
|
||||
|
||||
{!../../../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>
|
||||
|
||||
---
|
||||
|
||||
"_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>
|
||||
|
||||
---
|
||||
|
||||
"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_I’m over the moon excited about **FastAPI**. It’s 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>
|
||||
|
||||
---
|
||||
|
||||
## **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
|
||||
|
||||
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
INFO: Started reloader process [28720]
|
||||
INFO: Started server process [28722]
|
||||
INFO: Waiting for application startup.
|
||||
INFO: 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>):
|
||||
|
||||

|
||||
|
||||
### 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>):
|
||||
|
||||

|
||||
|
||||
## 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:
|
||||
|
||||

|
||||
|
||||
* Click on the button "Try it out", it allows you to fill the parameters and directly interact with the API:
|
||||
|
||||

|
||||
|
||||
* 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:
|
||||
|
||||

|
||||
|
||||
### 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:
|
||||
|
||||

|
||||
|
||||
### 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:
|
||||
|
||||

|
||||
|
||||
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.
|
||||
67
docs/ru/mkdocs.yml
Normal file
67
docs/ru/mkdocs.yml
Normal file
@@ -0,0 +1,67 @@
|
||||
site_name: FastAPI
|
||||
site_description: FastAPI framework, high performance, easy to learn, fast to code, ready for production
|
||||
site_url: https://fastapi.tiangolo.com/ru/
|
||||
theme:
|
||||
name: material
|
||||
palette:
|
||||
primary: teal
|
||||
accent: amber
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
favicon: https://fastapi.tiangolo.com/img/favicon.png
|
||||
language: ru
|
||||
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/
|
||||
- it: /it/
|
||||
- pt: /pt/
|
||||
- ru: /ru/
|
||||
- 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 ''
|
||||
- pymdownx.tabbed
|
||||
extra:
|
||||
social:
|
||||
- icon: fontawesome/brands/github-alt
|
||||
link: https://github.com/tiangolo/typer
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/tiangolo
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/in/tiangolo
|
||||
- icon: fontawesome/brands/dev
|
||||
link: https://dev.to/tiangolo
|
||||
- icon: fontawesome/brands/medium
|
||||
link: https://medium.com/@tiangolo
|
||||
- icon: fontawesome/solid/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
|
||||
510
docs/zh/docs/contributing.md
Normal file
510
docs/zh/docs/contributing.md
Normal file
@@ -0,0 +1,510 @@
|
||||
# 开发 - 贡献
|
||||
|
||||
首先,你最好先了解 [帮助 FastAPI 及获取帮助](help-fastapi.md){.internal-link target=_blank}的基本方式。
|
||||
|
||||
## 开发
|
||||
|
||||
如果你已经克隆了源码仓库,并且需要深入研究代码,下面是设置开发环境的指南。
|
||||
|
||||
### 通过 `venv` 管理虚拟环境
|
||||
|
||||
你可以使用 Python 的 `venv` 模块在一个目录中创建虚拟环境:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python -m venv env
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
这将使用 Python 程序创建一个 `./env/` 目录,然后你将能够为这个隔离的环境安装软件包。
|
||||
|
||||
### 激活虚拟环境
|
||||
|
||||
使用以下方法激活新环境:
|
||||
|
||||
=== "Linux, macOS"
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ source ./env/bin/activate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
=== "Windows PowerShell"
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ .\env\Scripts\Activate.ps1
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
=== "Windows Bash"
|
||||
|
||||
Or if you use Bash for Windows (e.g. <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>):
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ source ./env/Scripts/activate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
要检查操作是否成功,运行:
|
||||
|
||||
=== "Linux, macOS, Windows Bash"
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ which pip
|
||||
|
||||
some/directory/fastapi/env/bin/pip
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
=== "Windows PowerShell"
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ Get-Command pip
|
||||
|
||||
some/directory/fastapi/env/bin/pip
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
如果显示 `pip` 程序文件位于 `env/bin/pip` 则说明激活成功。 🎉
|
||||
|
||||
|
||||
!!! tip
|
||||
每一次你在该环境下使用 `pip` 安装了新软件包时,请再次激活该环境。
|
||||
|
||||
这样可以确保你在使用由该软件包安装的终端程序(如 `flit`)时使用的是当前虚拟环境中的程序,而不是其他的可能是全局安装的程序。
|
||||
|
||||
### Flit
|
||||
|
||||
**FastAPI** 使用 <a href="https://flit.readthedocs.io/en/latest/index.html" class="external-link" target="_blank">Flit</a> 来构建、打包和发布项目。
|
||||
|
||||
如上所述激活环境后,安装 `flit`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install flit
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
现在重新激活环境,以确保你正在使用的是刚刚安装的 `flit`(而不是全局环境的)。
|
||||
|
||||
然后使用 `flit` 来安装开发依赖:
|
||||
|
||||
=== "Linux, macOS"
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ flit install --deps develop --symlink
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
=== "Windows"
|
||||
|
||||
If you are on Windows, use `--pth-file` instead of `--symlink`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ flit install --deps develop --pth-file
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
这将在虚拟环境中安装所有依赖和本地版本的 FastAPI。
|
||||
|
||||
#### 使用本地 FastAPI
|
||||
|
||||
如果你创建一个导入并使用 FastAPI 的 Python 文件,然后使用虚拟环境中的 Python 运行它,它将使用你本地的 FastAPI 源码。
|
||||
|
||||
并且如果你更改该本地 FastAPI 的源码,由于它是通过 `--symlink` (或 Windows 上的 `--pth-file`)安装的,当你再次运行那个 Python 文件,它将使用你刚刚编辑过的最新版本的 FastAPI。
|
||||
|
||||
这样,你不必再去重新"安装"你的本地版本即可测试所有更改。
|
||||
|
||||
### 格式化
|
||||
|
||||
你可以运行下面的脚本来格式化和清理所有代码:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ bash scripts/format.sh
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
它还会自动对所有导入代码进行整理。
|
||||
|
||||
为了使整理正确进行,你需要在当前环境中安装本地的 FastAPI,即在运行上述段落中的命令时添加 `--symlink`(或 Windows 上的 `--pth-file`)。
|
||||
|
||||
### 格式化导入
|
||||
|
||||
还有另一个脚本可以格式化所有导入,并确保你没有未使用的导入代码:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ bash scripts/format-imports.sh
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
由于它依次运行了多个命令,并修改和还原了许多文件,所以运行时间会更长一些,因此经常地使用 `scripts/format.sh` 然后仅在提交前执行 `scripts/format-imports.sh` 会更好一些。
|
||||
|
||||
## 文档
|
||||
|
||||
首先,请确保按上述步骤设置好环境,这将安装所有需要的依赖。
|
||||
|
||||
文档使用 <a href="https://www.mkdocs.org/" class="external-link" target="_blank">MkDocs</a> 生成。
|
||||
|
||||
并且在 `./scripts/docs.py` 中还有适用的额外工具/脚本来处理翻译。
|
||||
|
||||
!!! tip
|
||||
你不需要去了解 `./scripts/docs.py` 中的代码,只需在命令行中使用它即可。
|
||||
|
||||
所有文档均在 `./docs/en/` 目录中以 Markdown 文件格式保存。
|
||||
|
||||
许多的教程章节里包含有代码块。
|
||||
|
||||
在大多数情况下,这些代码块是可以直接运行的真实完整的应用程序。
|
||||
|
||||
实际上,这些代码块不是写在 Markdown 文件内的,它们是位于 `./docs_src/` 目录中的 Python 文件。
|
||||
|
||||
生成站点时,这些 Python 文件会被包含/注入到文档中。
|
||||
|
||||
### 用于测试的文档
|
||||
|
||||
大多数的测试实际上都是针对文档中的示例源文件运行的。
|
||||
|
||||
这有助于确保:
|
||||
|
||||
* 文档始终是最新的。
|
||||
* 文档示例可以直接运行。
|
||||
* 绝大多数特性既在文档中得以阐述,又通过测试覆盖进行保障。
|
||||
|
||||
在本地开发期间,有一个脚本可以实时重载地构建站点并用来检查所做的任何更改:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python ./scripts/docs.py live
|
||||
|
||||
<span style="color: green;">[INFO]</span> Serving on http://127.0.0.1:8008
|
||||
<span style="color: green;">[INFO]</span> Start watching changes
|
||||
<span style="color: green;">[INFO]</span> Start detecting changes
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
它将在 `http://127.0.0.1:8008` 提供对文档的访问。
|
||||
|
||||
这样,你可以编辑文档/源文件并实时查看更改。
|
||||
|
||||
#### Typer CLI (可选)
|
||||
|
||||
本指引向你展示了如何直接用 `python` 程序运行 `./scripts/docs.py` 中的脚本。
|
||||
|
||||
但你也可以使用 <a href="https://typer.tiangolo.com/typer-cli/" class="external-link" target="_blank">Typer CLI</a>,而且在安装了补全功能后,你将可以在终端中对命令进行自动补全。
|
||||
|
||||
如果你打算安装 Typer CLI ,可以使用以下命令安装自动补全功能:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ typer --install-completion
|
||||
|
||||
zsh completion installed in /home/user/.bashrc.
|
||||
Completion will take effect once you restart the terminal.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### 应用和文档同时运行
|
||||
|
||||
如果你使用以下方式运行示例程序:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn tutorial001:app --reload
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
由于 Uvicorn 默认使用 `8000` 端口 ,因此运行在 `8008` 端口上的文档不会与之冲突。
|
||||
|
||||
### 翻译
|
||||
|
||||
非常感谢你能够参与文档的翻译!这项工作需要社区的帮助才能完成。 🌎 🚀
|
||||
|
||||
以下是参与帮助翻译的步骤。
|
||||
|
||||
#### 建议和指南
|
||||
|
||||
* 在当前 <a href="https://github.com/tiangolo/fastapi/pulls" class="external-link" target="_blank">已有的 pull requests</a> 中查找你使用的语言,添加要求修改或同意合并的评审意见。
|
||||
|
||||
!!! tip
|
||||
你可以为已有的 pull requests <a href="https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request" class="external-link" target="_blank">添加包含修改建议的评论</a>。
|
||||
|
||||
详情可查看关于 <a href="https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-request-reviews" class="external-link" target="_blank">添加 pull request 评审意见</a> 以同意合并或要求修改的文档。
|
||||
|
||||
* 在 <a href="https://github.com/tiangolo/fastapi/issues" class="external-link" target="_blank">issues</a> 中查找是否有对你所用语言所进行的协作翻译。
|
||||
|
||||
* 每翻译一个页面新增一个 pull request。这将使其他人更容易对其进行评审。
|
||||
|
||||
对于我(译注:作者使用西班牙语和英语)不懂的语言,我将在等待其他人评审翻译之后将其合并。
|
||||
|
||||
* 你还可以查看是否有你所用语言的翻译,并对其进行评审,这将帮助我了解翻译是否正确以及能否将其合并。
|
||||
|
||||
* 使用相同的 Python 示例并且仅翻译文档中的文本。无需进行任何其他更改示例也能正常工作。
|
||||
|
||||
* 使用相同的图片、文件名以及链接地址。无需进行任何其他调整来让它们兼容。
|
||||
|
||||
* 你可以从 <a href="https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes" class="external-link" target="_blank">ISO 639-1 代码列表</a> 表中查找你想要翻译语言的两位字母代码。
|
||||
|
||||
#### 已有的语言
|
||||
|
||||
假设你想将某个页面翻译成已经翻译了一些页面的语言,例如西班牙语。
|
||||
|
||||
对于西班牙语来说,它的两位字母代码是 `es`。所以西班牙语翻译的目录位于 `docs/es/`。
|
||||
|
||||
!!! tip
|
||||
主要("官方")语言是英语,位于 `docs/en/`目录。
|
||||
|
||||
现在为西班牙语文档运行实时服务器:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Use the command "live" and pass the language code as a CLI argument
|
||||
$ python ./scripts/docs.py live es
|
||||
|
||||
<span style="color: green;">[INFO]</span> Serving on http://127.0.0.1:8008
|
||||
<span style="color: green;">[INFO]</span> Start watching changes
|
||||
<span style="color: green;">[INFO]</span> Start detecting changes
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
现在你可以访问 <a href="http://127.0.0.1:8008" class="external-link" target="_blank">http://127.0.0.1:8008</a> 实时查看你所做的更改。
|
||||
|
||||
如果你查看 FastAPI 的线上文档网站,会看到每种语言都有所有页面。但是某些页面并未被翻译并且会有一处关于缺少翻译的提示。
|
||||
|
||||
但是当你像上面这样在本地运行文档时,你只会看到已经翻译的页面。
|
||||
|
||||
现在假设你要为 [Features](features.md){.internal-link target=_blank} 章节添加翻译。
|
||||
|
||||
* 复制下面的文件:
|
||||
|
||||
```
|
||||
docs/en/docs/features.md
|
||||
```
|
||||
|
||||
* 粘贴到你想要翻译语言目录的相同位置,比如:
|
||||
|
||||
```
|
||||
docs/es/docs/features.md
|
||||
```
|
||||
|
||||
!!! tip
|
||||
注意路径和文件名的唯一变化是语言代码,从 `en` 更改为 `es`。
|
||||
|
||||
* 现在打开位于英语文档目录下的 MkDocs 配置文件:
|
||||
|
||||
```
|
||||
docs/en/docs/mkdocs.yml
|
||||
```
|
||||
|
||||
* 在配置文件中找到 `docs/features.md` 所在的位置。结果像这样:
|
||||
|
||||
```YAML hl_lines="8"
|
||||
site_name: FastAPI
|
||||
# More stuff
|
||||
nav:
|
||||
- FastAPI: index.md
|
||||
- Languages:
|
||||
- en: /
|
||||
- es: /es/
|
||||
- features.md
|
||||
```
|
||||
|
||||
* 打开你正在编辑的语言目录中的 MkDocs 配置文件,例如:
|
||||
|
||||
```
|
||||
docs/es/docs/mkdocs.yml
|
||||
```
|
||||
|
||||
* 将其添加到与英语文档完全相同的位置,例如:
|
||||
|
||||
```YAML hl_lines="8"
|
||||
site_name: FastAPI
|
||||
# More stuff
|
||||
nav:
|
||||
- FastAPI: index.md
|
||||
- Languages:
|
||||
- en: /
|
||||
- es: /es/
|
||||
- features.md
|
||||
```
|
||||
|
||||
如果配置文件中还有其他条目,请确保你所翻译的新条目和它们之间的顺序与英文版本完全相同。
|
||||
|
||||
打开浏览器,现在你将看到文档展示了你所加入的新章节。 🎉
|
||||
|
||||
现在,你可以将它全部翻译完并在保存文件后进行预览。
|
||||
|
||||
#### 新语言
|
||||
|
||||
假设你想要为尚未有任何页面被翻译的语言添加翻译。
|
||||
|
||||
假设你想要添加克里奥尔语翻译,而且文档中还没有该语言的翻译。
|
||||
|
||||
点击上面提到的链接,可以查到"克里奥尔语"的代码为 `ht`。
|
||||
|
||||
下一步是运行脚本以生成新的翻译目录:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Use the command new-lang, pass the language code as a CLI argument
|
||||
$ python ./scripts/docs.py new-lang ht
|
||||
|
||||
Successfully initialized: docs/ht
|
||||
Updating ht
|
||||
Updating en
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
现在,你可以在编辑器中查看新创建的目录 `docs/ht/`。
|
||||
|
||||
!!! tip
|
||||
在添加实际的翻译之前,仅以此创建首个 pull request 来设定新语言的配置。
|
||||
|
||||
这样当你在翻译第一个页面时,其他人可以帮助翻译其他页面。🚀
|
||||
|
||||
首先翻译文档主页 `docs/ht/index.md`。
|
||||
|
||||
然后,你可以根据上面的"已有语言"的指引继续进行翻译。
|
||||
|
||||
##### 不支持的新语言
|
||||
|
||||
如果在运行实时服务器脚本时收到关于不支持该语言的错误,类似于:
|
||||
|
||||
```
|
||||
raise TemplateNotFound(template)
|
||||
jinja2.exceptions.TemplateNotFound: partials/language/xx.html
|
||||
```
|
||||
|
||||
这意味着文档的主题不支持该语言(在这种例子中,编造的语言代码是 `xx`)。
|
||||
|
||||
但是别担心,你可以将主题语言设置为英语,然后翻译文档的内容。
|
||||
|
||||
如果你需要这么做,编辑新语言目录下的 `mkdocs.yml`,它将有类似下面的内容:
|
||||
|
||||
```YAML hl_lines="5"
|
||||
site_name: FastAPI
|
||||
# More stuff
|
||||
theme:
|
||||
# More stuff
|
||||
language: xx
|
||||
```
|
||||
|
||||
将其中的 language 项从 `xx`(你的语言代码)更改为 `en`。
|
||||
|
||||
然后,你就可以再次启动实时服务器了。
|
||||
|
||||
#### 预览结果
|
||||
|
||||
当你通过 `live` 命令使用 `./scripts/docs.py` 中的脚本时,该脚本仅展示当前语言已有的文件和翻译。
|
||||
|
||||
但是当你完成翻译后,你可以像在线上展示一样测试所有内容。
|
||||
|
||||
为此,首先构建所有文档:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Use the command "build-all", this will take a bit
|
||||
$ python ./scripts/docs.py build-all
|
||||
|
||||
Updating es
|
||||
Updating en
|
||||
Building docs for: en
|
||||
Building docs for: es
|
||||
Successfully built docs for: es
|
||||
Copying en index.md to README.md
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
这将在 `./docs_build/` 目录中为每一种语言生成全部的文档。还包括添加所有缺少翻译的文件,并带有一条"此文件还没有翻译"的提醒。但是你不需要对该目录执行任何操作。
|
||||
|
||||
然后,它针对每种语言构建独立的 MkDocs 站点,将它们组合在一起,并在 `./site/` 目录中生成最终的输出。
|
||||
|
||||
然后你可以使用命令 `serve` 来运行生成的站点:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Use the command "serve" after running "build-all"
|
||||
$ python ./scripts/docs.py serve
|
||||
|
||||
Warning: this is a very simple server. For development, use mkdocs serve instead.
|
||||
This is here only to preview a site with translations already built.
|
||||
Make sure you run the build-all command first.
|
||||
Serving at: http://127.0.0.1:8008
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## 测试
|
||||
|
||||
你可以在本地运行下面的脚本来测试所有代码并生成 HTML 格式的覆盖率报告:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ bash scripts/test-cov-html.sh
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
该命令生成了一个 `./htmlcov/` 目录,如果你在浏览器中打开 `./htmlcov/index.html` 文件,你可以交互式地浏览被测试所覆盖的代码区块,并注意是否缺少了任何区块。
|
||||
|
||||
### 在编辑器中测试
|
||||
|
||||
如果你想要在编辑器中运行集成测试,请将 `./docs_src` 加入到你的 `PYTHONPATH` 变量中。
|
||||
|
||||
例如,在 VS Code 中你可以创建一个包含以下内容的 `.env` 文件:
|
||||
|
||||
```env
|
||||
PYTHONPATH=./docs_src
|
||||
```
|
||||
111
docs/zh/docs/help-fastapi.md
Normal file
111
docs/zh/docs/help-fastapi.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# 帮助 FastAPI - 获取帮助
|
||||
|
||||
你喜欢 **FastAPI** 吗?
|
||||
|
||||
您愿意去帮助 FastAPI,帮助其他用户以及作者吗?
|
||||
|
||||
或者你想要获得有关 **FastAPI** 的帮助?
|
||||
|
||||
下面是一些非常简单的方式去提供帮助(有些只需单击一两次链接)。
|
||||
|
||||
以及几种获取帮助的途径。
|
||||
|
||||
## 在 GitHub 上 Star **FastAPI**
|
||||
|
||||
你可以在 GitHub 上 "star" FastAPI(点击右上角的 star 按钮):<a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">https://github.com/tiangolo/fastapi</a>。
|
||||
|
||||
通过添加 star,其他用户将会更容易发现 FastAPI,并了解已经有许多人认为它有用。
|
||||
|
||||
## Watch GitHub 仓库的版本发布
|
||||
|
||||
你可以在 GitHub 上 "watch" FastAPI(点击右上角的 watch 按钮):<a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">https://github.com/tiangolo/fastapi</a>。
|
||||
|
||||
这时你可以选择 "Releases only" 选项。
|
||||
|
||||
之后,只要有 **FastAPI** 的新版本(包含缺陷修复和新功能)发布,你都会(通过电子邮件)收到通知。
|
||||
|
||||
## 加入聊天室
|
||||
|
||||
<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>
|
||||
|
||||
加入 Gitter 上的聊天室:<a href="https://gitter.im/tiangolo/fastapi" class="external-link" target="_blank">https://gitter.im/tiangolo/fastapi</a>。
|
||||
|
||||
在这里你可以快速提问、帮助他人、分享想法等。
|
||||
|
||||
## 与作者联系
|
||||
|
||||
你可以联系 <a href="https://tiangolo.com" class="external-link" target="_blank">我 (Sebastián Ramírez / `tiangolo`)</a> - FastAPI 的作者。
|
||||
|
||||
你可以:
|
||||
|
||||
* <a href="https://github.com/tiangolo" class="external-link" target="_blank">在 **GitHub** 上关注我</a>。
|
||||
* 查看我创建的其他的可能对你有帮助的开源项目。
|
||||
* 关注我以了解我创建的新开源项目。
|
||||
* <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">在 **Twitter** 上关注我</a>。
|
||||
* 告诉我你是如何使用 FastAPI 的(我很乐意听到)。
|
||||
* 提出问题。
|
||||
* <a href="https://www.linkedin.com/in/tiangolo/" class="external-link" target="_blank">在 **Linkedin** 上联系我</a>。
|
||||
* 与我交流。
|
||||
* 认可我的技能或推荐我 :)
|
||||
* <a href="https://medium.com/@tiangolo" class="external-link" target="_blank">在 **Medium** 上阅读我写的文章(或关注我)</a>。
|
||||
* 阅读我创建的其他想法,文章和工具。
|
||||
* 关注我以了解我发布的新内容。
|
||||
|
||||
## 发布和 **FastAPI** 有关的推特
|
||||
|
||||
<a href="https://twitter.com/compose/tweet?text=I'm loving FastAPI because... https://github.com/tiangolo/fastapi cc @tiangolo" class="external-link" target="_blank"> 发布和 **FastAPI** 有关的推特</a> 让我和其他人知道你为什么喜欢它。
|
||||
|
||||
## 告诉我你正在如何使用 **FastAPI**
|
||||
|
||||
我很乐意听到有关 **FastAPI** 被如何使用、你喜欢它的哪一点、被投入使用的项目/公司等等信息。
|
||||
|
||||
你可以通过以下平台让我知道:
|
||||
|
||||
* <a href="https://twitter.com/compose/tweet?text=Hey @tiangolo, I'm using FastAPI at..." class="external-link" target="_blank">**Twitter**</a>。
|
||||
* <a href="https://www.linkedin.com/in/tiangolo/" class="external-link" target="_blank">**Linkedin**</a>。
|
||||
* <a href="https://medium.com/@tiangolo" class="external-link" target="_blank">**Medium**</a>。
|
||||
|
||||
## 为 FastAPI 投票
|
||||
|
||||
* <a href="https://www.slant.co/options/34241/~fastapi-review" class="external-link" target="_blank">在 Slant 上为 **FastAPI** 投票</a>。
|
||||
|
||||
## 帮助他人解决 GitHub 的 issues
|
||||
|
||||
你可以查看 <a href="https://github.com/tiangolo/fastapi/issues" class="external-link" target="_blank">已有的 issues</a> 并尝试帮助其他人。
|
||||
|
||||
## Watch GitHub 仓库
|
||||
|
||||
你可以在 GitHub 上 "watch" FastAPI(点击右上角的 "watch" 按钮):<a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">https://github.com/tiangolo/fastapi</a>。
|
||||
|
||||
如果你选择的是 "Watching" 而不是 "Releases only" 选项,你会在其他人创建了新的 issue 时收到通知。
|
||||
|
||||
然后你可以尝试帮助他们解决这些 issue。
|
||||
|
||||
## 创建 issue
|
||||
|
||||
你可以在 GitHub 仓库中 <a href="https://github.com/tiangolo/fastapi/issues/new/choose" class="external-link" target="_blank">创建一个新 issue</a> 用来:
|
||||
|
||||
* 报告 bug 或问题。
|
||||
* 提议新的特性。
|
||||
* 提问。
|
||||
|
||||
## 创建 Pull Request
|
||||
|
||||
你可以 <a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">创建一个 Pull Request</a> 用来:
|
||||
|
||||
* 纠正你在文档中发现的错别字。
|
||||
* 添加新的文档内容。
|
||||
* 修复已有的 bug 或问题。
|
||||
* 添加新的特性。
|
||||
|
||||
## 赞助作者
|
||||
|
||||
你还可以通过 <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub sponsors</a> 在经济上支持作者(我)。
|
||||
|
||||
这样你可以给我买杯咖啡☕️以示谢意😄。
|
||||
|
||||
---
|
||||
|
||||
感谢!
|
||||
147
docs/zh/docs/tutorial/body.md
Normal file
147
docs/zh/docs/tutorial/body.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# 请求体
|
||||
|
||||
当你需要将数据从客户端(例如浏览器)发送给 API 时,你将其作为「请求体」发送。
|
||||
|
||||
**请求**体是客户端发送给 API 的数据。**响应**体是 API 发送给客户端的数据。
|
||||
|
||||
你的 API 几乎总是要发送**响应**体。但是客户端并不总是需要发送**请求**体。
|
||||
|
||||
我们使用 <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> 模型来声明**请求**体,并能够获得它们所具有的所有能力和优点。
|
||||
|
||||
!!! info
|
||||
你不能使用 `GET` 操作(HTTP 方法)发送请求体。
|
||||
|
||||
要发送数据,你必须使用下列方法之一:`POST`(较常见)、`PUT`、`DELETE` 或 `PATCH`。
|
||||
|
||||
## 导入 Pydantic 的 `BaseModel`
|
||||
|
||||
首先,你需要从 `pydantic` 中导入 `BaseModel`:
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
## 创建数据模型
|
||||
|
||||
然后,将你的数据模型声明为继承自 `BaseModel` 的类。
|
||||
|
||||
使用标准的 Python 类型来声明所有属性:
|
||||
|
||||
```Python hl_lines="5 6 7 8 9"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
和声明查询参数时一样,当一个模型属性具有默认值时,它不是必需的。否则它是一个必需属性。将默认值设为 `None` 可使其成为可选属性。
|
||||
|
||||
例如,上面的模型声明了一个这样的 JSON「`object`」(或 Python `dict`):
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"description": "An optional description",
|
||||
"price": 45.2,
|
||||
"tax": 3.5
|
||||
}
|
||||
```
|
||||
|
||||
...由于 `description` 和 `tax` 是可选的(它们的默认值为 `None`),下面的 JSON「`object`」也将是有效的:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"price": 45.2
|
||||
}
|
||||
```
|
||||
|
||||
## 声明为参数
|
||||
|
||||
使用与声明路径和查询参数的相同方式声明请求体,即可将其添加到「路径操作」中:
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
...并且将它的类型声明为你创建的 `Item` 模型。
|
||||
|
||||
## 结果
|
||||
|
||||
仅仅使用了 Python 类型声明,**FastAPI** 将会:
|
||||
|
||||
* 将请求体作为 JSON 读取。
|
||||
* 转换为相应的类型(在需要时)。
|
||||
* 校验数据。
|
||||
* 如果数据无效,将返回一条清晰易读的错误信息,指出不正确数据的确切位置和内容。
|
||||
* 将接收的数据赋值到参数 `item` 中。
|
||||
* 由于你已经在函数中将它声明为 `Item` 类型,你还将获得对于所有属性及其类型的一切编辑器支持(代码补全等)。
|
||||
* 为你的模型生成 <a href="http://json-schema.org" class="external-link" target="_blank">JSON 模式</a> 定义,你还可以在其他任何对你的项目有意义的地方使用它们。
|
||||
* 这些模式将成为生成的 OpenAPI 模式的一部分,并且被自动化文档 <abbr title="用户界面">UI</abbr> 所使用。
|
||||
|
||||
## 自动化文档
|
||||
|
||||
你所定义模型的 JSON 模式将成为生成的 OpenAPI 模式的一部分,并且在交互式 API 文档中展示:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/body/image01.png">
|
||||
|
||||
而且还将在每一个需要它们的*路径操作*的 API 文档中使用:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/body/image02.png">
|
||||
|
||||
## 编辑器支持
|
||||
|
||||
在你的编辑器中,你会在函数内部的任意地方得到类型提示和代码补全(如果你接收的是一个 `dict` 而不是 Pydantic 模型,则不会发生这种情况):
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/body/image03.png">
|
||||
|
||||
你还会获得对不正确的类型操作的错误检查:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/body/image04.png">
|
||||
|
||||
这并非偶然,整个框架都是围绕该设计而构建。
|
||||
|
||||
并且在进行任何实现之前,已经在设计阶段经过了全面测试,以确保它可以在所有的编辑器中生效。
|
||||
|
||||
Pydantic 本身甚至也进行了一些更改以支持此功能。
|
||||
|
||||
上面的截图取自 <a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>。
|
||||
|
||||
但是在 <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> 和绝大多数其他 Python 编辑器中你也会获得同样的编辑器支持:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/body/image05.png">
|
||||
|
||||
## 使用模型
|
||||
|
||||
在函数内部,你可以直接访问模型对象的所有属性:
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!../../../docs_src/body/tutorial002.py!}
|
||||
```
|
||||
|
||||
## 请求体 + 路径参数
|
||||
|
||||
你可以同时声明路径参数和请求体。
|
||||
|
||||
**FastAPI** 将识别出与路径参数匹配的函数参数应**从路径中获取**,而声明为 Pydantic 模型的函数参数应**从请求体中获取**。
|
||||
|
||||
```Python hl_lines="15 16"
|
||||
{!../../../docs_src/body/tutorial003.py!}
|
||||
```
|
||||
|
||||
## 请求体 + 路径参数 + 查询参数
|
||||
|
||||
你还可以同时声明**请求体**、**路径参数**和**查询参数**。
|
||||
|
||||
**FastAPI** 会识别它们中的每一个,并从正确的位置获取数据。
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!../../../docs_src/body/tutorial004.py!}
|
||||
```
|
||||
|
||||
函数参数将依次按如下规则进行识别:
|
||||
|
||||
* 如果在**路径**中也声明了该参数,它将被用作路径参数。
|
||||
* 如果参数属于**单一类型**(比如 `int`、`float`、`str`、`bool` 等)它将被解释为**查询**参数。
|
||||
* 如果参数的类型被声明为一个 **Pydantic 模型**,它将被解释为**请求体**。
|
||||
|
||||
## 不使用 Pydantic
|
||||
|
||||
如果你不想使用 Pydantic 模型,你还可以使用 **Body** 参数。请参阅文档 [请求体 - 多个参数:请求体中的单一值](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}。
|
||||
335
docs/zh/docs/tutorial/first-steps.md
Normal file
335
docs/zh/docs/tutorial/first-steps.md
Normal file
@@ -0,0 +1,335 @@
|
||||
# 第一步
|
||||
|
||||
最简单的 FastAPI 文件可能像下面这样:
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/first_steps/tutorial001.py!}
|
||||
```
|
||||
|
||||
将其复制到 `main.py` 文件中。
|
||||
|
||||
运行实时服务器:
|
||||
|
||||
<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>
|
||||
|
||||
!!! note
|
||||
`uvicorn main:app` 命令含义如下:
|
||||
|
||||
* `main`:`main.py` 文件(一个 Python「模块」)。
|
||||
* `app`:在 `main.py` 文件中通过 `app = FastAPI()` 创建的对象。
|
||||
* `--reload`:让服务器在更新代码后重新启动。仅在开发时使用该选项。
|
||||
|
||||
|
||||
在输出中,会有一行信息像下面这样:
|
||||
|
||||
```hl_lines="4"
|
||||
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
|
||||
该行显示了你的应用在本机所提供服务的 URL 地址。
|
||||
|
||||
### 查看
|
||||
|
||||
打开浏览器访问 <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>。
|
||||
|
||||
你将看到如下的 JSON 响应:
|
||||
|
||||
```JSON
|
||||
{"message": "Hello World"}
|
||||
```
|
||||
|
||||
### 交互式 API 文档
|
||||
|
||||
跳转到 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>。
|
||||
|
||||
你将会看到自动生成的交互式 API 文档(由 <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a> 提供):
|
||||
|
||||

|
||||
|
||||
### 可选的 API 文档
|
||||
|
||||
前往 <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>。
|
||||
|
||||
你将会看到可选的自动生成文档 (由 <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a> 提供):
|
||||
|
||||

|
||||
|
||||
### OpenAPI
|
||||
|
||||
**FastAPI** 使用定义 API 的 **OpenAPI** 标准将你的所有 API 转换成「模式」。
|
||||
|
||||
#### 「模式」
|
||||
|
||||
「模式」是对事物的一种定义或描述。它并非具体的实现代码,而只是抽象的描述。
|
||||
|
||||
#### API「模式」
|
||||
|
||||
在这种场景下,OpenAPI 是一种规定如何定义 API 模式的规范。
|
||||
|
||||
定义的 OpenAPI 模式将包括你的 API 路径,以及它们可能使用的参数等等。
|
||||
|
||||
#### 数据「模式」
|
||||
|
||||
「模式」这个术语也可能指的是某些数据比如 JSON 的结构。
|
||||
|
||||
在这种情况下,它可以表示 JSON 的属性及其具有的数据类型,等等。
|
||||
|
||||
#### OpenAPI 和 JSON Schema
|
||||
|
||||
OpenAPI 为你的 API 定义 API 模式。该模式中包含了你的 API 发送和接收的数据的定义(或称为「模式」),这些定义通过 JSON 数据模式标准 **JSON Schema** 所生成。
|
||||
|
||||
#### 查看 `openapi.json`
|
||||
|
||||
如果你对原始的 OpenAPI 模式长什么样子感到好奇,其实它只是一个自动生成的包含了所有 API 描述的 JSON。
|
||||
|
||||
你可以直接在:<a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a> 看到它。
|
||||
|
||||
它将显示以如下内容开头的 JSON:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"openapi": "3.0.2",
|
||||
"info": {
|
||||
"title": "FastAPI",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
|
||||
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
#### OpenAPI 的用途
|
||||
|
||||
驱动 FastAPI 内置的 2 个交互式文档系统的正是 OpenAPI 模式。
|
||||
|
||||
并且还有数十种替代方案,它们全部都基于 OpenAPI。你可以轻松地将这些替代方案中的任何一种添加到使用 **FastAPI** 构建的应用程序中。
|
||||
|
||||
你还可以使用它自动生成与你的 API 进行通信的客户端代码。例如 web 前端,移动端或物联网嵌入程序。
|
||||
|
||||
## 分步概括
|
||||
|
||||
### 步骤 1:导入 `FastAPI`
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/first_steps/tutorial001.py!}
|
||||
```
|
||||
|
||||
`FastAPI` 是一个为你的 API 提供了所有功能的 Python 类。
|
||||
|
||||
!!! note "技术细节"
|
||||
`FastAPI` 是直接从 `Starlette` 继承的类。
|
||||
|
||||
你可以通过 `FastAPI` 使用所有的 Starlette 的功能。
|
||||
|
||||
### 步骤 2:创建一个 `FastAPI`「实例」
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/first_steps/tutorial001.py!}
|
||||
```
|
||||
|
||||
这里的变量 `app` 会是 `FastAPI` 类的一个「实例」。
|
||||
|
||||
这个实例将是创建你所有 API 的主要交互对象。
|
||||
|
||||
这个 `app` 同样在如下命令中被 `uvicorn` 所引用:
|
||||
|
||||
<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)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
如果你像下面这样创建应用:
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/first_steps/tutorial002.py!}
|
||||
```
|
||||
|
||||
将代码放入 `main.py` 文件中,然后你可以像下面这样运行 `uvicorn`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:my_awesome_api --reload
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### 步骤 3:创建一个*路径操作*
|
||||
|
||||
#### 路径
|
||||
|
||||
这里的「路径」指的是 URL 中从第一个 `/` 起的后半部分。
|
||||
|
||||
所以,在一个这样的 URL 中:
|
||||
|
||||
```
|
||||
https://example.com/items/foo
|
||||
```
|
||||
|
||||
...路径会是:
|
||||
|
||||
```
|
||||
/items/foo
|
||||
```
|
||||
|
||||
!!! info
|
||||
「路径」也通常被称为「端点」或「路由」。
|
||||
|
||||
开发 API 时,「路径」是用来分离「关注点」和「资源」的主要手段。
|
||||
|
||||
#### 操作
|
||||
|
||||
这里的「操作」指的是一种 HTTP「方法」。
|
||||
|
||||
下列之一:
|
||||
|
||||
* `POST`
|
||||
* `GET`
|
||||
* `PUT`
|
||||
* `DELETE`
|
||||
|
||||
...以及更少见的几种:
|
||||
|
||||
* `OPTIONS`
|
||||
* `HEAD`
|
||||
* `PATCH`
|
||||
* `TRACE`
|
||||
|
||||
在 HTTP 协议中,你可以使用以上的其中一种(或多种)「方法」与每个路径进行通信。
|
||||
|
||||
---
|
||||
|
||||
在开发 API 时,你通常使用特定的 HTTP 方法去执行特定的行为。
|
||||
|
||||
通常使用:
|
||||
|
||||
* `POST`:创建数据。
|
||||
* `GET`:读取数据。
|
||||
* `PUT`:更新数据。
|
||||
* `DELETE`:删除数据。
|
||||
|
||||
因此,在 OpenAPI 中,每一个 HTTP 方法都被称为「操作」。
|
||||
|
||||
我们也打算称呼它们为「操作」。
|
||||
|
||||
#### 定义一个*路径操作装饰器*
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!../../../docs_src/first_steps/tutorial001.py!}
|
||||
```
|
||||
|
||||
`@app.get("/")` 告诉 **FastAPI** 在它下方的函数负责处理如下访问请求:
|
||||
|
||||
* 请求路径为 `/`
|
||||
* 使用 <abbr title="HTTP GET 方法"><code>get</code> 操作</abbr>
|
||||
|
||||
!!! info "`@decorator` Info"
|
||||
`@something` 语法在 Python 中被称为「装饰器」。
|
||||
|
||||
像一顶漂亮的装饰帽一样,将它放在一个函数的上方(我猜测这个术语的命名就是这么来的)。
|
||||
|
||||
装饰器接收位于其下方的函数并且用它完成一些工作。
|
||||
|
||||
在我们的例子中,这个装饰器告诉 **FastAPI** 位于其下方的函数对应着**路径** `/` 加上 `get` **操作**。
|
||||
|
||||
它是一个「**路径操作装饰器**」。
|
||||
|
||||
你也可以使用其他的操作:
|
||||
|
||||
* `@app.post()`
|
||||
* `@app.put()`
|
||||
* `@app.delete()`
|
||||
|
||||
以及更少见的:
|
||||
|
||||
* `@app.options()`
|
||||
* `@app.head()`
|
||||
* `@app.patch()`
|
||||
* `@app.trace()`
|
||||
|
||||
!!! tip
|
||||
您可以随意使用任何一个操作(HTTP方法)。
|
||||
|
||||
**FastAPI** 没有强制要求操作有任何特定的含义。
|
||||
|
||||
此处提供的信息仅作为指导,而不是要求。
|
||||
|
||||
比如,当使用 GraphQL 时通常你所有的动作都通过 `post` 一种方法执行。
|
||||
|
||||
### 步骤 4:定义**路径操作函数**
|
||||
|
||||
这是我们的「**路径操作函数**」:
|
||||
|
||||
* **路径**:是 `/`。
|
||||
* **操作**:是 `get`。
|
||||
* **函数**:是位于「装饰器」下方的函数(位于 `@app.get("/")` 下方)。
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!../../../docs_src/first_steps/tutorial001.py!}
|
||||
```
|
||||
|
||||
这是一个 Python 函数。
|
||||
|
||||
每当 **FastAPI** 接收一个使用 `GET` 方法访问 URL「`/`」的请求时这个函数会被调用。
|
||||
|
||||
在这个例子中,它是一个 `async` 函数。
|
||||
|
||||
---
|
||||
|
||||
你也可以将其定义为常规函数而不使用 `async def`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!../../../docs_src/first_steps/tutorial003.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
如果你不知道两者的区别,请查阅 [Async: *"In a hurry?"*](https://fastapi.tiangolo.com/async/#in-a-hurry){.internal-link target=_blank}。
|
||||
|
||||
### 步骤 5:返回内容
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!../../../docs_src/first_steps/tutorial001.py!}
|
||||
```
|
||||
|
||||
你可以返回一个 `dict`、`list`,像 `str`、`int` 一样的单个值,等等。
|
||||
|
||||
你还可以返回 Pydantic 模型(稍后你将了解更多)。
|
||||
|
||||
还有许多其他将会自动转换为 JSON 的对象和模型(包括 ORM 对象等)。尝试下使用你最喜欢的一种,它很有可能已经被支持。
|
||||
|
||||
## 总结
|
||||
|
||||
* 导入 `FastAPI`。
|
||||
* 创建一个 `app` 实例。
|
||||
* 编写一个**路径操作装饰器**(如 `@app.get("/")`)。
|
||||
* 编写一个**路径操作函数**(如上面的 `def root(): ...`)。
|
||||
* 运行开发服务器(如 `uvicorn main:app --reload`)。
|
||||
234
docs/zh/docs/tutorial/path-params.md
Normal file
234
docs/zh/docs/tutorial/path-params.md
Normal file
@@ -0,0 +1,234 @@
|
||||
# 路径参数
|
||||
|
||||
你可以使用与 Python 格式化字符串相同的语法来声明路径"参数"或"变量":
|
||||
|
||||
```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 类型标注为函数中的路径参数声明类型。
|
||||
|
||||
```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,是一个 Python `int` 值,而不是字符串 `"3"`。
|
||||
|
||||
所以,**FastAPI** 通过上面的类型声明提供了对请求的自动<abbr title="将来自 HTTP 请求中的字符串转换为 Python 数据类型">"解析"</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` 传入的值为 `"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
|
||||
所以,通过同样的 Python 类型声明,**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="https://fastapi.tiangolo.com/img/tutorial/path-params/image01.png">
|
||||
|
||||
!!! check
|
||||
再一次,还是通过相同的 Python 类型声明,**FastAPI** 为你提供了自动生成的交互式文档(集成 Swagger UI)。
|
||||
|
||||
注意这里的路径参数被声明为一个整数。
|
||||
|
||||
## 基于标准的好处:可选文档
|
||||
|
||||
由于生成的 API 模式来自于 <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md" class="external-link" target="_blank">OpenAPI</a> 标准,所以有很多工具与其兼容。
|
||||
|
||||
正因如此,**FastAPI** 内置了一个可选的 API 文档(使用 Redoc):
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/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`,我们假设它用来获取关于当前用户的数据.
|
||||
|
||||
然后,你还可以使用路径 `/users/{user_id}` 来通过用户 ID 获取关于特定用户的数据。
|
||||
|
||||
由于*路径操作*是按顺序依次运行的,你需要确保路径 `/users/me` 声明在路径 `/users/{user_id}`之前:
|
||||
```Python hl_lines="6 11"
|
||||
{!../../../docs_src/path_params/tutorial003.py!}
|
||||
```
|
||||
|
||||
否则,`/users/{user_id}` 的路径还将与 `/users/me` 相匹配,"认为"自己正在接收一个值为 `"me"` 的 `user_id` 参数。
|
||||
|
||||
## 预设值
|
||||
|
||||
如果你有一个接收路径参数的路径操作,但你希望预先设定可能的有效参数值,则可以使用标准的 Python <abbr title="Enumeration">`Enum`</abbr> 类型。
|
||||
|
||||
### 创建一个 `Enum` 类
|
||||
|
||||
导入 `Enum` 并创建一个继承自 `str` 和 `Enum` 的子类。
|
||||
|
||||
通过从 `str` 继承,API 文档将能够知道这些值必须为 `string` 类型并且能够正确地展示出来。
|
||||
|
||||
然后创建具有固定值的类属性,这些固定值将是可用的有效值:
|
||||
|
||||
```Python hl_lines="1 6 7 8 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 版本起在 Python 中可用。
|
||||
|
||||
!!! tip
|
||||
如果你想知道,"AlexNet"、"ResNet" 和 "LeNet" 只是机器学习中的<abbr title="技术上来说是深度学习模型架构">模型</abbr>名称。
|
||||
|
||||
### 声明*路径参数*
|
||||
|
||||
然后使用你定义的枚举类(`ModelName`)创建一个带有类型标注的*路径参数*:
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!../../../docs_src/path_params/tutorial005.py!}
|
||||
```
|
||||
|
||||
### 查看文档
|
||||
|
||||
因为已经指定了*路径参数*的可用值,所以交互式文档可以恰当地展示它们:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/path-params/image03.png">
|
||||
|
||||
### 使用 Python *枚举类型*
|
||||
|
||||
*路径参数*的值将是一个*枚举成员*。
|
||||
|
||||
#### 比较*枚举成员*
|
||||
|
||||
你可以将它与你创建的枚举类 `ModelName` 中的*枚举成员*进行比较:
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!../../../docs_src/path_params/tutorial005.py!}
|
||||
```
|
||||
|
||||
#### 获取*枚举值*
|
||||
|
||||
你可以使用 `model_name.value` 或通常来说 `your_enum_member.value` 来获取实际的值(在这个例子中为 `str`):
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!../../../docs_src/path_params/tutorial005.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
你也可以通过 `ModelName.lenet.value` 来获取值 `"lenet"`。
|
||||
|
||||
#### 返回*枚举成员*
|
||||
|
||||
你可以从*路径操作*中返回*枚举成员*,即使嵌套在 JSON 结构中(例如一个 `dict` 中)。
|
||||
|
||||
在返回给客户端之前,它们将被转换为对应的值:
|
||||
|
||||
```Python hl_lines="18 20 21"
|
||||
{!../../../docs_src/path_params/tutorial005.py!}
|
||||
```
|
||||
|
||||
## 包含路径的路径参数
|
||||
|
||||
假设你有一个*路径操作*,它的路径为 `/files/{file_path}`。
|
||||
|
||||
但是你需要 `file_path` 自身也包含*路径*,比如 `home/johndoe/myfile.txt`。
|
||||
|
||||
因此,该文件的URL将类似于这样:`/files/home/johndoe/myfile.txt`。
|
||||
|
||||
### OpenAPI 支持
|
||||
|
||||
OpenAPI 不支持任何方式去声明*路径参数*以在其内部包含*路径*,因为这可能会导致难以测试和定义的情况出现。
|
||||
|
||||
不过,你仍然可以通过 Starlette 的一个内部工具在 **FastAPI** 中实现它。
|
||||
|
||||
而且文档依旧可以使用,但是不会添加任何该参数应包含路径的说明。
|
||||
|
||||
### 路径转换器
|
||||
|
||||
你可以使用直接来自 Starlette 的选项来声明一个包含*路径*的*路径参数*:
|
||||
|
||||
```
|
||||
/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**,通过简短、直观和标准的 Python 类型声明,你将获得:
|
||||
|
||||
* 编辑器支持:错误检查,代码补全等
|
||||
* 数据 "<abbr title="将来自 HTTP 请求中的字符串转换为 Python 数据类型">解析</abbr>"
|
||||
* 数据校验
|
||||
* API 标注和自动生成的文档
|
||||
|
||||
而且你只需要声明一次即可。
|
||||
|
||||
这可能是 **FastAPI** 与其他框架相比主要的明显优势(除了原始性能以外)。
|
||||
281
docs/zh/docs/tutorial/query-params-str-validations.md
Normal file
281
docs/zh/docs/tutorial/query-params-str-validations.md
Normal file
@@ -0,0 +1,281 @@
|
||||
# 查询参数和字符串校验
|
||||
|
||||
**FastAPI** 允许你为参数声明额外的信息和校验。
|
||||
|
||||
让我们以下面的应用程序为例:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial001.py!}
|
||||
```
|
||||
|
||||
查询参数 `q` 的类型为 `str`,默认值为 `None`,因此它是可选的。
|
||||
|
||||
## 额外的校验
|
||||
|
||||
我们打算添加约束条件:即使 `q` 是可选的,但只要提供了该参数,则该参数值**不能超过50个字符的长度**。
|
||||
|
||||
### 导入 `Query`
|
||||
|
||||
为此,首先从 `fastapi` 导入 `Query`:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial002.py!}
|
||||
```
|
||||
|
||||
## 使用 `Query` 作为默认值
|
||||
|
||||
现在,将 `Query` 用作查询参数的默认值,并将它的 `max_length` 参数设置为 50:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial002.py!}
|
||||
```
|
||||
|
||||
由于我们必须用 `Query(None)` 替换默认值 `None`,`Query` 的第一个参数同样也是用于定义默认值。
|
||||
|
||||
所以:
|
||||
|
||||
```Python
|
||||
q: str = Query(None)
|
||||
```
|
||||
|
||||
...使得参数可选,等同于:
|
||||
|
||||
```Python
|
||||
q: str = None
|
||||
```
|
||||
|
||||
但是 `Query` 显式地将其声明为查询参数。
|
||||
|
||||
然后,我们可以将更多的参数传递给 `Query`。在本例中,适用于字符串的 `max_length` 参数:
|
||||
|
||||
```Python
|
||||
q: str = Query(None, max_length=50)
|
||||
```
|
||||
|
||||
将会校验数据,在数据无效时展示清晰的错误信息,并在 OpenAPI 模式的*路径操作*中记录该参数。
|
||||
|
||||
## 添加更多校验
|
||||
|
||||
你还可以添加 `min_length` 参数:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial003.py!}
|
||||
```
|
||||
|
||||
## 添加正则表达式
|
||||
|
||||
你可以定义一个参数值必须匹配的<abbr title="正则表达式或正则是定义字符串搜索模式的字符序列。">正则表达式</abbr>:
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial004.py!}
|
||||
```
|
||||
|
||||
这个指定的正则表达式通过以下规则检查接收到的参数值:
|
||||
|
||||
* `^`:以该符号之后的字符开头,符号之前没有字符。
|
||||
* `fixedquery`: 值精确地等于 `fixedquery`。
|
||||
* `$`: 到此结束,在 `fixedquery` 之后没有更多字符。
|
||||
|
||||
如果你对所有的这些**「正则表达式」**概念感到迷茫,请不要担心。对于许多人来说这都是一个困难的主题。你仍然可以在无需正则表达式的情况下做很多事情。
|
||||
|
||||
但是,一旦你需要用到并去学习它们时,请了解你已经可以在 **FastAPI** 中直接使用它们。
|
||||
|
||||
## 默认值
|
||||
|
||||
你可以向 `Query` 的第一个参数传入 `None` 用作查询参数的默认值,以同样的方式你也可以传递其他默认值。
|
||||
|
||||
假设你想要声明查询参数 `q`,使其 `min_length` 为 `3`,并且默认值为 `fixedquery`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial005.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
具有默认值还会使该参数成为可选参数。
|
||||
|
||||
## 声明为必需参数
|
||||
|
||||
当我们不需要声明额外的校验或元数据时,只需不声明默认值就可以使 `q` 参数成为必需参数,例如:
|
||||
|
||||
```Python
|
||||
q: str
|
||||
```
|
||||
|
||||
代替:
|
||||
|
||||
```Python
|
||||
q: str = None
|
||||
```
|
||||
|
||||
但是现在我们正在用 `Query` 声明它,例如:
|
||||
|
||||
```Python
|
||||
q: str = Query(None, min_length=3)
|
||||
```
|
||||
|
||||
因此,当你在使用 `Query` 且需要声明一个值是必需的时,可以将 `...` 用作第一个参数值:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial006.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
如果你之前没见过 `...` 这种用法:它是一个特殊的单独值,它是 <a href="https://docs.python.org/3/library/constants.html#Ellipsis" class="external-link" target="_blank">Python 的一部分并且被称为「省略号」</a>。
|
||||
|
||||
这将使 **FastAPI** 知道此查询参数是必需的。
|
||||
|
||||
## 查询参数列表 / 多个值
|
||||
|
||||
当你使用 `Query` 显式地定义查询参数时,你还可以声明它去接收一组值,或换句话来说,接收多个值。
|
||||
|
||||
例如,要声明一个可在 URL 中出现多次的查询参数 `q`,你可以这样写:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial011.py!}
|
||||
```
|
||||
|
||||
然后,输入如下网址:
|
||||
|
||||
```
|
||||
http://localhost:8000/items/?q=foo&q=bar
|
||||
```
|
||||
|
||||
你会在*路径操作函数*的*函数参数* `q` 中以一个 Python `list` 的形式接收到*查询参数* `q` 的多个值(`foo` 和 `bar`)。
|
||||
|
||||
因此,该 URL 的响应将会是:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"q": [
|
||||
"foo",
|
||||
"bar"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
要声明类型为 `list` 的查询参数,如上例所示,你需要显式地使用 `Query`,否则该参数将被解释为请求体。
|
||||
|
||||
交互式 API 文档将会相应地进行更新,以允许使用多个值:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/query-params-str-validations/image02.png">
|
||||
|
||||
### 具有默认值的查询参数列表 / 多个值
|
||||
|
||||
你还可以定义在没有任何给定值时的默认 `list` 值:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial012.py!}
|
||||
```
|
||||
|
||||
如果你访问:
|
||||
|
||||
```
|
||||
http://localhost:8000/items/
|
||||
```
|
||||
|
||||
`q` 的默认值将为:`["foo", "bar"]`,你的响应会是:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"q": [
|
||||
"foo",
|
||||
"bar"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用 `list`
|
||||
|
||||
你也可以直接使用 `list` 代替 `List [str]`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial013.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
请记住,在这种情况下 FastAPI 将不会检查列表的内容。
|
||||
|
||||
例如,`List[int]` 将检查(并记录到文档)列表的内容必须是整数。但是单独的 `list` 不会。
|
||||
|
||||
## 声明更多元数据
|
||||
|
||||
你可以添加更多有关该参数的信息。
|
||||
|
||||
这些信息将包含在生成的 OpenAPI 模式中,并由文档用户界面和外部工具所使用。
|
||||
|
||||
!!! note
|
||||
请记住,不同的工具对 OpenAPI 的支持程度可能不同。
|
||||
|
||||
其中一些可能不会展示所有已声明的额外信息,尽管在大多数情况下,缺少的这部分功能已经计划进行开发。
|
||||
|
||||
你可以添加 `title`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial007.py!}
|
||||
```
|
||||
|
||||
以及 `description`:
|
||||
|
||||
```Python hl_lines="11"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial008.py!}
|
||||
```
|
||||
|
||||
## 别名参数
|
||||
|
||||
假设你想要查询参数为 `item-query`。
|
||||
|
||||
像下面这样:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?item-query=foobaritems
|
||||
```
|
||||
|
||||
但是 `item-query` 不是一个有效的 Python 变量名称。
|
||||
|
||||
最接近的有效名称是 `item_query`。
|
||||
|
||||
但是你仍然要求它在 URL 中必须是 `item-query`...
|
||||
|
||||
这时你可以用 `alias` 参数声明一个别名,该别名将用于在 URL 中查找查询参数值:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial009.py!}
|
||||
```
|
||||
|
||||
## 弃用参数
|
||||
|
||||
现在假设你不再喜欢此参数。
|
||||
|
||||
你不得不将其保留一段时间,因为有些客户端正在使用它,但你希望文档清楚地将其展示为<abbr title ="已过时,建议不要使用它">已弃用</abbr>。
|
||||
|
||||
那么将参数 `deprecated=True` 传入 `Query`:
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial010.py!}
|
||||
```
|
||||
|
||||
文档将会像下面这样展示它:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/query-params-str-validations/image01.png">
|
||||
|
||||
## 总结
|
||||
|
||||
你可以为查询参数声明额外的校验和元数据。
|
||||
|
||||
通用的校验和元数据:
|
||||
|
||||
* `alias`
|
||||
* `title`
|
||||
* `description`
|
||||
* `deprecated`
|
||||
|
||||
特定于字符串的校验:
|
||||
|
||||
* `min_length`
|
||||
* `max_length`
|
||||
* `regex`
|
||||
|
||||
在这些示例中,你了解了如何声明对 `str` 值的校验。
|
||||
|
||||
请参阅下一章节,以了解如何声明对其他类型例如数值的校验。
|
||||
226
docs/zh/docs/tutorial/query-params.md
Normal file
226
docs/zh/docs/tutorial/query-params.md
Normal file
@@ -0,0 +1,226 @@
|
||||
# 查询参数
|
||||
|
||||
声明不属于路径参数的其他函数参数时,它们将被自动解释为"查询字符串"参数
|
||||
|
||||
```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 的一部分,因此它们的"原始值"是字符串。
|
||||
|
||||
但是,当你为它们声明了 Python 类型(在上面的示例中为 `int`)时,它们将转换为该类型并针对该类型进行校验。
|
||||
|
||||
应用于路径参数的所有相同过程也适用于查询参数:
|
||||
|
||||
* (很明显的)编辑器支持
|
||||
* 数据<abbr title="将来自 HTTP 请求的字符串转换为 Python 数据类型">"解析"</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="7"
|
||||
{!../../../docs_src/query_params/tutorial002.py!}
|
||||
```
|
||||
|
||||
在这个例子中,函数参数 `q` 将是可选的,并且默认值为 `None`。
|
||||
|
||||
!!! check
|
||||
还要注意的是,**FastAPI** 足够聪明,能够分辨出参数 `item_id` 是路径参数而 `q` 不是,因此 `q` 是一个查询参数。
|
||||
|
||||
## 查询参数类型转换
|
||||
|
||||
你还可以声明 `bool` 类型,它们将被自动转换:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!../../../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
|
||||
```
|
||||
|
||||
或任何其他的变体形式(大写,首字母大写等等),你的函数接收的 `short` 参数都会是布尔值 `True`。对于值为 `False` 的情况也是一样的。
|
||||
|
||||
|
||||
## 多个路径和查询参数
|
||||
|
||||
你可以同时声明多个路径参数和查询参数,**FastAPI** 能够识别它们。
|
||||
|
||||
而且你不需要以任何特定的顺序来声明。
|
||||
|
||||
它们将通过名称被检测到:
|
||||
|
||||
```Python hl_lines="6 8"
|
||||
{!../../../docs_src/query_params/tutorial004.py!}
|
||||
```
|
||||
|
||||
## 必需查询参数
|
||||
|
||||
当你为非路径参数声明了默认值时(目前而言,我们所知道的仅有查询参数),则该参数不是必需的。
|
||||
|
||||
如果你不想添加一个特定的值,而只是想使该参数成为可选的,则将默认值设置为 `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="7"
|
||||
{!../../../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`。
|
||||
|
||||
## Optional 类型声明
|
||||
|
||||
!!! warning
|
||||
这可能是一个比较高级的使用场景。
|
||||
|
||||
您也可以跳过它。
|
||||
|
||||
如果你正在使用 `mypy`,它可能会对如下的类型声明进行警告:
|
||||
|
||||
```Python
|
||||
limit: int = None
|
||||
```
|
||||
|
||||
提示类似以下错误:
|
||||
|
||||
```
|
||||
Incompatible types in assignment (expression has type "None", variable has type "int")
|
||||
```
|
||||
|
||||
在这种情况下,你可以使用 `Optional` 来告诉 `mypy` 该值可以为 `None`,例如:
|
||||
|
||||
```Python
|
||||
from typing import Optional
|
||||
|
||||
limit: Optional[int] = None
|
||||
```
|
||||
|
||||
在一个*路径操作*中,看起来会是:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params/tutorial007.py!}
|
||||
```
|
||||
@@ -22,13 +22,22 @@ nav:
|
||||
- Languages:
|
||||
- en: /
|
||||
- es: /es/
|
||||
- it: /it/
|
||||
- pt: /pt/
|
||||
- ru: /ru/
|
||||
- zh: /zh/
|
||||
- features.md
|
||||
- python-types.md
|
||||
- 教程 - 用户指南:
|
||||
- tutorial/index.md
|
||||
- tutorial/first-steps.md
|
||||
- tutorial/path-params.md
|
||||
- tutorial/query-params.md
|
||||
- tutorial/body.md
|
||||
- tutorial/query-params-str-validations.md
|
||||
- deployment.md
|
||||
- contributing.md
|
||||
- help-fastapi.md
|
||||
markdown_extensions:
|
||||
- toc:
|
||||
permalink: true
|
||||
|
||||
@@ -51,7 +51,7 @@ def test_create_item_bad_token():
|
||||
assert response.json() == {"detail": "Invalid X-Token header"}
|
||||
|
||||
|
||||
def test_create_existing_token():
|
||||
def test_create_existing_item():
|
||||
response = client.post(
|
||||
"/items/",
|
||||
headers={"X-Token": "coneofsilence"},
|
||||
|
||||
@@ -12,6 +12,6 @@ class Item(BaseModel):
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(*, item_id: int, item: Item = Body(..., embed=True)):
|
||||
async def update_item(item_id: int, item: Item = Body(..., embed=True)):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
||||
@@ -17,6 +17,6 @@ class User(BaseModel):
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(*, item_id: int, item: Item, user: User):
|
||||
async def update_item(item_id: int, item: Item, user: User):
|
||||
results = {"item_id": item_id, "item": item, "user": user}
|
||||
return results
|
||||
|
||||
@@ -18,7 +18,7 @@ class User(BaseModel):
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(
|
||||
*, item_id: int, item: Item, user: User, importance: int = Body(...)
|
||||
item_id: int, item: Item, user: User, importance: int = Body(...)
|
||||
):
|
||||
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
|
||||
return results
|
||||
|
||||
@@ -12,6 +12,6 @@ class Item(BaseModel):
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(*, item_id: int, item: Item = Body(..., embed=True)):
|
||||
async def update_item(item_id: int, item: Item = Body(..., embed=True)):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
||||
@@ -13,6 +13,6 @@ class Item(BaseModel):
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(*, item_id: int, item: Item):
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
||||
@@ -15,6 +15,6 @@ class Item(BaseModel):
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(*, item_id: int, item: Item):
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
||||
@@ -15,6 +15,6 @@ class Item(BaseModel):
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(*, item_id: int, item: Item):
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
||||
@@ -21,6 +21,6 @@ class Item(BaseModel):
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(*, item_id: int, item: Item):
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
||||
@@ -21,6 +21,6 @@ class Item(BaseModel):
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(*, item_id: int, item: Item):
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
||||
@@ -21,6 +21,6 @@ class Item(BaseModel):
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(*, item_id: int, item: Item):
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
||||
@@ -28,5 +28,5 @@ class Offer(BaseModel):
|
||||
|
||||
|
||||
@app.post("/offers/")
|
||||
async def create_offer(*, offer: Offer):
|
||||
async def create_offer(offer: Offer):
|
||||
return offer
|
||||
|
||||
@@ -12,5 +12,5 @@ class Image(BaseModel):
|
||||
|
||||
|
||||
@app.post("/images/multiple/")
|
||||
async def create_multiple_images(*, images: List[Image]):
|
||||
async def create_multiple_images(images: List[Image]):
|
||||
return images
|
||||
|
||||
@@ -4,5 +4,5 @@ app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(*, ads_id: str = Cookie(None)):
|
||||
async def read_items(ads_id: str = Cookie(None)):
|
||||
return {"ads_id": ads_id}
|
||||
|
||||
9
docs_src/custom_response/tutorial010.py
Normal file
9
docs_src/custom_response/tutorial010.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import ORJSONResponse
|
||||
|
||||
app = FastAPI(default_response_class=ORJSONResponse)
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items():
|
||||
return [{"item_id": "Foo"}]
|
||||
@@ -36,6 +36,6 @@ def fake_save_user(user_in: UserIn):
|
||||
|
||||
|
||||
@app.post("/user/", response_model=UserOut)
|
||||
async def create_user(*, user_in: UserIn):
|
||||
async def create_user(user_in: UserIn):
|
||||
user_saved = fake_save_user(user_in)
|
||||
return user_saved
|
||||
|
||||
@@ -34,6 +34,6 @@ def fake_save_user(user_in: UserIn):
|
||||
|
||||
|
||||
@app.post("/user/", response_model=UserOut)
|
||||
async def create_user(*, user_in: UserIn):
|
||||
async def create_user(user_in: UserIn):
|
||||
user_saved = fake_save_user(user_in)
|
||||
return user_saved
|
||||
|
||||
@@ -4,5 +4,5 @@ app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(*, user_agent: str = Header(None)):
|
||||
async def read_items(user_agent: str = Header(None)):
|
||||
return {"User-Agent": user_agent}
|
||||
|
||||
@@ -4,5 +4,5 @@ app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(*, strange_header: str = Header(None, convert_underscores=False)):
|
||||
async def read_items(strange_header: str = Header(None, convert_underscores=False)):
|
||||
return {"strange_header": strange_header}
|
||||
|
||||
28
docs_src/metadata/tutorial004.py
Normal file
28
docs_src/metadata/tutorial004.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from fastapi import FastAPI
|
||||
|
||||
tags_metadata = [
|
||||
{
|
||||
"name": "users",
|
||||
"description": "Operations with users. The **login** logic is also here.",
|
||||
},
|
||||
{
|
||||
"name": "items",
|
||||
"description": "Manage items. So _fancy_ they have their own docs.",
|
||||
"externalDocs": {
|
||||
"description": "Items external docs",
|
||||
"url": "https://fastapi.tiangolo.com/",
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
app = FastAPI(openapi_tags=tags_metadata)
|
||||
|
||||
|
||||
@app.get("/users/", tags=["users"])
|
||||
async def get_users():
|
||||
return [{"name": "Harry"}, {"name": "Ron"}]
|
||||
|
||||
|
||||
@app.get("/items/", tags=["items"])
|
||||
async def get_items():
|
||||
return [{"name": "wand"}, {"name": "flying broom"}]
|
||||
@@ -15,7 +15,7 @@ class Item(BaseModel):
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Item, summary="Create an item")
|
||||
async def create_item(*, item: Item):
|
||||
async def create_item(item: Item):
|
||||
"""
|
||||
Create an item with all the information:
|
||||
|
||||
|
||||
@@ -15,5 +15,5 @@ class Item(BaseModel):
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Item, status_code=status.HTTP_201_CREATED)
|
||||
async def create_item(*, item: Item):
|
||||
async def create_item(item: Item):
|
||||
return item
|
||||
|
||||
@@ -15,7 +15,7 @@ class Item(BaseModel):
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Item, tags=["items"])
|
||||
async def create_item(*, item: Item):
|
||||
async def create_item(item: Item):
|
||||
return item
|
||||
|
||||
|
||||
|
||||
@@ -20,5 +20,5 @@ class Item(BaseModel):
|
||||
summary="Create an item",
|
||||
description="Create an item with all the information, name, description, price, tax and a set of unique tags",
|
||||
)
|
||||
async def create_item(*, item: Item):
|
||||
async def create_item(item: Item):
|
||||
return item
|
||||
|
||||
@@ -15,7 +15,7 @@ class Item(BaseModel):
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Item, summary="Create an item")
|
||||
async def create_item(*, item: Item):
|
||||
async def create_item(item: Item):
|
||||
"""
|
||||
Create an item with all the information:
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class Item(BaseModel):
|
||||
summary="Create an item",
|
||||
response_description="The created item",
|
||||
)
|
||||
async def create_item(*, item: Item):
|
||||
async def create_item(item: Item):
|
||||
"""
|
||||
Create an item with all the information:
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ app = FastAPI()
|
||||
async def get_model(model_name: ModelName):
|
||||
if model_name == ModelName.alexnet:
|
||||
return {"model_name": model_name, "message": "Deep Learning FTW!"}
|
||||
|
||||
if model_name.value == "lenet":
|
||||
return {"model_name": model_name, "message": "LeCNN all the images"}
|
||||
|
||||
return {"model_name": model_name, "message": "Have some residuals"}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
class Person:
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def get_person_name(one_person: Person):
|
||||
return one_person.name
|
||||
def say_hi(name: Optional[str] = None):
|
||||
if name is not None:
|
||||
print(f"Hey {name}!")
|
||||
else:
|
||||
print("Hello World")
|
||||
|
||||
@@ -1,23 +1,7 @@
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
class Person:
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
id: int
|
||||
name = "John Doe"
|
||||
signup_ts: datetime = None
|
||||
friends: List[int] = []
|
||||
|
||||
|
||||
external_data = {
|
||||
"id": "123",
|
||||
"signup_ts": "2017-06-01 12:22",
|
||||
"friends": [1, "2", b"3"],
|
||||
}
|
||||
user = User(**external_data)
|
||||
print(user)
|
||||
# > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]
|
||||
print(user.id)
|
||||
# > 123
|
||||
def get_person_name(one_person: Person):
|
||||
return one_person.name
|
||||
|
||||
23
docs_src/python_types/tutorial011.py
Normal file
23
docs_src/python_types/tutorial011.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
id: int
|
||||
name = "John Doe"
|
||||
signup_ts: Optional[datetime] = None
|
||||
friends: List[int] = []
|
||||
|
||||
|
||||
external_data = {
|
||||
"id": "123",
|
||||
"signup_ts": "2017-06-01 12:22",
|
||||
"friends": [1, "2", b"3"],
|
||||
}
|
||||
user = User(**external_data)
|
||||
print(user)
|
||||
# > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]
|
||||
print(user.id)
|
||||
# > 123
|
||||
@@ -4,5 +4,5 @@ app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/login/")
|
||||
async def login(*, username: str = Form(...), password: str = Form(...)):
|
||||
async def login(username: str = Form(...), password: str = Form(...)):
|
||||
return {"username": username}
|
||||
|
||||
@@ -13,5 +13,5 @@ class UserIn(BaseModel):
|
||||
|
||||
# Don't do this in production!
|
||||
@app.post("/user/", response_model=UserIn)
|
||||
async def create_user(*, user: UserIn):
|
||||
async def create_user(user: UserIn):
|
||||
return user
|
||||
|
||||
@@ -18,5 +18,5 @@ class UserOut(BaseModel):
|
||||
|
||||
|
||||
@app.post("/user/", response_model=UserOut)
|
||||
async def create_user(*, user: UserIn):
|
||||
async def create_user(user: UserIn):
|
||||
return user
|
||||
|
||||
@@ -22,6 +22,6 @@ class Item(BaseModel):
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(*, item_id: int, item: Item):
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
||||
@@ -12,6 +12,6 @@ class Item(BaseModel):
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(*, item_id: int, item: Item):
|
||||
async def update_item(item_id: int, item: Item):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
||||
@@ -13,7 +13,6 @@ class Item(BaseModel):
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(
|
||||
*,
|
||||
item_id: int,
|
||||
item: Item = Body(
|
||||
...,
|
||||
@@ -23,7 +22,7 @@ async def update_item(
|
||||
"price": 35.4,
|
||||
"tax": 3.2,
|
||||
},
|
||||
)
|
||||
),
|
||||
):
|
||||
results = {"item_id": item_id, "item": item}
|
||||
return results
|
||||
|
||||
@@ -75,7 +75,7 @@ def authenticate_user(fake_db, username: str, password: str):
|
||||
return user
|
||||
|
||||
|
||||
def create_access_token(*, data: dict, expires_delta: timedelta = None):
|
||||
def create_access_token(data: dict, expires_delta: timedelta = None):
|
||||
to_encode = data.copy()
|
||||
if expires_delta:
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
|
||||
@@ -91,7 +91,7 @@ def authenticate_user(fake_db, username: str, password: str):
|
||||
return user
|
||||
|
||||
|
||||
def create_access_token(*, data: dict, expires_delta: timedelta = None):
|
||||
def create_access_token(data: dict, expires_delta: timedelta = None):
|
||||
to_encode = data.copy()
|
||||
if expires_delta:
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from fastapi import Cookie, Depends, FastAPI, Header, WebSocket, status
|
||||
from fastapi import Cookie, Depends, FastAPI, Query, WebSocket, status
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
@@ -13,8 +13,9 @@ html = """
|
||||
<h1>WebSocket Chat</h1>
|
||||
<form action="" onsubmit="sendMessage(event)">
|
||||
<label>Item ID: <input type="text" id="itemId" autocomplete="off" value="foo"/></label>
|
||||
<label>Token: <input type="text" id="token" autocomplete="off" value="some-key-token"/></label>
|
||||
<button onclick="connect(event)">Connect</button>
|
||||
<br>
|
||||
<hr>
|
||||
<label>Message: <input type="text" id="messageText" autocomplete="off"/></label>
|
||||
<button>Send</button>
|
||||
</form>
|
||||
@@ -23,8 +24,9 @@ html = """
|
||||
<script>
|
||||
var ws = null;
|
||||
function connect(event) {
|
||||
var input = document.getElementById("itemId")
|
||||
ws = new WebSocket("ws://localhost:8000/items/" + input.value + "/ws");
|
||||
var itemId = document.getElementById("itemId")
|
||||
var token = document.getElementById("token")
|
||||
ws = new WebSocket("ws://localhost:8000/items/" + itemId.value + "/ws?token=" + token.value);
|
||||
ws.onmessage = function(event) {
|
||||
var messages = document.getElementById('messages')
|
||||
var message = document.createElement('li')
|
||||
@@ -32,6 +34,7 @@ html = """
|
||||
message.appendChild(content)
|
||||
messages.appendChild(message)
|
||||
};
|
||||
event.preventDefault()
|
||||
}
|
||||
function sendMessage(event) {
|
||||
var input = document.getElementById("messageText")
|
||||
@@ -50,26 +53,26 @@ async def get():
|
||||
return HTMLResponse(html)
|
||||
|
||||
|
||||
async def get_cookie_or_client(
|
||||
websocket: WebSocket, session: str = Cookie(None), x_client: str = Header(None)
|
||||
async def get_cookie_or_token(
|
||||
websocket: WebSocket, session: str = Cookie(None), token: str = Query(None)
|
||||
):
|
||||
if session is None and x_client is None:
|
||||
if session is None and token is None:
|
||||
await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
|
||||
return session or x_client
|
||||
return session or token
|
||||
|
||||
|
||||
@app.websocket("/items/{item_id}/ws")
|
||||
async def websocket_endpoint(
|
||||
websocket: WebSocket,
|
||||
item_id: int,
|
||||
q: str = None,
|
||||
cookie_or_client: str = Depends(get_cookie_or_client),
|
||||
item_id: str,
|
||||
q: int = None,
|
||||
cookie_or_token: str = Depends(get_cookie_or_token),
|
||||
):
|
||||
await websocket.accept()
|
||||
while True:
|
||||
data = await websocket.receive_text()
|
||||
await websocket.send_text(
|
||||
f"Session Cookie or X-Client Header value is: {cookie_or_client}"
|
||||
f"Session cookie or query token value is: {cookie_or_token}"
|
||||
)
|
||||
if q is not None:
|
||||
await websocket.send_text(f"Query parameter q is: {q}")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
|
||||
|
||||
__version__ = "0.56.0"
|
||||
__version__ = "0.58.0"
|
||||
|
||||
from starlette import status
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ class FastAPI(Starlette):
|
||||
description: str = "",
|
||||
version: str = "0.1.0",
|
||||
openapi_url: Optional[str] = "/openapi.json",
|
||||
openapi_tags: Optional[List[Dict[str, Any]]] = None,
|
||||
servers: Optional[List[Dict[str, Union[str, Any]]]] = None,
|
||||
default_response_class: Type[Response] = JSONResponse,
|
||||
docs_url: Optional[str] = "/docs",
|
||||
redoc_url: Optional[str] = "/redoc",
|
||||
@@ -69,7 +71,9 @@ class FastAPI(Starlette):
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.version = version
|
||||
self.servers = servers
|
||||
self.openapi_url = openapi_url
|
||||
self.openapi_tags = openapi_tags
|
||||
# TODO: remove when discarding the openapi_prefix parameter
|
||||
if openapi_prefix:
|
||||
logger.warning(
|
||||
@@ -103,6 +107,8 @@ class FastAPI(Starlette):
|
||||
description=self.description,
|
||||
routes=self.routes,
|
||||
openapi_prefix=openapi_prefix,
|
||||
tags=self.openapi_tags,
|
||||
servers=self.servers,
|
||||
)
|
||||
return self.openapi_schema
|
||||
|
||||
|
||||
@@ -274,7 +274,7 @@ def get_dependant(
|
||||
path_param_names = get_path_param_names(path)
|
||||
endpoint_signature = get_typed_signature(call)
|
||||
signature_params = endpoint_signature.parameters
|
||||
if inspect.isgeneratorfunction(call) or inspect.isasyncgenfunction(call):
|
||||
if is_gen_callable(call) or is_async_gen_callable(call):
|
||||
check_dependency_contextmanagers()
|
||||
dependant = Dependant(call=call, name=name, path=path, use_cache=use_cache)
|
||||
for param_name, param in signature_params.items():
|
||||
@@ -294,7 +294,7 @@ def get_dependant(
|
||||
if param_name in path_param_names:
|
||||
assert is_scalar_field(
|
||||
field=param_field
|
||||
), f"Path params must be of one of the supported types"
|
||||
), "Path params must be of one of the supported types"
|
||||
if isinstance(param.default, params.Path):
|
||||
ignore_default = False
|
||||
else:
|
||||
@@ -412,19 +412,41 @@ def add_param_to_fields(*, field: ModelField, dependant: Dependant) -> None:
|
||||
|
||||
def is_coroutine_callable(call: Callable) -> bool:
|
||||
if inspect.isroutine(call):
|
||||
return asyncio.iscoroutinefunction(call)
|
||||
return inspect.iscoroutinefunction(call)
|
||||
if inspect.isclass(call):
|
||||
return False
|
||||
call = getattr(call, "__call__", None)
|
||||
return asyncio.iscoroutinefunction(call)
|
||||
return inspect.iscoroutinefunction(call)
|
||||
|
||||
|
||||
def is_async_gen_callable(call: Callable) -> bool:
|
||||
if inspect.isasyncgenfunction(call):
|
||||
return True
|
||||
call = getattr(call, "__call__", None)
|
||||
return inspect.isasyncgenfunction(call)
|
||||
|
||||
|
||||
def is_gen_callable(call: Callable) -> bool:
|
||||
if inspect.isgeneratorfunction(call):
|
||||
return True
|
||||
call = getattr(call, "__call__", None)
|
||||
return inspect.isgeneratorfunction(call)
|
||||
|
||||
|
||||
async def solve_generator(
|
||||
*, call: Callable, stack: AsyncExitStack, sub_values: Dict[str, Any]
|
||||
) -> Any:
|
||||
if inspect.isgeneratorfunction(call):
|
||||
if is_gen_callable(call):
|
||||
cm = contextmanager_in_threadpool(contextmanager(call)(**sub_values))
|
||||
elif inspect.isasyncgenfunction(call):
|
||||
elif is_async_gen_callable(call):
|
||||
if not inspect.isasyncgenfunction(call):
|
||||
# asynccontextmanager from the async_generator backfill pre python3.7
|
||||
# does not support callables that are not functions or methods.
|
||||
# See https://github.com/python-trio/async_generator/issues/32
|
||||
#
|
||||
# Expand the callable class into its __call__ method before decorating it.
|
||||
# This approach will work on newer python versions as well.
|
||||
call = getattr(call, "__call__", None)
|
||||
cm = asynccontextmanager(call)(**sub_values)
|
||||
return await stack.enter_async_context(cm)
|
||||
|
||||
@@ -478,6 +500,7 @@ async def solve_dependencies(
|
||||
name=sub_dependant.name,
|
||||
security_scopes=sub_dependant.security_scopes,
|
||||
)
|
||||
use_sub_dependant.security_scopes = sub_dependant.security_scopes
|
||||
|
||||
solved_result = await solve_dependencies(
|
||||
request=request,
|
||||
@@ -492,20 +515,16 @@ async def solve_dependencies(
|
||||
sub_values,
|
||||
sub_errors,
|
||||
background_tasks,
|
||||
sub_response,
|
||||
_, # the subdependency returns the same response we have
|
||||
sub_dependency_cache,
|
||||
) = solved_result
|
||||
sub_response = cast(Response, sub_response)
|
||||
response.headers.raw.extend(sub_response.headers.raw)
|
||||
if sub_response.status_code:
|
||||
response.status_code = sub_response.status_code
|
||||
dependency_cache.update(sub_dependency_cache)
|
||||
if sub_errors:
|
||||
errors.extend(sub_errors)
|
||||
continue
|
||||
if sub_dependant.use_cache and sub_dependant.cache_key in dependency_cache:
|
||||
solved = dependency_cache[sub_dependant.cache_key]
|
||||
elif inspect.isgeneratorfunction(call) or inspect.isasyncgenfunction(call):
|
||||
elif is_gen_callable(call) or is_async_gen_callable(call):
|
||||
stack = request.scope.get("fastapi_astack")
|
||||
if stack is None:
|
||||
raise RuntimeError(
|
||||
@@ -623,9 +642,17 @@ async def request_body_to_args(
|
||||
field = required_params[0]
|
||||
field_info = get_field_info(field)
|
||||
embed = getattr(field_info, "embed", None)
|
||||
if len(required_params) == 1 and not embed:
|
||||
field_alias_omitted = len(required_params) == 1 and not embed
|
||||
if field_alias_omitted:
|
||||
received_body = {field.alias: received_body}
|
||||
|
||||
for field in required_params:
|
||||
loc: Tuple[str, ...]
|
||||
if field_alias_omitted:
|
||||
loc = ("body",)
|
||||
else:
|
||||
loc = ("body", field.alias)
|
||||
|
||||
value: Any = None
|
||||
if received_body is not None:
|
||||
if (
|
||||
@@ -636,7 +663,7 @@ async def request_body_to_args(
|
||||
try:
|
||||
value = received_body.get(field.alias)
|
||||
except AttributeError:
|
||||
errors.append(get_missing_field_error(field.alias))
|
||||
errors.append(get_missing_field_error(loc))
|
||||
continue
|
||||
if (
|
||||
value is None
|
||||
@@ -648,7 +675,7 @@ async def request_body_to_args(
|
||||
)
|
||||
):
|
||||
if field.required:
|
||||
errors.append(get_missing_field_error(field.alias))
|
||||
errors.append(get_missing_field_error(loc))
|
||||
else:
|
||||
values[field.name] = deepcopy(field.default)
|
||||
continue
|
||||
@@ -667,7 +694,9 @@ async def request_body_to_args(
|
||||
awaitables = [sub_value.read() for sub_value in value]
|
||||
contents = await asyncio.gather(*awaitables)
|
||||
value = sequence_shape_to_type[field.shape](contents)
|
||||
v_, errors_ = field.validate(value, values, loc=("body", field.alias))
|
||||
|
||||
v_, errors_ = field.validate(value, values, loc=loc)
|
||||
|
||||
if isinstance(errors_, ErrorWrapper):
|
||||
errors.append(errors_)
|
||||
elif isinstance(errors_, list):
|
||||
@@ -677,12 +706,12 @@ async def request_body_to_args(
|
||||
return values, errors
|
||||
|
||||
|
||||
def get_missing_field_error(field_alias: str) -> ErrorWrapper:
|
||||
def get_missing_field_error(loc: Tuple[str, ...]) -> ErrorWrapper:
|
||||
if PYDANTIC_1:
|
||||
missing_field_error = ErrorWrapper(MissingError(), loc=("body", field_alias))
|
||||
missing_field_error = ErrorWrapper(MissingError(), loc=loc)
|
||||
else: # pragma: no cover
|
||||
missing_field_error = ErrorWrapper( # type: ignore
|
||||
MissingError(), loc=("body", field_alias), config=BaseConfig,
|
||||
MissingError(), loc=loc, config=BaseConfig,
|
||||
)
|
||||
return missing_field_error
|
||||
|
||||
|
||||
@@ -71,6 +71,8 @@ def jsonable_encoder(
|
||||
by_alias=by_alias,
|
||||
skip_defaults=bool(exclude_unset or skip_defaults),
|
||||
)
|
||||
if "__root__" in obj_dict:
|
||||
obj_dict = obj_dict["__root__"]
|
||||
return jsonable_encoder(
|
||||
obj_dict,
|
||||
exclude_none=exclude_none,
|
||||
|
||||
@@ -44,7 +44,9 @@ def get_swagger_ui_html(
|
||||
SwaggerUIBundle.SwaggerUIStandalonePreset
|
||||
],
|
||||
layout: "BaseLayout",
|
||||
deepLinking: true
|
||||
deepLinking: true,
|
||||
showExtensions: true,
|
||||
showCommonExtensions: true
|
||||
})"""
|
||||
|
||||
if init_oauth:
|
||||
|
||||
@@ -63,7 +63,7 @@ class ServerVariable(BaseModel):
|
||||
|
||||
|
||||
class Server(BaseModel):
|
||||
url: AnyUrl
|
||||
url: Union[AnyUrl, str]
|
||||
description: Optional[str] = None
|
||||
variables: Optional[Dict[str, ServerVariable]] = None
|
||||
|
||||
@@ -112,7 +112,7 @@ class SchemaBase(BaseModel):
|
||||
allOf: Optional[List[Any]] = None
|
||||
oneOf: Optional[List[Any]] = None
|
||||
anyOf: Optional[List[Any]] = None
|
||||
not_: Optional[List[Any]] = Field(None, alias="not")
|
||||
not_: Optional[Any] = Field(None, alias="not")
|
||||
items: Optional[Any] = None
|
||||
properties: Optional[Dict[str, Any]] = None
|
||||
additionalProperties: Optional[Union[Dict[str, Any], bool]] = None
|
||||
@@ -133,7 +133,7 @@ class Schema(SchemaBase):
|
||||
allOf: Optional[List[SchemaBase]] = None
|
||||
oneOf: Optional[List[SchemaBase]] = None
|
||||
anyOf: Optional[List[SchemaBase]] = None
|
||||
not_: Optional[List[SchemaBase]] = Field(None, alias="not")
|
||||
not_: Optional[SchemaBase] = Field(None, alias="not")
|
||||
items: Optional[SchemaBase] = None
|
||||
properties: Optional[Dict[str, SchemaBase]] = None
|
||||
additionalProperties: Optional[Union[Dict[str, Any], bool]] = None
|
||||
|
||||
@@ -14,6 +14,7 @@ from fastapi.openapi.constants import (
|
||||
from fastapi.openapi.models import OpenAPI
|
||||
from fastapi.params import Body, Param
|
||||
from fastapi.utils import (
|
||||
deep_dict_update,
|
||||
generate_operation_id_for_path,
|
||||
get_field_info,
|
||||
get_model_definitions,
|
||||
@@ -86,7 +87,7 @@ def get_openapi_security_definitions(flat_dependant: Dependant) -> Tuple[Dict, L
|
||||
def get_openapi_operation_parameters(
|
||||
*,
|
||||
all_route_params: Sequence[ModelField],
|
||||
model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str]
|
||||
model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
|
||||
) -> List[Dict[str, Any]]:
|
||||
parameters = []
|
||||
for param in all_route_params:
|
||||
@@ -112,7 +113,7 @@ def get_openapi_operation_parameters(
|
||||
def get_openapi_operation_request_body(
|
||||
*,
|
||||
body_field: Optional[ModelField],
|
||||
model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str]
|
||||
model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
|
||||
) -> Optional[Dict]:
|
||||
if not body_field:
|
||||
return None
|
||||
@@ -201,29 +202,6 @@ def get_openapi_path(
|
||||
)
|
||||
callbacks[callback.name] = {callback.path: cb_path}
|
||||
operation["callbacks"] = callbacks
|
||||
if route.responses:
|
||||
for (additional_status_code, response) in route.responses.items():
|
||||
assert isinstance(
|
||||
response, dict
|
||||
), "An additional response must be a dict"
|
||||
field = route.response_fields.get(additional_status_code)
|
||||
if field:
|
||||
response_schema, _, _ = field_schema(
|
||||
field, model_name_map=model_name_map, ref_prefix=REF_PREFIX
|
||||
)
|
||||
response.setdefault("content", {}).setdefault(
|
||||
route_response_media_type or "application/json", {}
|
||||
)["schema"] = response_schema
|
||||
status_text: Optional[str] = status_code_ranges.get(
|
||||
str(additional_status_code).upper()
|
||||
) or http.client.responses.get(int(additional_status_code))
|
||||
response.setdefault(
|
||||
"description", status_text or "Additional Response"
|
||||
)
|
||||
status_code_key = str(additional_status_code).upper()
|
||||
if status_code_key == "DEFAULT":
|
||||
status_code_key = "default"
|
||||
operation.setdefault("responses", {})[status_code_key] = response
|
||||
status_code = str(route.status_code)
|
||||
operation.setdefault("responses", {}).setdefault(status_code, {})[
|
||||
"description"
|
||||
@@ -247,7 +225,47 @@ def get_openapi_path(
|
||||
).setdefault("content", {}).setdefault(route_response_media_type, {})[
|
||||
"schema"
|
||||
] = response_schema
|
||||
|
||||
if route.responses:
|
||||
operation_responses = operation.setdefault("responses", {})
|
||||
for (
|
||||
additional_status_code,
|
||||
additional_response,
|
||||
) in route.responses.items():
|
||||
process_response = additional_response.copy()
|
||||
process_response.pop("model", None)
|
||||
status_code_key = str(additional_status_code).upper()
|
||||
if status_code_key == "DEFAULT":
|
||||
status_code_key = "default"
|
||||
openapi_response = operation_responses.setdefault(
|
||||
status_code_key, {}
|
||||
)
|
||||
assert isinstance(
|
||||
process_response, dict
|
||||
), "An additional response must be a dict"
|
||||
field = route.response_fields.get(additional_status_code)
|
||||
additional_field_schema: Optional[Dict[str, Any]] = None
|
||||
if field:
|
||||
additional_field_schema, _, _ = field_schema(
|
||||
field, model_name_map=model_name_map, ref_prefix=REF_PREFIX
|
||||
)
|
||||
media_type = route_response_media_type or "application/json"
|
||||
additional_schema = (
|
||||
process_response.setdefault("content", {})
|
||||
.setdefault(media_type, {})
|
||||
.setdefault("schema", {})
|
||||
)
|
||||
deep_dict_update(additional_schema, additional_field_schema)
|
||||
status_text: Optional[str] = status_code_ranges.get(
|
||||
str(additional_status_code).upper()
|
||||
) or http.client.responses.get(int(additional_status_code))
|
||||
description = (
|
||||
process_response.get("description")
|
||||
or openapi_response.get("description")
|
||||
or status_text
|
||||
or "Additional Response"
|
||||
)
|
||||
deep_dict_update(openapi_response, process_response)
|
||||
openapi_response["description"] = description
|
||||
http422 = str(HTTP_422_UNPROCESSABLE_ENTITY)
|
||||
if (all_route_params or route.body_field) and not any(
|
||||
[
|
||||
@@ -313,12 +331,16 @@ def get_openapi(
|
||||
openapi_version: str = "3.0.2",
|
||||
description: str = None,
|
||||
routes: Sequence[BaseRoute],
|
||||
openapi_prefix: str = ""
|
||||
openapi_prefix: str = "",
|
||||
tags: Optional[List[Dict[str, Any]]] = None,
|
||||
servers: Optional[List[Dict[str, Union[str, Any]]]] = None,
|
||||
) -> Dict:
|
||||
info = {"title": title, "version": version}
|
||||
if description:
|
||||
info["description"] = description
|
||||
output = {"openapi": openapi_version, "info": info}
|
||||
output: Dict[str, Any] = {"openapi": openapi_version, "info": info}
|
||||
if servers:
|
||||
output["servers"] = servers
|
||||
components: Dict[str, Dict] = {}
|
||||
paths: Dict[str, Dict] = {}
|
||||
flat_models = get_flat_models_from_routes(routes)
|
||||
@@ -348,4 +370,6 @@ def get_openapi(
|
||||
if components:
|
||||
output["components"] = components
|
||||
output["paths"] = paths
|
||||
if tags:
|
||||
output["tags"] = tags
|
||||
return jsonable_encoder(OpenAPI(**output), by_alias=True, exclude_none=True)
|
||||
|
||||
@@ -51,6 +51,9 @@ class Param(FieldInfo):
|
||||
**extra,
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.__class__.__name__}({self.default})"
|
||||
|
||||
|
||||
class Path(Param):
|
||||
in_ = ParamTypes.path
|
||||
@@ -239,6 +242,9 @@ class Body(FieldInfo):
|
||||
**extra,
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.__class__.__name__}({self.default})"
|
||||
|
||||
|
||||
class Form(Body):
|
||||
def __init__(
|
||||
@@ -316,6 +322,11 @@ class Depends:
|
||||
self.dependency = dependency
|
||||
self.use_cache = use_cache
|
||||
|
||||
def __repr__(self) -> str:
|
||||
attr = getattr(self.dependency, "__name__", type(self.dependency).__name__)
|
||||
cache = "" if self.use_cache else ", use_cache=False"
|
||||
return f"{self.__class__.__name__}({attr}{cache})"
|
||||
|
||||
|
||||
class Security(Depends):
|
||||
def __init__(
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import asyncio
|
||||
import enum
|
||||
import inspect
|
||||
import json
|
||||
from typing import Any, Callable, Dict, List, Optional, Sequence, Set, Type, Union
|
||||
|
||||
from fastapi import params
|
||||
@@ -12,7 +14,6 @@ from fastapi.dependencies.utils import (
|
||||
)
|
||||
from fastapi.encoders import DictIntStrAny, SetIntStr, jsonable_encoder
|
||||
from fastapi.exceptions import RequestValidationError, WebSocketRequestValidationError
|
||||
from fastapi.logger import logger
|
||||
from fastapi.openapi.constants import STATUS_CODES_WITH_NO_BODY
|
||||
from fastapi.utils import (
|
||||
PYDANTIC_1,
|
||||
@@ -178,8 +179,9 @@ def get_request_handler(
|
||||
body_bytes = await request.body()
|
||||
if body_bytes:
|
||||
body = await request.json()
|
||||
except json.JSONDecodeError as e:
|
||||
raise RequestValidationError([ErrorWrapper(e, ("body", e.pos))], body=e.doc)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting request body: {e}")
|
||||
raise HTTPException(
|
||||
status_code=400, detail="There was an error parsing the body"
|
||||
) from e
|
||||
@@ -295,6 +297,9 @@ class APIRoute(routing.Route):
|
||||
dependency_overrides_provider: Any = None,
|
||||
callbacks: Optional[List["APIRoute"]] = None,
|
||||
) -> None:
|
||||
# normalise enums e.g. http.HTTPStatus
|
||||
if isinstance(status_code, enum.IntEnum):
|
||||
status_code = int(status_code)
|
||||
self.path = path
|
||||
self.endpoint = endpoint
|
||||
self.name = get_name(endpoint) if name is None else name
|
||||
@@ -366,7 +371,7 @@ class APIRoute(routing.Route):
|
||||
self.include_in_schema = include_in_schema
|
||||
self.response_class = response_class
|
||||
|
||||
assert callable(endpoint), f"An endpoint must be a callable"
|
||||
assert callable(endpoint), "An endpoint must be a callable"
|
||||
self.dependant = get_dependant(path=self.path_format, call=self.endpoint)
|
||||
for depends in self.dependencies[::-1]:
|
||||
self.dependant.dependencies.insert(
|
||||
|
||||
@@ -11,6 +11,7 @@ from .oauth2 import (
|
||||
OAuth2AuthorizationCodeBearer,
|
||||
OAuth2PasswordBearer,
|
||||
OAuth2PasswordRequestForm,
|
||||
OAuth2PasswordRequestFormStrict,
|
||||
SecurityScopes,
|
||||
)
|
||||
from .open_id_connect_url import OpenIdConnect
|
||||
|
||||
@@ -67,7 +67,7 @@ def get_model_definitions(
|
||||
|
||||
|
||||
def get_path_param_names(path: str) -> Set[str]:
|
||||
return {item.strip("{}") for item in re.findall("{[^}]*}", path)}
|
||||
return set(re.findall("{(.*?)}", path))
|
||||
|
||||
|
||||
def create_response_field(
|
||||
@@ -172,3 +172,15 @@ def generate_operation_id_for_path(*, name: str, path: str, method: str) -> str:
|
||||
operation_id = re.sub("[^0-9a-zA-Z_]", "_", operation_id)
|
||||
operation_id = operation_id + "_" + method.lower()
|
||||
return operation_id
|
||||
|
||||
|
||||
def deep_dict_update(main_dict: dict, update_dict: dict) -> None:
|
||||
for key in update_dict:
|
||||
if (
|
||||
key in main_dict
|
||||
and isinstance(main_dict[key], dict)
|
||||
and isinstance(update_dict[key], dict)
|
||||
):
|
||||
deep_dict_update(main_dict[key], update_dict[key])
|
||||
else:
|
||||
main_dict[key] = update_dict[key]
|
||||
|
||||
@@ -32,7 +32,7 @@ classifiers = [
|
||||
"Topic :: Internet :: WWW/HTTP",
|
||||
]
|
||||
requires = [
|
||||
"starlette ==0.13.2",
|
||||
"starlette ==0.13.4",
|
||||
"pydantic >=0.32.2,<2.0.0"
|
||||
]
|
||||
description-file = "README.md"
|
||||
@@ -43,7 +43,7 @@ Documentation = "https://fastapi.tiangolo.com/"
|
||||
|
||||
[tool.flit.metadata.requires-extra]
|
||||
test = [
|
||||
"pytest >=4.0.0",
|
||||
"pytest >=5.4.3",
|
||||
"pytest-cov",
|
||||
"mypy",
|
||||
"black",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import http
|
||||
|
||||
from fastapi import FastAPI, Path, Query
|
||||
|
||||
app = FastAPI()
|
||||
@@ -184,3 +186,8 @@ def get_query_param_required(query=Query(...)):
|
||||
@app.get("/query/param-required/int")
|
||||
def get_query_param_required_type(query: int = Query(...)):
|
||||
return f"foo bar {query}"
|
||||
|
||||
|
||||
@app.get("/enum-status-code", status_code=http.HTTPStatus.CREATED)
|
||||
def get_enum_status_code():
|
||||
return "foo bar"
|
||||
|
||||
138
tests/test_additional_responses_custom_model_in_callback.py
Normal file
138
tests/test_additional_responses_custom_model_in_callback.py
Normal file
@@ -0,0 +1,138 @@
|
||||
from fastapi import APIRouter, FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
|
||||
class CustomModel(BaseModel):
|
||||
a: int
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
callback_router = APIRouter(default_response_class=JSONResponse)
|
||||
|
||||
|
||||
@callback_router.get(
|
||||
"{$callback_url}/callback/", responses={400: {"model": CustomModel}}
|
||||
)
|
||||
def callback_route():
|
||||
pass # pragma: no cover
|
||||
|
||||
|
||||
@app.post("/", callbacks=callback_router.routes)
|
||||
def main_route(callback_url: HttpUrl):
|
||||
pass # pragma: no cover
|
||||
|
||||
|
||||
openapi_schema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/": {
|
||||
"post": {
|
||||
"summary": "Main Route",
|
||||
"operationId": "main_route__post",
|
||||
"parameters": [
|
||||
{
|
||||
"required": True,
|
||||
"schema": {
|
||||
"title": "Callback Url",
|
||||
"maxLength": 2083,
|
||||
"minLength": 1,
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
},
|
||||
"name": "callback_url",
|
||||
"in": "query",
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
"callbacks": {
|
||||
"callback_route": {
|
||||
"{$callback_url}/callback/": {
|
||||
"get": {
|
||||
"summary": "Callback Route",
|
||||
"operationId": "callback_route__callback_url__callback__get",
|
||||
"responses": {
|
||||
"400": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CustomModel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Bad Request",
|
||||
},
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"CustomModel": {
|
||||
"title": "CustomModel",
|
||||
"required": ["a"],
|
||||
"type": "object",
|
||||
"properties": {"a": {"title": "A", "type": "integer"}},
|
||||
},
|
||||
"HTTPValidationError": {
|
||||
"title": "HTTPValidationError",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"detail": {
|
||||
"title": "Detail",
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||
}
|
||||
},
|
||||
},
|
||||
"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"},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == openapi_schema
|
||||
@@ -1078,6 +1078,18 @@ openapi_schema = {
|
||||
],
|
||||
}
|
||||
},
|
||||
"/enum-status-code": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
},
|
||||
"summary": "Get Enum Status Code",
|
||||
"operationId": "get_enum_status_code_enum_status_code_get",
|
||||
}
|
||||
},
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
@@ -1149,3 +1161,9 @@ def test_redoc():
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.headers["content-type"] == "text/html; charset=utf-8"
|
||||
assert "redoc@next" in response.text
|
||||
|
||||
|
||||
def test_enum_status_code_response():
|
||||
response = client.get("/enum-status-code")
|
||||
assert response.status_code == 201, response.text
|
||||
assert response.json() == "foo bar"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from typing import AsyncGenerator, Generator
|
||||
|
||||
import pytest
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
@@ -10,11 +12,21 @@ class CallableDependency:
|
||||
return value
|
||||
|
||||
|
||||
class CallableGenDependency:
|
||||
def __call__(self, value: str) -> Generator[str, None, None]:
|
||||
yield value
|
||||
|
||||
|
||||
class AsyncCallableDependency:
|
||||
async def __call__(self, value: str) -> str:
|
||||
return value
|
||||
|
||||
|
||||
class AsyncCallableGenDependency:
|
||||
async def __call__(self, value: str) -> AsyncGenerator[str, None]:
|
||||
yield value
|
||||
|
||||
|
||||
class MethodsDependency:
|
||||
def synchronous(self, value: str) -> str:
|
||||
return value
|
||||
@@ -22,9 +34,17 @@ class MethodsDependency:
|
||||
async def asynchronous(self, value: str) -> str:
|
||||
return value
|
||||
|
||||
def synchronous_gen(self, value: str) -> Generator[str, None, None]:
|
||||
yield value
|
||||
|
||||
async def asynchronous_gen(self, value: str) -> AsyncGenerator[str, None]:
|
||||
yield value
|
||||
|
||||
|
||||
callable_dependency = CallableDependency()
|
||||
callable_gen_dependency = CallableGenDependency()
|
||||
async_callable_dependency = AsyncCallableDependency()
|
||||
async_callable_gen_dependency = AsyncCallableGenDependency()
|
||||
methods_dependency = MethodsDependency()
|
||||
|
||||
|
||||
@@ -33,11 +53,23 @@ async def get_callable_dependency(value: str = Depends(callable_dependency)):
|
||||
return value
|
||||
|
||||
|
||||
@app.get("/callable-gen-dependency")
|
||||
async def get_callable_gen_dependency(value: str = Depends(callable_gen_dependency)):
|
||||
return value
|
||||
|
||||
|
||||
@app.get("/async-callable-dependency")
|
||||
async def get_callable_dependency(value: str = Depends(async_callable_dependency)):
|
||||
return value
|
||||
|
||||
|
||||
@app.get("/async-callable-gen-dependency")
|
||||
async def get_callable_gen_dependency(
|
||||
value: str = Depends(async_callable_gen_dependency),
|
||||
):
|
||||
return value
|
||||
|
||||
|
||||
@app.get("/synchronous-method-dependency")
|
||||
async def get_synchronous_method_dependency(
|
||||
value: str = Depends(methods_dependency.synchronous),
|
||||
@@ -45,6 +77,13 @@ async def get_synchronous_method_dependency(
|
||||
return value
|
||||
|
||||
|
||||
@app.get("/synchronous-method-gen-dependency")
|
||||
async def get_synchronous_method_gen_dependency(
|
||||
value: str = Depends(methods_dependency.synchronous_gen),
|
||||
):
|
||||
return value
|
||||
|
||||
|
||||
@app.get("/asynchronous-method-dependency")
|
||||
async def get_asynchronous_method_dependency(
|
||||
value: str = Depends(methods_dependency.asynchronous),
|
||||
@@ -52,6 +91,13 @@ async def get_asynchronous_method_dependency(
|
||||
return value
|
||||
|
||||
|
||||
@app.get("/asynchronous-method-gen-dependency")
|
||||
async def get_asynchronous_method_gen_dependency(
|
||||
value: str = Depends(methods_dependency.asynchronous_gen),
|
||||
):
|
||||
return value
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
@@ -59,9 +105,13 @@ client = TestClient(app)
|
||||
"route,value",
|
||||
[
|
||||
("/callable-dependency", "callable-dependency"),
|
||||
("/callable-gen-dependency", "callable-gen-dependency"),
|
||||
("/async-callable-dependency", "async-callable-dependency"),
|
||||
("/async-callable-gen-dependency", "async-callable-gen-dependency"),
|
||||
("/synchronous-method-dependency", "synchronous-method-dependency"),
|
||||
("/synchronous-method-gen-dependency", "synchronous-method-gen-dependency"),
|
||||
("/asynchronous-method-dependency", "asynchronous-method-dependency"),
|
||||
("/asynchronous-method-gen-dependency", "asynchronous-method-gen-dependency"),
|
||||
],
|
||||
)
|
||||
def test_class_dependency(route, value):
|
||||
|
||||
65
tests/test_dependency_security_overrides.py
Normal file
65
tests/test_dependency_security_overrides.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from typing import List, Tuple
|
||||
|
||||
from fastapi import Depends, FastAPI, Security
|
||||
from fastapi.security import SecurityScopes
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def get_user(required_scopes: SecurityScopes):
|
||||
return "john", required_scopes.scopes
|
||||
|
||||
|
||||
def get_user_override(required_scopes: SecurityScopes):
|
||||
return "alice", required_scopes.scopes
|
||||
|
||||
|
||||
def get_data():
|
||||
return [1, 2, 3]
|
||||
|
||||
|
||||
def get_data_override():
|
||||
return [3, 4, 5]
|
||||
|
||||
|
||||
@app.get("/user")
|
||||
def read_user(
|
||||
user_data: Tuple[str, List[str]] = Security(get_user, scopes=["foo", "bar"]),
|
||||
data: List[int] = Depends(get_data),
|
||||
):
|
||||
return {"user": user_data[0], "scopes": user_data[1], "data": data}
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_normal():
|
||||
response = client.get("/user")
|
||||
assert response.json() == {
|
||||
"user": "john",
|
||||
"scopes": ["foo", "bar"],
|
||||
"data": [1, 2, 3],
|
||||
}
|
||||
|
||||
|
||||
def test_override_data():
|
||||
app.dependency_overrides[get_data] = get_data_override
|
||||
response = client.get("/user")
|
||||
assert response.json() == {
|
||||
"user": "john",
|
||||
"scopes": ["foo", "bar"],
|
||||
"data": [3, 4, 5],
|
||||
}
|
||||
app.dependency_overrides = {}
|
||||
|
||||
|
||||
def test_override_security():
|
||||
app.dependency_overrides[get_user] = get_user_override
|
||||
response = client.get("/user")
|
||||
assert response.json() == {
|
||||
"user": "alice",
|
||||
"scopes": ["foo", "bar"],
|
||||
"data": [1, 2, 3],
|
||||
}
|
||||
app.dependency_overrides = {}
|
||||
@@ -76,6 +76,10 @@ class ModelWithDefault(BaseModel):
|
||||
bla: str = "bla"
|
||||
|
||||
|
||||
class ModelWithRoot(BaseModel):
|
||||
__root__: str
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
name="model_with_path", params=[PurePath, PurePosixPath, PureWindowsPath]
|
||||
)
|
||||
@@ -158,3 +162,8 @@ def test_encode_model_with_path(model_with_path):
|
||||
else:
|
||||
expected = "/foo/bar"
|
||||
assert jsonable_encoder(model_with_path) == {"path": expected}
|
||||
|
||||
|
||||
def test_encode_root():
|
||||
model = ModelWithRoot(__root__="Foo")
|
||||
assert jsonable_encoder(model) == "Foo"
|
||||
|
||||
@@ -104,7 +104,7 @@ single_error = {
|
||||
"detail": [
|
||||
{
|
||||
"ctx": {"limit_value": 0.0},
|
||||
"loc": ["body", "item", 0, "age"],
|
||||
"loc": ["body", 0, "age"],
|
||||
"msg": "ensure this value is greater than 0",
|
||||
"type": "value_error.number.not_gt",
|
||||
}
|
||||
@@ -114,22 +114,22 @@ single_error = {
|
||||
multiple_errors = {
|
||||
"detail": [
|
||||
{
|
||||
"loc": ["body", "item", 0, "name"],
|
||||
"loc": ["body", 0, "name"],
|
||||
"msg": "field required",
|
||||
"type": "value_error.missing",
|
||||
},
|
||||
{
|
||||
"loc": ["body", "item", 0, "age"],
|
||||
"loc": ["body", 0, "age"],
|
||||
"msg": "value is not a valid decimal",
|
||||
"type": "type_error.decimal",
|
||||
},
|
||||
{
|
||||
"loc": ["body", "item", 1, "name"],
|
||||
"loc": ["body", 1, "name"],
|
||||
"msg": "field required",
|
||||
"type": "value_error.missing",
|
||||
},
|
||||
{
|
||||
"loc": ["body", "item", 1, "age"],
|
||||
"loc": ["body", 1, "age"],
|
||||
"msg": "value is not a valid decimal",
|
||||
"type": "type_error.decimal",
|
||||
},
|
||||
|
||||
60
tests/test_openapi_servers.py
Normal file
60
tests/test_openapi_servers.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
app = FastAPI(
|
||||
servers=[
|
||||
{"url": "/", "description": "Default, relative server"},
|
||||
{
|
||||
"url": "http://staging.localhost.tiangolo.com:8000",
|
||||
"description": "Staging but actually localhost still",
|
||||
},
|
||||
{"url": "https://prod.example.com"},
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@app.get("/foo")
|
||||
def foo():
|
||||
return {"message": "Hello World"}
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
openapi_schema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"servers": [
|
||||
{"url": "/", "description": "Default, relative server"},
|
||||
{
|
||||
"url": "http://staging.localhost.tiangolo.com:8000",
|
||||
"description": "Staging but actually localhost still",
|
||||
},
|
||||
{"url": "https://prod.example.com"},
|
||||
],
|
||||
"paths": {
|
||||
"/foo": {
|
||||
"get": {
|
||||
"summary": "Foo",
|
||||
"operationId": "foo_foo_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_openapi_servers():
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == openapi_schema
|
||||
|
||||
|
||||
def test_app():
|
||||
response = client.get("/foo")
|
||||
assert response.status_code == 200, response.text
|
||||
46
tests/test_params_repr.py
Normal file
46
tests/test_params_repr.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import pytest
|
||||
from fastapi.params import Body, Cookie, Depends, Header, Param, Path, Query
|
||||
|
||||
test_data = ["teststr", None, ..., 1, []]
|
||||
|
||||
|
||||
def get_user():
|
||||
return {} # pragma: no cover
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", params=test_data)
|
||||
def params(request):
|
||||
return request.param
|
||||
|
||||
|
||||
def test_param_repr(params):
|
||||
assert repr(Param(params)) == "Param(" + str(params) + ")"
|
||||
|
||||
|
||||
def test_path_repr(params):
|
||||
assert repr(Path(params)) == "Path(Ellipsis)"
|
||||
|
||||
|
||||
def test_query_repr(params):
|
||||
assert repr(Query(params)) == "Query(" + str(params) + ")"
|
||||
|
||||
|
||||
def test_header_repr(params):
|
||||
assert repr(Header(params)) == "Header(" + str(params) + ")"
|
||||
|
||||
|
||||
def test_cookie_repr(params):
|
||||
assert repr(Cookie(params)) == "Cookie(" + str(params) + ")"
|
||||
|
||||
|
||||
def test_body_repr(params):
|
||||
assert repr(Body(params)) == "Body(" + str(params) + ")"
|
||||
|
||||
|
||||
def test_depends_repr():
|
||||
assert repr(Depends()) == "Depends(NoneType)"
|
||||
assert repr(Depends(get_user)) == "Depends(get_user)"
|
||||
assert repr(Depends(use_cache=False)) == "Depends(NoneType, use_cache=False)"
|
||||
assert (
|
||||
repr(Depends(get_user, use_cache=False)) == "Depends(get_user, use_cache=False)"
|
||||
)
|
||||
34
tests/test_repeated_cookie_headers.py
Normal file
34
tests/test_repeated_cookie_headers.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from fastapi import Depends, FastAPI, Response
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def set_cookie(*, response: Response):
|
||||
response.set_cookie("cookie-name", "cookie-value")
|
||||
return {}
|
||||
|
||||
|
||||
def set_indirect_cookie(*, dep: str = Depends(set_cookie)):
|
||||
return dep
|
||||
|
||||
|
||||
@app.get("/directCookie")
|
||||
def get_direct_cookie(dep: str = Depends(set_cookie)):
|
||||
return {"dep": dep}
|
||||
|
||||
|
||||
@app.get("/indirectCookie")
|
||||
def get_indirect_cookie(dep: str = Depends(set_indirect_cookie)):
|
||||
return {"dep": dep}
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_cookie_is_set_once():
|
||||
direct_response = client.get("/directCookie")
|
||||
indirect_response = client.get("/indirectCookie")
|
||||
assert (
|
||||
direct_response.headers["set-cookie"] == indirect_response.headers["set-cookie"]
|
||||
)
|
||||
@@ -1,7 +1,6 @@
|
||||
import pytest
|
||||
from fastapi import Depends, FastAPI, Security
|
||||
from fastapi.security import OAuth2
|
||||
from fastapi.security.oauth2 import OAuth2PasswordRequestFormStrict
|
||||
from fastapi.security import OAuth2, OAuth2PasswordRequestFormStrict
|
||||
from fastapi.testclient import TestClient
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@ from typing import Optional
|
||||
|
||||
import pytest
|
||||
from fastapi import Depends, FastAPI, Security
|
||||
from fastapi.security import OAuth2
|
||||
from fastapi.security.oauth2 import OAuth2PasswordRequestFormStrict
|
||||
from fastapi.security import OAuth2, OAuth2PasswordRequestFormStrict
|
||||
from fastapi.testclient import TestClient
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ openapi_schema = {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"description": "Return the JSON item or an image.",
|
||||
"content": {
|
||||
"image/png": {},
|
||||
"application/json": {
|
||||
|
||||
@@ -20,7 +20,7 @@ openapi_schema = {
|
||||
},
|
||||
},
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"description": "Item requested by ID",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {"$ref": "#/components/schemas/Item"},
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
@@ -92,7 +94,7 @@ def test_openapi_schema():
|
||||
price_missing = {
|
||||
"detail": [
|
||||
{
|
||||
"loc": ["body", "item", "price"],
|
||||
"loc": ["body", "price"],
|
||||
"msg": "field required",
|
||||
"type": "value_error.missing",
|
||||
}
|
||||
@@ -102,7 +104,7 @@ price_missing = {
|
||||
price_not_float = {
|
||||
"detail": [
|
||||
{
|
||||
"loc": ["body", "item", "price"],
|
||||
"loc": ["body", "price"],
|
||||
"msg": "value is not a valid float",
|
||||
"type": "type_error.float",
|
||||
}
|
||||
@@ -112,12 +114,12 @@ price_not_float = {
|
||||
name_price_missing = {
|
||||
"detail": [
|
||||
{
|
||||
"loc": ["body", "item", "name"],
|
||||
"loc": ["body", "name"],
|
||||
"msg": "field required",
|
||||
"type": "value_error.missing",
|
||||
},
|
||||
{
|
||||
"loc": ["body", "item", "price"],
|
||||
"loc": ["body", "price"],
|
||||
"msg": "field required",
|
||||
"type": "value_error.missing",
|
||||
},
|
||||
@@ -126,11 +128,7 @@ name_price_missing = {
|
||||
|
||||
body_missing = {
|
||||
"detail": [
|
||||
{
|
||||
"loc": ["body", "item"],
|
||||
"msg": "field required",
|
||||
"type": "value_error.missing",
|
||||
}
|
||||
{"loc": ["body"], "msg": "field required", "type": "value_error.missing",}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -176,5 +174,24 @@ def test_post_body(path, body, expected_status, expected_response):
|
||||
|
||||
def test_post_broken_body():
|
||||
response = client.post("/items/", data={"name": "Foo", "price": 50.5})
|
||||
assert response.status_code == 400, response.text
|
||||
assert response.status_code == 422, response.text
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
{
|
||||
"ctx": {
|
||||
"colno": 1,
|
||||
"doc": "name=Foo&price=50.5",
|
||||
"lineno": 1,
|
||||
"msg": "Expecting value",
|
||||
"pos": 0,
|
||||
},
|
||||
"loc": ["body", 0],
|
||||
"msg": "Expecting value: line 1 column 1 (char 0)",
|
||||
"type": "value_error.jsondecode",
|
||||
}
|
||||
]
|
||||
}
|
||||
with patch("json.loads", side_effect=Exception):
|
||||
response = client.post("/items/", json={"test": "test2"})
|
||||
assert response.status_code == 400, response.text
|
||||
assert response.json() == {"detail": "There was an error parsing the body"}
|
||||
|
||||
@@ -95,7 +95,7 @@ def test_post_invalid_body():
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
{
|
||||
"loc": ["body", "weights", "__key__"],
|
||||
"loc": ["body", "__key__"],
|
||||
"msg": "value is not a valid integer",
|
||||
"type": "type_error.integer",
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user