Compare commits

...

35 Commits

Author SHA1 Message Date
Sebastián Ramírez
8231fbede4 🔖 Release version 0.56.1 2020-06-13 01:17:06 +02:00
Sebastián Ramírez
50bc14b835 📝 Update release notes 2020-06-13 01:14:58 +02:00
Kai Chen
4310c89c83 📝 Add link to Advanced User Guide: response status code (#1512) 2020-06-13 01:12:59 +02:00
Sebastián Ramírez
d39dd06a22 📝 Update release notes 2020-06-13 00:57:34 +02:00
kota matsuoka
a0ab47e89e 🎨 Remove unused f-string (#1526) 2020-06-13 00:56:00 +02:00
Sebastián Ramírez
5cbcb9a965 📝 Update release notes 2020-06-13 00:55:30 +02:00
Xie Wei
801ceaec80 🌐 Add Chinese translation for query-params-str-validations.md (#1500)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-13 00:53:00 +02:00
Sebastián Ramírez
c7334ae9f8 📝 Update release notes 2020-06-13 00:50:22 +02:00
Xie Wei
d737599a2c 🌐 Add Chinese translation for body.md (#1492)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-13 00:47:50 +02:00
Sebastián Ramírez
d2d72a8e4a 📝 Update release notes 2020-06-13 00:43:07 +02:00
Xie Wei
7895c12fa1 🌐 Add Chinese translation for help-fastapi.md (#1465)
* add chinese translation for help-fastapi.md

* improve translations

Co-authored-by: Waynerv <wei.xie@woqutech.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-13 00:40:05 +02:00
Sebastián Ramírez
5f6a14c413 📝 Update release notes 2020-06-13 00:29:43 +02:00
Xie Wei
2b4e88fa98 🌐 Add Chinese translation for query-params.md (#1454)
Co-authored-by: Waynerv <wei.xie@woqutech.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-13 00:26:40 +02:00
Sebastián Ramírez
11723bca27 📝 Update release notes 2020-06-13 00:21:53 +02:00
Xie Wei
b49517a64f 🌐 Add Chinese translation for contributing.md (#1460)
Co-authored-by: Waynerv <wei.xie@woqutech.com>
2020-06-13 00:18:57 +02:00
Sebastián Ramírez
f910e0c96c 📝 Update release notes 2020-06-13 00:18:20 +02:00
Xie Wei
c1ba2a3127 🌐 Add Chinese translation for path-params.md (#1453)
* add chinese translation for path-params.md

* improve translations

* improve translations

Co-authored-by: Waynerv <wei.xie@woqutech.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-13 00:14:58 +02:00
Sebastián Ramírez
28396173c7 📝 Update release notes 2020-06-13 00:09:41 +02:00
Kabir Khan
69974b792e 📝 Add cookiecutter-spacy-fastapi to docs (#1390) 2020-06-13 00:06:53 +02:00
Sebastián Ramírez
352412a3cb 📝 Update release notes 2020-06-12 23:46:05 +02:00
yaegassy
745ab48d65 📝 Add docs in Python Types for Optional (#1377)
* docs: Fix pydantic example in python-types.md

* 📝 Update Python Types Intro to include Optional

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-12 23:44:23 +02:00
Sebastián Ramírez
4a5cda0d77 📝 Update release notes 2020-06-12 23:00:09 +02:00
Micah Rosales
b90bf2da9e 🐛 Fix callable class generator dependencies (#1365)
* Fix callable class generator dependencies

* workaround to support asynccontextmanager backfill for pre python3.7

Co-authored-by: Micah Rosales <mrosales@users.noreply.github.com>
2020-06-12 22:57:59 +02:00
Sebastián Ramírez
a552cbdf59 📝 Update release notes 2020-06-12 22:47:37 +02:00
Dylan Anthony
2351fb5623 🔇 Remove error log when parsing malformed JSON body as it's a client error (#1351) 2020-06-12 22:44:40 +02:00
Sebastián Ramírez
807522c616 📝 Update release notes 2020-06-12 22:42:40 +02:00
Xie Wei
81a529c251 🌐 Translate doc first steps to Chinese (#1323)
* WIP:add Chinese translation for first steps doc

* add Chinese translation for first steps doc

* improve translations

Co-authored-by: Waynerv <wei.xie@woqutech.com>
2020-06-12 22:39:26 +02:00
Sebastián Ramírez
7efc15aeef 📝 Update release notes 2020-06-12 22:37:34 +02:00
Nik
d66d8379c0 🐛 Fix OpenAPI generation when using callbacks with routers including Pydantic models (#1322)
* drop model class from additional responses when generating openapi

* ♻️ Copy response to be mutated early in get_openapi_path

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-12 22:35:59 +02:00
Sebastián Ramírez
5a00467951 📝 Update release notes 2020-06-12 22:01:22 +02:00
Kazantcev Andrey
434d32b891 Optimize regexp pattern in get_path_param_names (#1243) 2020-06-12 21:59:32 +02:00
Sebastián Ramírez
535247ffc4 📝 Update release notes 2020-06-12 21:43:17 +02:00
Pankaj Giri
7e2518350a 📝 Remove *, from functions where it's not needed #1234 (#1239)
* Fix for - [FEATURE] Remove *, where it's not needed #1234

* 🔥 Remove unnecessary arg *,

* 🎨 Update docs format highlight lines

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-12 21:41:44 +02:00
Sebastián Ramírez
1b2a7546af 📝 Update release notes 2020-06-12 18:58:15 +02:00
Cesare De Cal
2d9bb64047 🌐 Generated new translation directory to support Italian docs (#1557)
* Generated new translation directory to support Italian docs

* ⬆️ Upgrade/pin pytest to >= 5.4.3

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-12 18:53:52 +02:00
60 changed files with 2740 additions and 91 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -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`:

View File

@@ -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.

View File

@@ -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!}
```

View File

@@ -22,6 +22,7 @@ nav:
- Languages:
- en: /
- es: /es/
- it: /it/
- pt: /pt/
- zh: /zh/
- features.md

View File

@@ -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
View 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>
---
"_Im over the moon excited about **FastAPI**. Its so fun!_"
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
---
"_Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that._"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="http://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
"_If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]_"
"_We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]_"
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <a href="https://spacy.io" target="_blank">spaCy</a> creators</strong> <a href="https://twitter.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://twitter.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
---
## **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>):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### Alternative API docs
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
You will see the alternative automatic documentation (provided by <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## Example upgrade
Now modify the file `main.py` to receive a body from a `PUT` request.
Declare the body using standard Python types, thanks to Pydantic.
```Python hl_lines="2 7 8 9 10 23 24 25"
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: bool = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
The server should reload automatically (because you added `--reload` to the `uvicorn` command above).
### Interactive API docs upgrade
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
* The interactive API documentation will be automatically updated, including the new body:
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* Click on the button "Try it out", it allows you to fill the parameters and directly interact with the API:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-04-swagger-03.png)
* Then click on the "Execute" button, the user interface will communicate with your API, send the parameters, get the results and show them on the screen:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png)
### Alternative API docs upgrade
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
* The alternative documentation will also reflect the new query parameter and body:
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### Recap
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
You do that with standard modern Python types.
You don't have to learn a new syntax, the methods or classes of a specific library, etc.
Just standard **Python 3.6+**.
For example, for an `int`:
```Python
item_id: int
```
or for a more complex `Item` model:
```Python
item: Item
```
...and with that single declaration you get:
* Editor support, including:
* Completion.
* Type checks.
* Validation of data:
* Automatic and clear errors when the data is invalid.
* Validation even for deeply nested JSON objects.
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of input data: coming from the network to Python data and types. Reading from:
* JSON.
* Path parameters.
* Query parameters.
* Cookies.
* Headers.
* Forms.
* Files.
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of output data: converting from Python data and types to network data (as JSON):
* Convert Python types (`str`, `int`, `float`, `bool`, `list`, etc).
* `datetime` objects.
* `UUID` objects.
* Database models.
* ...and many more.
* Automatic interactive API documentation, including 2 alternative user interfaces:
* Swagger UI.
* ReDoc.
---
Coming back to the previous code example, **FastAPI** will:
* Validate that there is an `item_id` in the path for `GET` and `PUT` requests.
* Validate that the `item_id` is of type `int` for `GET` and `PUT` requests.
* If it is not, the client will see a useful, clear error.
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`) for `GET` requests.
* As the `q` parameter is declared with `= None`, it is optional.
* Without the `None` it would be required (as is the body in the case with `PUT`).
* For `PUT` requests to `/items/{item_id}`, Read the body as JSON:
* Check that it has a required attribute `name` that should be a `str`.
* Check that it has a required attribute `price` that has to be a `float`.
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
* All this would also work for deeply nested JSON objects.
* Convert from and to JSON automatically.
* Document everything with OpenAPI, that can be used by:
* Interactive documentation systems.
* Automatic client code generation systems, for many languages.
* Provide 2 interactive documentation web interfaces directly.
---
We just scratched the surface, but you already get the idea of how it all works.
Try changing the line with:
```Python
return {"item_name": item.name, "item_id": item_id}
```
...from:
```Python
... "item_name": item.name ...
```
...to:
```Python
... "item_price": item.price ...
```
...and see how your editor will auto-complete the attributes and know their types:
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
For a more complete example including more features, see the <a href="https://fastapi.tiangolo.com/tutorial/">Tutorial - User Guide</a>.
**Spoiler alert**: the tutorial - user guide includes:
* Declaration of **parameters** from other different places as: **headers**, **cookies**, **form fields** and **files**.
* How to set **validation constraints** as `maximum_length` or `regex`.
* A very powerful and easy to use **<abbr title="also known as components, resources, providers, services, injectables">Dependency Injection</abbr>** system.
* Security and authentication, including support for **OAuth2** with **JWT tokens** and **HTTP Basic** auth.
* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic).
* Many extra features (thanks to Starlette) as:
* **WebSockets**
* **GraphQL**
* extremely easy tests based on `requests` and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
## Performance
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
To understand more about it, see the section <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
## Optional Dependencies
Used by Pydantic:
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - for faster JSON <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>.
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email_validator</code></a> - for email validation.
Used by Starlette:
* <a href="http://docs.python-requests.org" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
* <a href="https://github.com/Tinche/aiofiles" target="_blank"><code>aiofiles</code></a> - Required if you want to use `FileResponse` or `StaticFiles`.
* <a href="http://jinja.pocoo.org" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Required for Starlette's `SchemaGenerator` support (you probably don't need it with FastAPI).
* <a href="https://graphene-python.org/" target="_blank"><code>graphene</code></a> - Required for `GraphQLApp` support.
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Required if you want to use `UJSONResponse`.
Used by FastAPI / Starlette:
* <a href="http://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - for the server that loads and serves your application.
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Required if you want to use `ORJSONResponse`.
You can install all of these with `pip install fastapi[all]`.
## License
This project is licensed under the terms of the MIT license.

66
docs/it/mkdocs.yml Normal file
View 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

View File

@@ -22,6 +22,7 @@ nav:
- Languages:
- en: /
- es: /es/
- it: /it/
- pt: /pt/
- zh: /zh/
- features.md

View 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
```

View 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> 在经济上支持作者(我)。
这样你可以给我买杯咖啡☕️以示谢意😄。
---
感谢!

View 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}。

View 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> 提供):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### 可选的 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> 提供)
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
### 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`)。

View 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** 与其他框架相比主要的明显优势(除了原始性能以外)。

View 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` 值的校验。
请参阅下一章节,以了解如何声明对其他类型例如数值的校验。

View 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!}
```

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -28,5 +28,5 @@ class Offer(BaseModel):
@app.post("/offers/")
async def create_offer(*, offer: Offer):
async def create_offer(offer: Offer):
return offer

View File

@@ -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

View File

@@ -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}

View File

@@ -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

View File

@@ -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

View File

@@ -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}

View File

@@ -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}

View File

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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

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

View File

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

View File

@@ -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")

View File

@@ -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

View 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

View File

@@ -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}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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(

View File

@@ -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"

View File

@@ -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(

View File

@@ -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(

View File

@@ -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",

View 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

View File

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