mirror of
https://github.com/fastapi/fastapi.git
synced 2025-12-27 08:10:57 -05:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -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,26 @@
|
||||
|
||||
## Latest changes
|
||||
|
||||
## 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`:
|
||||
|
||||
@@ -83,4 +83,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,6 +22,7 @@ nav:
|
||||
- Languages:
|
||||
- en: /
|
||||
- es: /es/
|
||||
- it: /it/
|
||||
- pt: /pt/
|
||||
- zh: /zh/
|
||||
- features.md
|
||||
|
||||
@@ -22,6 +22,7 @@ nav:
|
||||
- Languages:
|
||||
- en: /
|
||||
- es: /es/
|
||||
- it: /it/
|
||||
- pt: /pt/
|
||||
- zh: /zh/
|
||||
- features.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.
|
||||
66
docs/it/mkdocs.yml
Normal file
66
docs/it/mkdocs.yml
Normal file
@@ -0,0 +1,66 @@
|
||||
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/
|
||||
- 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,6 +22,7 @@ nav:
|
||||
- Languages:
|
||||
- en: /
|
||||
- es: /es/
|
||||
- it: /it/
|
||||
- pt: /pt/
|
||||
- zh: /zh/
|
||||
- features.md
|
||||
|
||||
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,21 @@ nav:
|
||||
- Languages:
|
||||
- en: /
|
||||
- es: /es/
|
||||
- it: /it/
|
||||
- pt: /pt/
|
||||
- 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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
|
||||
|
||||
__version__ = "0.56.0"
|
||||
__version__ = "0.56.1"
|
||||
|
||||
from starlette import status
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -505,7 +527,7 @@ async def solve_dependencies(
|
||||
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(
|
||||
|
||||
@@ -203,27 +203,31 @@ def get_openapi_path(
|
||||
operation["callbacks"] = callbacks
|
||||
if route.responses:
|
||||
for (additional_status_code, response) in route.responses.items():
|
||||
process_response = response.copy()
|
||||
assert isinstance(
|
||||
response, dict
|
||||
process_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(
|
||||
process_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(
|
||||
process_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
|
||||
process_response.pop("model", None)
|
||||
operation.setdefault("responses", {})[
|
||||
status_code_key
|
||||
] = process_response
|
||||
status_code = str(route.status_code)
|
||||
operation.setdefault("responses", {}).setdefault(status_code, {})[
|
||||
"description"
|
||||
|
||||
@@ -179,7 +179,6 @@ def get_request_handler(
|
||||
if body_bytes:
|
||||
body = await request.json()
|
||||
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
|
||||
@@ -366,7 +365,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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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",
|
||||
|
||||
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
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user