From 263766548dfe75d38a67f578b0a72f536b3e2092 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:26 +0800 Subject: [PATCH 001/163] New translations additional-responses.md (Chinese Simplified) --- docs/zh/docs/advanced/additional-responses.md | 240 ++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 docs/zh/docs/advanced/additional-responses.md diff --git a/docs/zh/docs/advanced/additional-responses.md b/docs/zh/docs/advanced/additional-responses.md new file mode 100644 index 0000000000000..c76a92f238c37 --- /dev/null +++ b/docs/zh/docs/advanced/additional-responses.md @@ -0,0 +1,240 @@ +# Additional Responses in OpenAPI + +!!! warning + This is a rather advanced topic. + + If you are starting with **FastAPI**, you might not need this. + +You can declare additional responses, with additional status codes, media types, descriptions, etc. + +Those additional responses will be included in the OpenAPI schema, so they will also appear in the API docs. + +But for those additional responses you have to make sure you return a `Response` like `JSONResponse` directly, with your status code and content. + +## Additional Response with `model` + +You can pass to your *path operation decorators* a parameter `responses`. + +It receives a `dict`, the keys are status codes for each response, like `200`, and the values are other `dict`s with the information for each of them. + +Each of those response `dict`s can have a key `model`, containing a Pydantic model, just like `response_model`. + +**FastAPI** will take that model, generate its JSON Schema and include it in the correct place in OpenAPI. + +For example, to declare another response with a status code `404` and a Pydantic model `Message`, you can write: + +```Python hl_lines="18 22" +{!../../../docs_src/additional_responses/tutorial001.py!} +``` + +!!! note + Have in mind that you have to return the `JSONResponse` directly. + +!!! info + The `model` key is not part of OpenAPI. + + **FastAPI** will take the Pydantic model from there, generate the `JSON Schema`, and put it in the correct place. + + The correct place is: + + * In the key `content`, that has as value another JSON object (`dict`) that contains: + * A key with the media type, e.g. `application/json`, that contains as value another JSON object, that contains: + * A key `schema`, that has as the value the JSON Schema from the model, here's the correct place. + * **FastAPI** adds a reference here to the global JSON Schemas in another place in your OpenAPI instead of including it directly. This way, other applications and clients can use those JSON Schemas directly, provide better code generation tools, etc. + +The generated responses in the OpenAPI for this *path operation* will be: + +```JSON hl_lines="3-12" +{ + "responses": { + "404": { + "description": "Additional Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Item" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } +} +``` + +The schemas are referenced to another place inside the OpenAPI schema: + +```JSON hl_lines="4-16" +{ + "components": { + "schemas": { + "Message": { + "title": "Message", + "required": [ + "message" + ], + "type": "object", + "properties": { + "message": { + "title": "Message", + "type": "string" + } + } + }, + "Item": { + "title": "Item", + "required": [ + "id", + "value" + ], + "type": "object", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "value": { + "title": "Value", + "type": "string" + } + } + }, + "ValidationError": { + "title": "ValidationError", + "required": [ + "loc", + "msg", + "type" + ], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "type": "string" + } + }, + "msg": { + "title": "Message", + "type": "string" + }, + "type": { + "title": "Error Type", + "type": "string" + } + } + }, + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + } + } + } + } + } + } +} +``` + +## Additional media types for the main response + +You can use this same `responses` parameter to add different media types for the same main response. + +For example, you can add an additional media type of `image/png`, declaring that your *path operation* can return a JSON object (with media type `application/json`) or a PNG image: + +```Python hl_lines="19-24 28" +{!../../../docs_src/additional_responses/tutorial002.py!} +``` + +!!! note + Notice that you have to return the image using a `FileResponse` directly. + +!!! info + Unless you specify a different media type explicitly in your `responses` parameter, FastAPI will assume the response has the same media type as the main response class (default `application/json`). + + But if you have specified a custom response class with `None` as its media type, FastAPI will use `application/json` for any additional response that has an associated model. + +## Combining information + +You can also combine response information from multiple places, including the `response_model`, `status_code`, and `responses` parameters. + +You can declare a `response_model`, using the default status code `200` (or a custom one if you need), and then declare additional information for that same response in `responses`, directly in the OpenAPI schema. + +**FastAPI** will keep the additional information from `responses`, and combine it with the JSON Schema from your model. + +For example, you can declare a response with a status code `404` that uses a Pydantic model and has a custom `description`. + +And a response with a status code `200` that uses your `response_model`, but includes a custom `example`: + +```Python hl_lines="20-31" +{!../../../docs_src/additional_responses/tutorial003.py!} +``` + +It will all be combined and included in your OpenAPI, and shown in the API docs: + + + +## Combine predefined responses and custom ones + +You might want to have some predefined responses that apply to many *path operations*, but you want to combine them with custom responses needed by each *path operation*. + +For those cases, you can use the Python technique of "unpacking" a `dict` with `**dict_to_unpack`: + +```Python +old_dict = { + "old key": "old value", + "second old key": "second old value", +} +new_dict = {**old_dict, "new key": "new value"} +``` + +Here, `new_dict` will contain all the key-value pairs from `old_dict` plus the new key-value pair: + +```Python +{ + "old key": "old value", + "second old key": "second old value", + "new key": "new value", +} +``` + +You can use that technique to re-use some predefined responses in your *path operations* and combine them with additional custom ones. + +For example: + +```Python hl_lines="13-17 26" +{!../../../docs_src/additional_responses/tutorial004.py!} +``` + +## More information about OpenAPI responses + +To see what exactly you can include in the responses, you can check these sections in the OpenAPI specification: + +* OpenAPI Responses Object, it includes the `Response Object`. +* OpenAPI Response Object, you can include anything from this directly in each response inside your `responses` parameter. Including `description`, `headers`, `content` (inside of this is that you declare different media types and JSON Schemas), and `links`. From fc7c5bdf7ad5b69a073441aee0539d3b9c570157 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:27 +0800 Subject: [PATCH 002/163] New translations additional-status-codes.md (Chinese Simplified) --- .../docs/advanced/additional-status-codes.md | 74 +++++++++++++------ 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/docs/zh/docs/advanced/additional-status-codes.md b/docs/zh/docs/advanced/additional-status-codes.md index 54ec9775b6204..2bccdbb402b2b 100644 --- a/docs/zh/docs/advanced/additional-status-codes.md +++ b/docs/zh/docs/advanced/additional-status-codes.md @@ -1,37 +1,69 @@ -# 额外的状态码 +# Additional Status Codes -**FastAPI** 默认使用 `JSONResponse` 返回一个响应,将你的 *路径操作* 中的返回内容放到该 `JSONResponse` 中。 +By default, **FastAPI** will return the responses using a `JSONResponse`, putting the content you return from your *path operation* inside of that `JSONResponse`. -**FastAPI** 会自动使用默认的状态码或者使用你在 *路径操作* 中设置的状态码。 +It will use the default status code or the one you set in your *path operation*. -## 额外的状态码 +## Additional status codes -如果你想要返回主要状态码之外的状态码,你可以通过直接返回一个 `Response` 来实现,比如 `JSONResponse`,然后直接设置额外的状态码。 +If you want to return additional status codes apart from the main one, you can do that by returning a `Response` directly, like a `JSONResponse`, and set the additional status code directly. -例如,假设你想有一个 *路径操作* 能够更新条目,并且更新成功时返回 200 「成功」 的 HTTP 状态码。 +For example, let's say that you want to have a *path operation* that allows to update items, and returns HTTP status codes of 200 "OK" when successful. -但是你也希望它能够接受新的条目。并且当这些条目不存在时,会自动创建并返回 201 「创建」的 HTTP 状态码。 +But you also want it to accept new items. And when the items didn't exist before, it creates them, and returns an HTTP status code of 201 "Created". -要实现它,导入 `JSONResponse`,然后在其中直接返回你的内容,并将 `status_code` 设置为为你要的值。 +To achieve that, import `JSONResponse`, and return your content there directly, setting the `status_code` that you want: -```Python hl_lines="4 25" -{!../../../docs_src/additional_status_codes/tutorial001.py!} -``` +=== "Python 3.10+" -!!! warning "警告" - 当你直接返回一个像上面例子中的 `Response` 对象时,它会直接返回。 + ```Python hl_lines="4 25" + {!> ../../../docs_src/additional_status_codes/tutorial001_an_py310.py!} + ``` - FastAPI 不会用模型等对该响应进行序列化。 +=== "Python 3.9+" - 确保其中有你想要的数据,且返回的值为合法的 JSON(如果你使用 `JSONResponse` 的话)。 + ```Python hl_lines="4 25" + {!> ../../../docs_src/additional_status_codes/tutorial001_an_py39.py!} + ``` -!!! note "技术细节" - 你也可以使用 `from starlette.responses import JSONResponse`。  +=== "Python 3.6+" - 出于方便,**FastAPI** 为开发者提供同 `starlette.responses` 一样的 `fastapi.responses`。但是大多数可用的响应都是直接来自 Starlette。`status` 也是一样。 + ```Python hl_lines="4 26" + {!> ../../../docs_src/additional_status_codes/tutorial001_an.py!} + ``` -## OpenAPI 和 API 文档 +=== "Python 3.10+ non-Annotated" -如果你直接返回额外的状态码和响应,它们不会包含在 OpenAPI 方案(API 文档)中,因为 FastAPI 没办法预先知道你要返回什么。 + !!! tip + Prefer to use the `Annotated` version if possible. -但是你可以使用 [额外的响应](additional-responses.md){.internal-link target=_blank} 在代码中记录这些内容。 + ```Python hl_lines="2 23" + {!> ../../../docs_src/additional_status_codes/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="4 25" + {!> ../../../docs_src/additional_status_codes/tutorial001.py!} + ``` + +!!! warning + When you return a `Response` directly, like in the example above, it will be returned directly. + + It won't be serialized with a model, etc. + + Make sure it has the data you want it to have, and that the values are valid JSON (if you are using `JSONResponse`). + +!!! note "Technical Details" + You could also use `from starlette.responses import JSONResponse`. + + **FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. The same with `status`. + +## OpenAPI and API docs + +If you return additional status codes and responses directly, they won't be included in the OpenAPI schema (the API docs), because FastAPI doesn't have a way to know beforehand what you are going to return. + +But you can document that in your code, using: [Additional Responses](additional-responses.md){.internal-link target=_blank}. From 98209f5abd9b3ce5bbd1ac6628b34d36bdb8fa7e Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:28 +0800 Subject: [PATCH 003/163] New translations advanced-dependencies.md (Chinese Simplified) --- .../zh/docs/advanced/advanced-dependencies.md | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 docs/zh/docs/advanced/advanced-dependencies.md diff --git a/docs/zh/docs/advanced/advanced-dependencies.md b/docs/zh/docs/advanced/advanced-dependencies.md new file mode 100644 index 0000000000000..845713c30a966 --- /dev/null +++ b/docs/zh/docs/advanced/advanced-dependencies.md @@ -0,0 +1,138 @@ +# Advanced Dependencies + +## Parameterized dependencies + +All the dependencies we have seen are a fixed function or class. + +But there could be cases where you want to be able to set parameters on the dependency, without having to declare many different functions or classes. + +Let's imagine that we want to have a dependency that checks if the query parameter `q` contains some fixed content. + +But we want to be able to parameterize that fixed content. + +## A "callable" instance + +In Python there's a way to make an instance of a class a "callable". + +Not the class itself (which is already a callable), but an instance of that class. + +To do that, we declare a method `__call__`: + +=== "Python 3.9+" + + ```Python hl_lines="12" + {!> ../../../docs_src/dependencies/tutorial011_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="11" + {!> ../../../docs_src/dependencies/tutorial011_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="10" + {!> ../../../docs_src/dependencies/tutorial011.py!} + ``` + +In this case, this `__call__` is what **FastAPI** will use to check for additional parameters and sub-dependencies, and this is what will be called to pass a value to the parameter in your *path operation function* later. + +## Parameterize the instance + +And now, we can use `__init__` to declare the parameters of the instance that we can use to "parameterize" the dependency: + +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/dependencies/tutorial011_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="8" + {!> ../../../docs_src/dependencies/tutorial011_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7" + {!> ../../../docs_src/dependencies/tutorial011.py!} + ``` + +In this case, **FastAPI** won't ever touch or care about `__init__`, we will use it directly in our code. + +## Create an instance + +We could create an instance of this class with: + +=== "Python 3.9+" + + ```Python hl_lines="18" + {!> ../../../docs_src/dependencies/tutorial011_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="17" + {!> ../../../docs_src/dependencies/tutorial011_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="16" + {!> ../../../docs_src/dependencies/tutorial011.py!} + ``` + +And that way we are able to "parameterize" our dependency, that now has `"bar"` inside of it, as the attribute `checker.fixed_content`. + +## Use the instance as a dependency + +Then, we could use this `checker` in a `Depends(checker)`, instead of `Depends(FixedContentQueryChecker)`, because the dependency is the instance, `checker`, not the class itself. + +And when solving the dependency, **FastAPI** will call this `checker` like: + +```Python +checker(q="somequery") +``` + +...and pass whatever that returns as the value of the dependency in our *path operation function* as the parameter `fixed_content_included`: + +=== "Python 3.9+" + + ```Python hl_lines="22" + {!> ../../../docs_src/dependencies/tutorial011_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="21" + {!> ../../../docs_src/dependencies/tutorial011_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="20" + {!> ../../../docs_src/dependencies/tutorial011.py!} + ``` + +!!! tip + All this might seem contrived. And it might not be very clear how is it useful yet. + + These examples are intentionally simple, but show how it all works. + + In the chapters about security, there are utility functions that are implemented in this same way. + + If you understood all this, you already know how those utility tools for security work underneath. From b890d056a623274fe1ff3d0179d1c916c2ce6ae9 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:29 +0800 Subject: [PATCH 004/163] New translations async-sql-databases.md (Chinese Simplified) --- docs/zh/docs/advanced/async-sql-databases.md | 169 +++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 docs/zh/docs/advanced/async-sql-databases.md diff --git a/docs/zh/docs/advanced/async-sql-databases.md b/docs/zh/docs/advanced/async-sql-databases.md new file mode 100644 index 0000000000000..6a85f2237ea1f --- /dev/null +++ b/docs/zh/docs/advanced/async-sql-databases.md @@ -0,0 +1,169 @@ +# Async SQL (Relational) Databases + +!!! info + These docs are about to be updated. 🎉 + + The current version assumes Pydantic v1. + + The new docs will include Pydantic v2 and will use SQLModel once it is updated to use Pydantic v2 as well. + +You can also use `encode/databases` with **FastAPI** to connect to databases using `async` and `await`. + +It is compatible with: + +* PostgreSQL +* MySQL +* SQLite + +In this example, we'll use **SQLite**, because it uses a single file and Python has integrated support. So, you can copy this example and run it as is. + +Later, for your production application, you might want to use a database server like **PostgreSQL**. + +!!! tip + You could adopt ideas from the section about SQLAlchemy ORM ([SQL (Relational) Databases](../tutorial/sql-databases.md){.internal-link target=_blank}), like using utility functions to perform operations in the database, independent of your **FastAPI** code. + + This section doesn't apply those ideas, to be equivalent to the counterpart in Starlette. + +## Import and set up `SQLAlchemy` + +* Import `SQLAlchemy`. +* Create a `metadata` object. +* Create a table `notes` using the `metadata` object. + +```Python hl_lines="4 14 16-22" +{!../../../docs_src/async_sql_databases/tutorial001.py!} +``` + +!!! tip + Notice that all this code is pure SQLAlchemy Core. + + `databases` is not doing anything here yet. + +## Import and set up `databases` + +* Import `databases`. +* Create a `DATABASE_URL`. +* Create a `database` object. + +```Python hl_lines="3 9 12" +{!../../../docs_src/async_sql_databases/tutorial001.py!} +``` + +!!! tip + If you were connecting to a different database (e.g. PostgreSQL), you would need to change the `DATABASE_URL`. + +## Create the tables + +In this case, we are creating the tables in the same Python file, but in production, you would probably want to create them with Alembic, integrated with migrations, etc. + +Here, this section would run directly, right before starting your **FastAPI** application. + +* Create an `engine`. +* Create all the tables from the `metadata` object. + +```Python hl_lines="25-28" +{!../../../docs_src/async_sql_databases/tutorial001.py!} +``` + +## Create models + +Create Pydantic models for: + +* Notes to be created (`NoteIn`). +* Notes to be returned (`Note`). + +```Python hl_lines="31-33 36-39" +{!../../../docs_src/async_sql_databases/tutorial001.py!} +``` + +By creating these Pydantic models, the input data will be validated, serialized (converted), and annotated (documented). + +So, you will be able to see it all in the interactive API docs. + +## Connect and disconnect + +* Create your `FastAPI` application. +* Create event handlers to connect and disconnect from the database. + +```Python hl_lines="42 45-47 50-52" +{!../../../docs_src/async_sql_databases/tutorial001.py!} +``` + +## Read notes + +Create the *path operation function* to read notes: + +```Python hl_lines="55-58" +{!../../../docs_src/async_sql_databases/tutorial001.py!} +``` + +!!! Note + Notice that as we communicate with the database using `await`, the *path operation function* is declared with `async`. + +### Notice the `response_model=List[Note]` + +It uses `typing.List`. + +That documents (and validates, serializes, filters) the output data, as a `list` of `Note`s. + +## Create notes + +Create the *path operation function* to create notes: + +```Python hl_lines="61-65" +{!../../../docs_src/async_sql_databases/tutorial001.py!} +``` + +!!! Note + Notice that as we communicate with the database using `await`, the *path operation function* is declared with `async`. + +### About `{**note.dict(), "id": last_record_id}` + +`note` is a Pydantic `Note` object. + +`note.dict()` returns a `dict` with its data, something like: + +```Python +{ + "text": "Some note", + "completed": False, +} +``` + +but it doesn't have the `id` field. + +So we create a new `dict`, that contains the key-value pairs from `note.dict()` with: + +```Python +{**note.dict()} +``` + +`**note.dict()` "unpacks" the key value pairs directly, so, `{**note.dict()}` would be, more or less, a copy of `note.dict()`. + +And then, we extend that copy `dict`, adding another key-value pair: `"id": last_record_id`: + +```Python +{**note.dict(), "id": last_record_id} +``` + +So, the final result returned would be something like: + +```Python +{ + "id": 1, + "text": "Some note", + "completed": False, +} +``` + +## Check it + +You can copy this code as is, and see the docs at http://127.0.0.1:8000/docs. + +There you can see all your API documented and interact with it: + + + +## More info + +You can read more about `encode/databases` at its GitHub page. From 6369a0e4b93af378edf755b32e668cf9056886be Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:30 +0800 Subject: [PATCH 005/163] New translations async-tests.md (Chinese Simplified) --- docs/zh/docs/advanced/async-tests.md | 92 ++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 docs/zh/docs/advanced/async-tests.md diff --git a/docs/zh/docs/advanced/async-tests.md b/docs/zh/docs/advanced/async-tests.md new file mode 100644 index 0000000000000..9b39d70fca6a8 --- /dev/null +++ b/docs/zh/docs/advanced/async-tests.md @@ -0,0 +1,92 @@ +# Async Tests + +You have already seen how to test your **FastAPI** applications using the provided `TestClient`. Up to now, you have only seen how to write synchronous tests, without using `async` functions. + +Being able to use asynchronous functions in your tests could be useful, for example, when you're querying your database asynchronously. Imagine you want to test sending requests to your FastAPI application and then verify that your backend successfully wrote the correct data in the database, while using an async database library. + +Let's look at how we can make that work. + +## pytest.mark.anyio + +If we want to call asynchronous functions in our tests, our test functions have to be asynchronous. AnyIO provides a neat plugin for this, that allows us to specify that some test functions are to be called asynchronously. + +## HTTPX + +Even if your **FastAPI** application uses normal `def` functions instead of `async def`, it is still an `async` application underneath. + +The `TestClient` does some magic inside to call the asynchronous FastAPI application in your normal `def` test functions, using standard pytest. But that magic doesn't work anymore when we're using it inside asynchronous functions. By running our tests asynchronously, we can no longer use the `TestClient` inside our test functions. + +The `TestClient` is based on HTTPX, and luckily, we can use it directly to test the API. + +## Example + +For a simple example, let's consider a file structure similar to the one described in [Bigger Applications](../tutorial/bigger-applications.md){.internal-link target=_blank} and [Testing](../tutorial/testing.md){.internal-link target=_blank}: + +``` +. +├── app +│   ├── __init__.py +│   ├── main.py +│   └── test_main.py +``` + +The file `main.py` would have: + +```Python +{!../../../docs_src/async_tests/main.py!} +``` + +The file `test_main.py` would have the tests for `main.py`, it could look like this now: + +```Python +{!../../../docs_src/async_tests/test_main.py!} +``` + +## Run it + +You can run your tests as usual via: + +
+ +```console +$ pytest + +---> 100% +``` + +
+ +## In Detail + +The marker `@pytest.mark.anyio` tells pytest that this test function should be called asynchronously: + +```Python hl_lines="7" +{!../../../docs_src/async_tests/test_main.py!} +``` + +!!! tip + Note that the test function is now `async def` instead of just `def` as before when using the `TestClient`. + +Then we can create an `AsyncClient` with the app, and send async requests to it, using `await`. + +```Python hl_lines="9-10" +{!../../../docs_src/async_tests/test_main.py!} +``` + +This is the equivalent to: + +```Python +response = client.get('/') +``` + +...that we used to make our requests with the `TestClient`. + +!!! tip + Note that we're using async/await with the new `AsyncClient` - the request is asynchronous. + +## Other Asynchronous Function Calls + +As the testing function is now asynchronous, you can now also call (and `await`) other `async` functions apart from sending requests to your FastAPI application in your tests, exactly as you would call them anywhere else in your code. + +!!! tip + If you encounter a `RuntimeError: Task attached to a different loop` when integrating asynchronous function calls in your tests (e.g. when using MongoDB's MotorClient) Remember to instantiate objects that need an event loop only within async functions, e.g. an `'@app.on_event("startup")` callback. From a745dcb305ff66d3f6df1f326e25dc442804b327 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:30 +0800 Subject: [PATCH 006/163] New translations behind-a-proxy.md (Chinese Simplified) --- docs/zh/docs/advanced/behind-a-proxy.md | 346 ++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 docs/zh/docs/advanced/behind-a-proxy.md diff --git a/docs/zh/docs/advanced/behind-a-proxy.md b/docs/zh/docs/advanced/behind-a-proxy.md new file mode 100644 index 0000000000000..94ffd994824d4 --- /dev/null +++ b/docs/zh/docs/advanced/behind-a-proxy.md @@ -0,0 +1,346 @@ +# Behind a Proxy + +In some situations, you might need to use a **proxy** server like Traefik or Nginx with a configuration that adds an extra path prefix that is not seen by your application. + +In these cases you can use `root_path` to configure your application. + +The `root_path` is a mechanism provided by the ASGI specification (that FastAPI is built on, through Starlette). + +The `root_path` is used to handle these specific cases. + +And it's also used internally when mounting sub-applications. + +## Proxy with a stripped path prefix + +Having a proxy with a stripped path prefix, in this case, means that you could declare a path at `/app` in your code, but then, you add a layer on top (the proxy) that would put your **FastAPI** application under a path like `/api/v1`. + +In this case, the original path `/app` would actually be served at `/api/v1/app`. + +Even though all your code is written assuming there's just `/app`. + +And the proxy would be **"stripping"** the **path prefix** on the fly before transmitting the request to Uvicorn, keep your application convinced that it is serving at `/app`, so that you don't have to update all your code to include the prefix `/api/v1`. + +Up to here, everything would work as normally. + +But then, when you open the integrated docs UI (the frontend), it would expect to get the OpenAPI schema at `/openapi.json`, instead of `/api/v1/openapi.json`. + +So, the frontend (that runs in the browser) would try to reach `/openapi.json` and wouldn't be able to get the OpenAPI schema. + +Because we have a proxy with a path prefix of `/api/v1` for our app, the frontend needs to fetch the OpenAPI schema at `/api/v1/openapi.json`. + +```mermaid +graph LR + +browser("Browser") +proxy["Proxy on http://0.0.0.0:9999/api/v1/app"] +server["Server on http://127.0.0.1:8000/app"] + +browser --> proxy +proxy --> server +``` + +!!! tip + The IP `0.0.0.0` is commonly used to mean that the program listens on all the IPs available in that machine/server. + +The docs UI would also need the OpenAPI schema to declare that this API `server` is located at `/api/v1` (behind the proxy). For example: + +```JSON hl_lines="4-8" +{ + "openapi": "3.1.0", + // More stuff here + "servers": [ + { + "url": "/api/v1" + } + ], + "paths": { + // More stuff here + } +} +``` + +In this example, the "Proxy" could be something like **Traefik**. And the server would be something like **Uvicorn**, running your FastAPI application. + +### Providing the `root_path` + +To achieve this, you can use the command line option `--root-path` like: + +
+ +```console +$ uvicorn main:app --root-path /api/v1 + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +If you use Hypercorn, it also has the option `--root-path`. + +!!! note "Technical Details" + The ASGI specification defines a `root_path` for this use case. + + And the `--root-path` command line option provides that `root_path`. + +### Checking the current `root_path` + +You can get the current `root_path` used by your application for each request, it is part of the `scope` dictionary (that's part of the ASGI spec). + +Here we are including it in the message just for demonstration purposes. + +```Python hl_lines="8" +{!../../../docs_src/behind_a_proxy/tutorial001.py!} +``` + +Then, if you start Uvicorn with: + +
+ +```console +$ uvicorn main:app --root-path /api/v1 + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +The response would be something like: + +```JSON +{ + "message": "Hello World", + "root_path": "/api/v1" +} +``` + +### Setting the `root_path` in the FastAPI app + +Alternatively, if you don't have a way to provide a command line option like `--root-path` or equivalent, you can set the `root_path` parameter when creating your FastAPI app: + +```Python hl_lines="3" +{!../../../docs_src/behind_a_proxy/tutorial002.py!} +``` + +Passing the `root_path` to `FastAPI` would be the equivalent of passing the `--root-path` command line option to Uvicorn or Hypercorn. + +### About `root_path` + +Have in mind that the server (Uvicorn) won't use that `root_path` for anything else than passing it to the app. + +But if you go with your browser to http://127.0.0.1:8000/app you will see the normal response: + +```JSON +{ + "message": "Hello World", + "root_path": "/api/v1" +} +``` + +So, it won't expect to be accessed at `http://127.0.0.1:8000/api/v1/app`. + +Uvicorn will expect the proxy to access Uvicorn at `http://127.0.0.1:8000/app`, and then it would be the proxy's responsibility to add the extra `/api/v1` prefix on top. + +## About proxies with a stripped path prefix + +Have in mind that a proxy with stripped path prefix is only one of the ways to configure it. + +Probably in many cases the default will be that the proxy doesn't have a stripped path prefix. + +In a case like that (without a stripped path prefix), the proxy would listen on something like `https://myawesomeapp.com`, and then if the browser goes to `https://myawesomeapp.com/api/v1/app` and your server (e.g. Uvicorn) listens on `http://127.0.0.1:8000` the proxy (without a stripped path prefix) would access Uvicorn at the same path: `http://127.0.0.1:8000/api/v1/app`. + +## Testing locally with Traefik + +You can easily run the experiment locally with a stripped path prefix using Traefik. + +Download Traefik, it's a single binary, you can extract the compressed file and run it directly from the terminal. + +Then create a file `traefik.toml` with: + +```TOML hl_lines="3" +[entryPoints] + [entryPoints.http] + address = ":9999" + +[providers] + [providers.file] + filename = "routes.toml" +``` + +This tells Traefik to listen on port 9999 and to use another file `routes.toml`. + +!!! tip + We are using port 9999 instead of the standard HTTP port 80 so that you don't have to run it with admin (`sudo`) privileges. + +Now create that other file `routes.toml`: + +```TOML hl_lines="5 12 20" +[http] + [http.middlewares] + + [http.middlewares.api-stripprefix.stripPrefix] + prefixes = ["/api/v1"] + + [http.routers] + + [http.routers.app-http] + entryPoints = ["http"] + service = "app" + rule = "PathPrefix(`/api/v1`)" + middlewares = ["api-stripprefix"] + + [http.services] + + [http.services.app] + [http.services.app.loadBalancer] + [[http.services.app.loadBalancer.servers]] + url = "http://127.0.0.1:8000" +``` + +This file configures Traefik to use the path prefix `/api/v1`. + +And then it will redirect its requests to your Uvicorn running on `http://127.0.0.1:8000`. + +Now start Traefik: + +
+ +```console +$ ./traefik --configFile=traefik.toml + +INFO[0000] Configuration loaded from file: /home/user/awesomeapi/traefik.toml +``` + +
+ +And now start your app with Uvicorn, using the `--root-path` option: + +
+ +```console +$ uvicorn main:app --root-path /api/v1 + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +### Check the responses + +Now, if you go to the URL with the port for Uvicorn: http://127.0.0.1:8000/app, you will see the normal response: + +```JSON +{ + "message": "Hello World", + "root_path": "/api/v1" +} +``` + +!!! tip + Notice that even though you are accessing it at `http://127.0.0.1:8000/app` it shows the `root_path` of `/api/v1`, taken from the option `--root-path`. + +And now open the URL with the port for Traefik, including the path prefix: http://127.0.0.1:9999/api/v1/app. + +We get the same response: + +```JSON +{ + "message": "Hello World", + "root_path": "/api/v1" +} +``` + +but this time at the URL with the prefix path provided by the proxy: `/api/v1`. + +Of course, the idea here is that everyone would access the app through the proxy, so the version with the path prefix `/api/v1` is the "correct" one. + +And the version without the path prefix (`http://127.0.0.1:8000/app`), provided by Uvicorn directly, would be exclusively for the _proxy_ (Traefik) to access it. + +That demonstrates how the Proxy (Traefik) uses the path prefix and how the server (Uvicorn) uses the `root_path` from the option `--root-path`. + +### Check the docs UI + +But here's the fun part. ✨ + +The "official" way to access the app would be through the proxy with the path prefix that we defined. So, as we would expect, if you try the docs UI served by Uvicorn directly, without the path prefix in the URL, it won't work, because it expects to be accessed through the proxy. + +You can check it at http://127.0.0.1:8000/docs: + + + +But if we access the docs UI at the "official" URL using the proxy with port `9999`, at `/api/v1/docs`, it works correctly! 🎉 + +You can check it at http://127.0.0.1:9999/api/v1/docs: + + + +Right as we wanted it. ✔️ + +This is because FastAPI uses this `root_path` to create the default `server` in OpenAPI with the URL provided by `root_path`. + +## Additional servers + +!!! warning + This is a more advanced use case. Feel free to skip it. + +By default, **FastAPI** will create a `server` in the OpenAPI schema with the URL for the `root_path`. + +But you can also provide other alternative `servers`, for example if you want *the same* docs UI to interact with a staging and production environments. + +If you pass a custom list of `servers` and there's a `root_path` (because your API lives behind a proxy), **FastAPI** will insert a "server" with this `root_path` at the beginning of the list. + +For example: + +```Python hl_lines="4-7" +{!../../../docs_src/behind_a_proxy/tutorial003.py!} +``` + +Will generate an OpenAPI schema like: + +```JSON hl_lines="5-7" +{ + "openapi": "3.1.0", + // More stuff here + "servers": [ + { + "url": "/api/v1" + }, + { + "url": "https://stag.example.com", + "description": "Staging environment" + }, + { + "url": "https://prod.example.com", + "description": "Production environment" + } + ], + "paths": { + // More stuff here + } +} +``` + +!!! tip + Notice the auto-generated server with a `url` value of `/api/v1`, taken from the `root_path`. + +In the docs UI at http://127.0.0.1:9999/api/v1/docs it would look like: + + + +!!! tip + The docs UI will interact with the server that you select. + +### Disable automatic server from `root_path` + +If you don't want **FastAPI** to include an automatic server using the `root_path`, you can use the parameter `root_path_in_servers=False`: + +```Python hl_lines="9" +{!../../../docs_src/behind_a_proxy/tutorial004.py!} +``` + +and then it won't include it in the OpenAPI schema. + +## Mounting a sub-application + +If you need to mount a sub-application (as described in [Sub Applications - Mounts](./sub-applications.md){.internal-link target=_blank}) while also using a proxy with `root_path`, you can do it normally, as you would expect. + +FastAPI will internally use the `root_path` smartly, so it will just work. ✨ From 896bb02a1191f9a6cad2b68222ace9cac8220273 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:31 +0800 Subject: [PATCH 007/163] New translations conditional-openapi.md (Chinese Simplified) --- docs/zh/docs/advanced/conditional-openapi.md | 58 ++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 docs/zh/docs/advanced/conditional-openapi.md diff --git a/docs/zh/docs/advanced/conditional-openapi.md b/docs/zh/docs/advanced/conditional-openapi.md new file mode 100644 index 0000000000000..add16fbec519e --- /dev/null +++ b/docs/zh/docs/advanced/conditional-openapi.md @@ -0,0 +1,58 @@ +# Conditional OpenAPI + +If you needed to, you could use settings and environment variables to configure OpenAPI conditionally depending on the environment, and even disable it entirely. + +## About security, APIs, and docs + +Hiding your documentation user interfaces in production *shouldn't* be the way to protect your API. + +That doesn't add any extra security to your API, the *path operations* will still be available where they are. + +If there's a security flaw in your code, it will still exist. + +Hiding the documentation just makes it more difficult to understand how to interact with your API, and could make it more difficult for you to debug it in production. It could be considered simply a form of Security through obscurity. + +If you want to secure your API, there are several better things you can do, for example: + +* Make sure you have well defined Pydantic models for your request bodies and responses. +* Configure any required permissions and roles using dependencies. +* Never store plaintext passwords, only password hashes. +* Implement and use well-known cryptographic tools, like Passlib and JWT tokens, etc. +* Add more granular permission controls with OAuth2 scopes where needed. +* ...etc. + +Nevertheless, you might have a very specific use case where you really need to disable the API docs for some environment (e.g. for production) or depending on configurations from environment variables. + +## Conditional OpenAPI from settings and env vars + +You can easily use the same Pydantic settings to configure your generated OpenAPI and the docs UIs. + +For example: + +```Python hl_lines="6 11" +{!../../../docs_src/conditional_openapi/tutorial001.py!} +``` + +Here we declare the setting `openapi_url` with the same default of `"/openapi.json"`. + +And then we use it when creating the `FastAPI` app. + +Then you could disable OpenAPI (including the UI docs) by setting the environment variable `OPENAPI_URL` to the empty string, like: + +
+ +```console +$ OPENAPI_URL= uvicorn main:app + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +Then if you go to the URLs at `/openapi.json`, `/docs`, or `/redoc` you will just get a `404 Not Found` error like: + +```JSON +{ + "detail": "Not Found" +} +``` From b54983ec4897008649c445ed1c4336df1435b95d Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:32 +0800 Subject: [PATCH 008/163] New translations custom-request-and-route.md (Chinese Simplified) --- .../docs/advanced/custom-request-and-route.md | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 docs/zh/docs/advanced/custom-request-and-route.md diff --git a/docs/zh/docs/advanced/custom-request-and-route.md b/docs/zh/docs/advanced/custom-request-and-route.md new file mode 100644 index 0000000000000..bfaf5c8d87877 --- /dev/null +++ b/docs/zh/docs/advanced/custom-request-and-route.md @@ -0,0 +1,109 @@ +# Custom Request and APIRoute class + +In some cases, you may want to override the logic used by the `Request` and `APIRoute` classes. + +In particular, this may be a good alternative to logic in a middleware. + +For example, if you want to read or manipulate the request body before it is processed by your application. + +!!! danger + This is an "advanced" feature. + + If you are just starting with **FastAPI** you might want to skip this section. + +## Use cases + +Some use cases include: + +* Converting non-JSON request bodies to JSON (e.g. `msgpack`). +* Decompressing gzip-compressed request bodies. +* Automatically logging all request bodies. + +## Handling custom request body encodings + +Let's see how to make use of a custom `Request` subclass to decompress gzip requests. + +And an `APIRoute` subclass to use that custom request class. + +### Create a custom `GzipRequest` class + +!!! tip + This is a toy example to demonstrate how it works, if you need Gzip support, you can use the provided [`GzipMiddleware`](./middleware.md#gzipmiddleware){.internal-link target=_blank}. + +First, we create a `GzipRequest` class, which will overwrite the `Request.body()` method to decompress the body in the presence of an appropriate header. + +If there's no `gzip` in the header, it will not try to decompress the body. + +That way, the same route class can handle gzip compressed or uncompressed requests. + +```Python hl_lines="8-15" +{!../../../docs_src/custom_request_and_route/tutorial001.py!} +``` + +### Create a custom `GzipRoute` class + +Next, we create a custom subclass of `fastapi.routing.APIRoute` that will make use of the `GzipRequest`. + +This time, it will overwrite the method `APIRoute.get_route_handler()`. + +This method returns a function. And that function is what will receive a request and return a response. + +Here we use it to create a `GzipRequest` from the original request. + +```Python hl_lines="18-26" +{!../../../docs_src/custom_request_and_route/tutorial001.py!} +``` + +!!! note "Technical Details" + A `Request` has a `request.scope` attribute, that's just a Python `dict` containing the metadata related to the request. + + A `Request` also has a `request.receive`, that's a function to "receive" the body of the request. + + The `scope` `dict` and `receive` function are both part of the ASGI specification. + + And those two things, `scope` and `receive`, are what is needed to create a new `Request` instance. + + To learn more about the `Request` check Starlette's docs about Requests. + +The only thing the function returned by `GzipRequest.get_route_handler` does differently is convert the `Request` to a `GzipRequest`. + +Doing this, our `GzipRequest` will take care of decompressing the data (if necessary) before passing it to our *path operations*. + +After that, all of the processing logic is the same. + +But because of our changes in `GzipRequest.body`, the request body will be automatically decompressed when it is loaded by **FastAPI** when needed. + +## Accessing the request body in an exception handler + +!!! tip + To solve this same problem, it's probably a lot easier to use the `body` in a custom handler for `RequestValidationError` ([Handling Errors](../tutorial/handling-errors.md#use-the-requestvalidationerror-body){.internal-link target=_blank}). + + But this example is still valid and it shows how to interact with the internal components. + +We can also use this same approach to access the request body in an exception handler. + +All we need to do is handle the request inside a `try`/`except` block: + +```Python hl_lines="13 15" +{!../../../docs_src/custom_request_and_route/tutorial002.py!} +``` + +If an exception occurs, the`Request` instance will still be in scope, so we can read and make use of the request body when handling the error: + +```Python hl_lines="16-18" +{!../../../docs_src/custom_request_and_route/tutorial002.py!} +``` + +## Custom `APIRoute` class in a router + +You can also set the `route_class` parameter of an `APIRouter`: + +```Python hl_lines="26" +{!../../../docs_src/custom_request_and_route/tutorial003.py!} +``` + +In this example, the *path operations* under the `router` will use the custom `TimedRoute` class, and will have an extra `X-Response-Time` header in the response with the time it took to generate the response: + +```Python hl_lines="13-20" +{!../../../docs_src/custom_request_and_route/tutorial003.py!} +``` From 94f439a76addd45e764f35d1ec636bc381ed892b Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:34 +0800 Subject: [PATCH 009/163] New translations custom-response.md (Chinese Simplified) --- docs/zh/docs/advanced/custom-response.md | 274 +++++++++++++++-------- 1 file changed, 181 insertions(+), 93 deletions(-) diff --git a/docs/zh/docs/advanced/custom-response.md b/docs/zh/docs/advanced/custom-response.md index 155ce28828b0b..e3876f2928643 100644 --- a/docs/zh/docs/advanced/custom-response.md +++ b/docs/zh/docs/advanced/custom-response.md @@ -1,124 +1,127 @@ -# 自定义响应 - HTML,流,文件和其他 +# Custom Response - HTML, Stream, File, others -**FastAPI** 默认会使用 `JSONResponse` 返回响应。 +By default, **FastAPI** will return the responses using `JSONResponse`. -你可以通过直接返回 `Response` 来重载它,参见 [直接返回响应](response-directly.md){.internal-link target=_blank}。 +You can override it by returning a `Response` directly as seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}. -但如果你直接返回 `Response`,返回数据不会自动转换,也不会自动生成文档(例如,在 HTTP 头 `Content-Type` 中包含特定的「媒体类型」作为生成的 OpenAPI 的一部分)。 +But if you return a `Response` directly, the data won't be automatically converted, and the documentation won't be automatically generated (for example, including the specific "media type", in the HTTP header `Content-Type` as part of the generated OpenAPI). -你还可以在 *路径操作装饰器* 中声明你想用的 `Response`。 +But you can also declare the `Response` that you want to be used, in the *path operation decorator*. -你从 *路径操作函数* 中返回的内容将被放在该 `Response` 中。 +The contents that you return from your *path operation function* will be put inside of that `Response`. -并且如果该 `Response` 有一个 JSON 媒体类型(`application/json`),比如使用 `JSONResponse` 或者 `UJSONResponse` 的时候,返回的数据将使用你在路径操作装饰器中声明的任何 Pydantic 的 `response_model` 自动转换(和过滤)。 +And if that `Response` has a JSON media type (`application/json`), like is the case with the `JSONResponse` and `UJSONResponse`, the data you return will be automatically converted (and filtered) with any Pydantic `response_model` that you declared in the *path operation decorator*. -!!! note "说明" - 如果你使用不带有任何媒体类型的响应类,FastAPI 认为你的响应没有任何内容,所以不会在生成的OpenAPI文档中记录响应格式。 +!!! note + If you use a response class with no media type, FastAPI will expect your response to have no content, so it will not document the response format in its generated OpenAPI docs. -## 使用 `ORJSONResponse` +## Use `ORJSONResponse` -例如,如果你需要压榨性能,你可以安装并使用 `orjson` 并将响应设置为 `ORJSONResponse`。 +For example, if you are squeezing performance, you can install and use `orjson` and set the response to be `ORJSONResponse`. -导入你想要使用的 `Response` 类(子类)然后在 *路径操作装饰器* 中声明它。 +Import the `Response` class (sub-class) you want to use and declare it in the *path operation decorator*. -```Python hl_lines="2 7" -{!../../../docs_src/custom_response/tutorial001b.py!} -``` +For large responses, returning a `Response` directly is much faster than returning a dictionary. -!!! info "提示" - 参数 `response_class` 也会用来定义响应的「媒体类型」。 +This is because by default, FastAPI will inspect every item inside and make sure it is serializable with JSON, using the same [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank} explained in the tutorial. This is what allows you to return **arbitrary objects**, for example database models. - 在这个例子中,HTTP 头的 `Content-Type` 会被设置成 `application/json`。 +But if you are certain that the content that you are returning is **serializable with JSON**, you can pass it directly to the response class and avoid the extra overhead that FastAPI would have by passing your return content through the `jsonable_encoder` before passing it to the response class. - 并且在 OpenAPI 文档中也会这样记录。 +```Python hl_lines="2 7" +{!../../../docs_src/custom_response/tutorial001b.py!} +``` -!!! tip "小贴士" - `ORJSONResponse` 目前只在 FastAPI 中可用,而在 Starlette 中不可用。 +!!! info + The parameter `response_class` will also be used to define the "media type" of the response. + In this case, the HTTP header `Content-Type` will be set to `application/json`. + + And it will be documented as such in OpenAPI. +!!! tip + The `ORJSONResponse` is currently only available in FastAPI, not in Starlette. -## HTML 响应 +## HTML Response -使用 `HTMLResponse` 来从 **FastAPI** 中直接返回一个 HTML 响应。 +To return a response with HTML directly from **FastAPI**, use `HTMLResponse`. -* 导入 `HTMLResponse`。 -* 将 `HTMLResponse` 作为你的 *路径操作* 的 `response_class` 参数传入。 +* Import `HTMLResponse`. +* Pass `HTMLResponse` as the parameter `response_class` of your *path operation decorator*. -```Python hl_lines="2 7" +```Python hl_lines="2 7" {!../../../docs_src/custom_response/tutorial002.py!} ``` -!!! info "提示" - 参数 `response_class` 也会用来定义响应的「媒体类型」。 +!!! info + The parameter `response_class` will also be used to define the "media type" of the response. - 在这个例子中,HTTP 头的 `Content-Type` 会被设置成 `text/html`。 + In this case, the HTTP header `Content-Type` will be set to `text/html`. + + And it will be documented as such in OpenAPI. - 并且在 OpenAPI 文档中也会这样记录。 +### Return a `Response` -### 返回一个 `Response` +As seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}, you can also override the response directly in your *path operation*, by returning it. -正如你在 [直接返回响应](response-directly.md){.internal-link target=_blank} 中了解到的,你也可以通过直接返回响应在 *路径操作* 中直接重载响应。 +The same example from above, returning an `HTMLResponse`, could look like: -和上面一样的例子,返回一个 `HTMLResponse` 看起来可能是这样: - -```Python hl_lines="2 7 19" +```Python hl_lines="2 7 19" {!../../../docs_src/custom_response/tutorial003.py!} ``` -!!! warning "警告" - *路径操作函数* 直接返回的 `Response` 不会被 OpenAPI 的文档记录(比如,`Content-Type` 不会被文档记录),并且在自动化交互文档中也是不可见的。 +!!! warning + A `Response` returned directly by your *path operation function* won't be documented in OpenAPI (for example, the `Content-Type` won't be documented) and won't be visible in the automatic interactive docs. -!!! info "提示" - 当然,实际的 `Content-Type` 头,状态码等等,将来自于你返回的 `Response` 对象。 +!!! info + Of course, the actual `Content-Type` header, status code, etc, will come from the `Response` object your returned. -### OpenAPI 中的文档和重载 `Response` +### Document in OpenAPI and override `Response` -如果你想要在函数内重载响应,但是同时在 OpenAPI 中文档化「媒体类型」,你可以使用 `response_class` 参数并返回一个 `Response` 对象。 +If you want to override the response from inside of the function but at the same time document the "media type" in OpenAPI, you can use the `response_class` parameter AND return a `Response` object. -接着 `response_class` 参数只会被用来文档化 OpenAPI 的 *路径操作*,你的 `Response` 用来返回响应。 +The `response_class` will then be used only to document the OpenAPI *path operation*, but your `Response` will be used as is. -### 直接返回 `HTMLResponse` +#### Return an `HTMLResponse` directly -比如像这样: +For example, it could be something like: -```Python hl_lines="7 23 21" +```Python hl_lines="7 21 23" {!../../../docs_src/custom_response/tutorial004.py!} ``` -在这个例子中,函数 `generate_html_response()` 已经生成并返回 `Response` 对象而不是在 `str` 中返回 HTML。 +In this example, the function `generate_html_response()` already generates and returns a `Response` instead of returning the HTML in a `str`. -通过返回函数 `generate_html_response()` 的调用结果,你已经返回一个重载 **FastAPI** 默认行为的 `Response` 对象, +By returning the result of calling `generate_html_response()`, you are already returning a `Response` that will override the default **FastAPI** behavior. -但如果你在 `response_class` 中也传入了 `HTMLResponse`,**FastAPI** 会知道如何在 OpenAPI 和交互式文档中使用 `text/html` 将其文档化为 HTML。 +But as you passed the `HTMLResponse` in the `response_class` too, **FastAPI** will know how to document it in OpenAPI and the interactive docs as HTML with `text/html`: - + -## 可用响应 +## Available responses -这里有一些可用的响应。 +Here are some of the available responses. -要记得你可以使用 `Response` 来返回任何其他东西,甚至创建一个自定义的子类。 +Have in mind that you can use `Response` to return anything else, or even create a custom sub-class. -!!! note "技术细节" - 你也可以使用 `from starlette.responses import HTMLResponse`。 +!!! note "Technical Details" + You could also use `from starlette.responses import HTMLResponse`. - **FastAPI** 提供了同 `fastapi.responses` 相同的 `starlette.responses` 只是为了方便开发者。但大多数可用的响应都直接来自 Starlette。 + **FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. ### `Response` -其他全部的响应都继承自主类 `Response`。 - -你可以直接返回它。 +The main `Response` class, all the other responses inherit from it. -`Response` 类接受如下参数: +You can return it directly. -* `content` - 一个 `str` 或者 `bytes`。 -* `status_code` - 一个 `int` 类型的 HTTP 状态码。 -* `headers` - 一个由字符串组成的 `dict`。 -* `media_type` - 一个给出媒体类型的 `str`,比如 `"text/html"`。 +It accepts the following parameters: -FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它还将包含一个基于 media_type 的 Content-Type 头,并为文本类型附加一个字符集。 +* `content` - A `str` or `bytes`. +* `status_code` - An `int` HTTP status code. +* `headers` - A `dict` of strings. +* `media_type` - A `str` giving the media type. E.g. `"text/html"`. +FastAPI (actually Starlette) will automatically include a Content-Length header. It will also include a Content-Type header, based on the media_type and appending a charset for text types. ```Python hl_lines="1 18" {!../../../docs_src/response_directly/tutorial002.py!} @@ -126,11 +129,11 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它 ### `HTMLResponse` -如上文所述,接受文本或字节并返回 HTML 响应。 +Takes some text or bytes and returns an HTML response, as you read above. ### `PlainTextResponse` -接受文本或字节并返回纯文本响应。 +Takes some text or bytes and returns an plain text response. ```Python hl_lines="2 7 9" {!../../../docs_src/custom_response/tutorial005.py!} @@ -138,75 +141,160 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它 ### `JSONResponse` -接受数据并返回一个 `application/json` 编码的响应。 +Takes some data and returns an `application/json` encoded response. -如上文所述,这是 **FastAPI** 中使用的默认响应。 +This is the default response used in **FastAPI**, as you read above. ### `ORJSONResponse` -如上文所述,`ORJSONResponse` 是一个使用 `orjson` 的快速的可选 JSON 响应。 - +A fast alternative JSON response using `orjson`, as you read above. ### `UJSONResponse` -`UJSONResponse` 是一个使用 `ujson` 的可选 JSON 响应。 +An alternative JSON response using `ujson`. -!!! warning "警告" - 在处理某些边缘情况时,`ujson` 不如 Python 的内置实现那么谨慎。 +!!! warning + `ujson` is less careful than Python's built-in implementation in how it handles some edge-cases. -```Python hl_lines="2 7" +```Python hl_lines="2 7" {!../../../docs_src/custom_response/tutorial001.py!} ``` -!!! tip "小贴士" - `ORJSONResponse` 可能是一个更快的选择。 +!!! tip + It's possible that `ORJSONResponse` might be a faster alternative. ### `RedirectResponse` -返回 HTTP 重定向。默认情况下使用 307 状态代码(临时重定向)。 +Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default. + +You can return a `RedirectResponse` directly: ```Python hl_lines="2 9" {!../../../docs_src/custom_response/tutorial006.py!} ``` +--- + +Or you can use it in the `response_class` parameter: + + +```Python hl_lines="2 7 9" +{!../../../docs_src/custom_response/tutorial006b.py!} +``` + +If you do that, then you can return the URL directly from your *path operation* function. + +In this case, the `status_code` used will be the default one for the `RedirectResponse`, which is `307`. + +--- + +You can also use the `status_code` parameter combined with the `response_class` parameter: + +```Python hl_lines="2 7 9" +{!../../../docs_src/custom_response/tutorial006c.py!} +``` + ### `StreamingResponse` -采用异步生成器或普通生成器/迭代器,然后流式传输响应主体。 +Takes an async generator or a normal generator/iterator and streams the response body. ```Python hl_lines="2 14" {!../../../docs_src/custom_response/tutorial007.py!} ``` -#### 对类似文件的对象使用 `StreamingResponse` +#### Using `StreamingResponse` with file-like objects + +If you have a file-like object (e.g. the object returned by `open()`), you can create a generator function to iterate over that file-like object. -如果您有类似文件的对象(例如,由 `open()` 返回的对象),则可以在 `StreamingResponse` 中将其返回。 +That way, you don't have to read it all first in memory, and you can pass that generator function to the `StreamingResponse`, and return it. -包括许多与云存储,视频处理等交互的库。 +This includes many libraries to interact with cloud storage, video processing, and others. -```Python hl_lines="2 10-12 14" +```{ .python .annotate hl_lines="2 10-12 14" } {!../../../docs_src/custom_response/tutorial008.py!} ``` -!!! tip "小贴士" - 注意在这里,因为我们使用的是不支持 `async` 和 `await` 的标准 `open()`,我们使用普通的 `def` 声明了路径操作。 +1. This is the generator function. It's a "generator function" because it contains `yield` statements inside. +2. By using a `with` block, we make sure that the file-like object is closed after the generator function is done. So, after it finishes sending the response. +3. This `yield from` tells the function to iterate over that thing named `file_like`. And then, for each part iterated, yield that part as coming from this generator function. + + So, it is a generator function that transfers the "generating" work to something else internally. + + By doing it this way, we can put it in a `with` block, and that way, ensure that it is closed after finishing. + +!!! tip + Notice that here as we are using standard `open()` that doesn't support `async` and `await`, we declare the path operation with normal `def`. ### `FileResponse` -异步传输文件作为响应。 +Asynchronously streams a file as the response. -与其他响应类型相比,接受不同的参数集进行实例化: +Takes a different set of arguments to instantiate than the other response types: -* `path` - 要流式传输的文件的文件路径。 -* `headers` - 任何自定义响应头,传入字典类型。 -* `media_type` - 给出媒体类型的字符串。如果未设置,则文件名或路径将用于推断媒体类型。 -* `filename` - 如果给出,它将包含在响应的 `Content-Disposition` 中。 +* `path` - The filepath to the file to stream. +* `headers` - Any custom headers to include, as a dictionary. +* `media_type` - A string giving the media type. If unset, the filename or path will be used to infer a media type. +* `filename` - If set, this will be included in the response `Content-Disposition`. -文件响应将包含适当的 `Content-Length`,`Last-Modified` 和 `ETag` 的响应头。 +File responses will include appropriate `Content-Length`, `Last-Modified` and `ETag` headers. ```Python hl_lines="2 10" {!../../../docs_src/custom_response/tutorial009.py!} ``` -## 额外文档 +You can also use the `response_class` parameter: + +```Python hl_lines="2 8 10" +{!../../../docs_src/custom_response/tutorial009b.py!} +``` + +In this case, you can return the file path directly from your *path operation* function. + +## Custom response class + +You can create your own custom response class, inheriting from `Response` and using it. + +For example, let's say that you want to use `orjson`, but with some custom settings not used in the included `ORJSONResponse` class. + +Let's say you want it to return indented and formatted JSON, so you want to use the orjson option `orjson.OPT_INDENT_2`. + +You could create a `CustomORJSONResponse`. The main thing you have to do is create a `Response.render(content)` method that returns the content as `bytes`: + +```Python hl_lines="9-14 17" +{!../../../docs_src/custom_response/tutorial009c.py!} +``` + +Now instead of returning: + +```json +{"message": "Hello World"} +``` + +...this response will return: + +```json +{ + "message": "Hello World" +} +``` + +Of course, you will probably find much better ways to take advantage of this than formatting JSON. 😉 + +## Default response class + +When creating a **FastAPI** class instance or an `APIRouter` you can specify which response class to use by default. + +The parameter that defines this is `default_response_class`. + +In the example below, **FastAPI** will use `ORJSONResponse` by default, in all *path operations*, instead of `JSONResponse`. + +```Python hl_lines="2 4" +{!../../../docs_src/custom_response/tutorial010.py!} +``` + +!!! tip + You can still override `response_class` in *path operations* as before. + +## Additional documentation -您还可以使用 `response` 在 OpenAPI 中声明媒体类型和许多其他详细信息:[OpenAPI 中的额外文档](additional-responses.md){.internal-link target=_blank}。 +You can also declare the media type and many other details in OpenAPI using `responses`: [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}. From faf37886bd57e19da0ce5a191959ebf7ab60ed46 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:35 +0800 Subject: [PATCH 010/163] New translations dataclasses.md (Chinese Simplified) --- docs/zh/docs/advanced/dataclasses.md | 98 ++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 docs/zh/docs/advanced/dataclasses.md diff --git a/docs/zh/docs/advanced/dataclasses.md b/docs/zh/docs/advanced/dataclasses.md new file mode 100644 index 0000000000000..fe6b740aa391a --- /dev/null +++ b/docs/zh/docs/advanced/dataclasses.md @@ -0,0 +1,98 @@ +# Using Dataclasses + +FastAPI is built on top of **Pydantic**, and I have been showing you how to use Pydantic models to declare requests and responses. + +But FastAPI also supports using `dataclasses` the same way: + +```Python hl_lines="1 7-12 19-20" +{!../../../docs_src/dataclasses/tutorial001.py!} +``` + +This is still supported thanks to **Pydantic**, as it has internal support for `dataclasses`. + +So, even with the code above that doesn't use Pydantic explicitly, FastAPI is using Pydantic to convert those standard dataclasses to Pydantic's own flavor of dataclasses. + +And of course, it supports the same: + +* data validation +* data serialization +* data documentation, etc. + +This works the same way as with Pydantic models. And it is actually achieved in the same way underneath, using Pydantic. + +!!! info + Have in mind that dataclasses can't do everything Pydantic models can do. + + So, you might still need to use Pydantic models. + + But if you have a bunch of dataclasses laying around, this is a nice trick to use them to power a web API using FastAPI. 🤓 + +## Dataclasses in `response_model` + +You can also use `dataclasses` in the `response_model` parameter: + +```Python hl_lines="1 7-13 19" +{!../../../docs_src/dataclasses/tutorial002.py!} +``` + +The dataclass will be automatically converted to a Pydantic dataclass. + +This way, its schema will show up in the API docs user interface: + + + +## Dataclasses in Nested Data Structures + +You can also combine `dataclasses` with other type annotations to make nested data structures. + +In some cases, you might still have to use Pydantic's version of `dataclasses`. For example, if you have errors with the automatically generated API documentation. + +In that case, you can simply swap the standard `dataclasses` with `pydantic.dataclasses`, which is a drop-in replacement: + +```{ .python .annotate hl_lines="1 5 8-11 14-17 23-25 28" } +{!../../../docs_src/dataclasses/tutorial003.py!} +``` + +1. We still import `field` from standard `dataclasses`. + +2. `pydantic.dataclasses` is a drop-in replacement for `dataclasses`. + +3. The `Author` dataclass includes a list of `Item` dataclasses. + +4. The `Author` dataclass is used as the `response_model` parameter. + +5. You can use other standard type annotations with dataclasses as the request body. + + In this case, it's a list of `Item` dataclasses. + +6. Here we are returning a dictionary that contains `items` which is a list of dataclasses. + + FastAPI is still capable of serializing the data to JSON. + +7. Here the `response_model` is using a type annotation of a list of `Author` dataclasses. + + Again, you can combine `dataclasses` with standard type annotations. + +8. Notice that this *path operation function* uses regular `def` instead of `async def`. + + As always, in FastAPI you can combine `def` and `async def` as needed. + + If you need a refresher about when to use which, check out the section _"In a hurry?"_ in the docs about `async` and `await`. + +9. This *path operation function* is not returning dataclasses (although it could), but a list of dictionaries with internal data. + + FastAPI will use the `response_model` parameter (that includes dataclasses) to convert the response. + +You can combine `dataclasses` with other type annotations in many different combinations to form complex data structures. + +Check the in-code annotation tips above to see more specific details. + +## Learn More + +You can also combine `dataclasses` with other Pydantic models, inherit from them, include them in your own models, etc. + +To learn more, check the Pydantic docs about dataclasses. + +## Version + +This is available since FastAPI version `0.67.0`. 🔖 From 6db8453b26ecd97de30f6b5c4409ff49b1266833 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:35 +0800 Subject: [PATCH 011/163] New translations events.md (Chinese Simplified) --- docs/zh/docs/advanced/events.md | 162 ++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 docs/zh/docs/advanced/events.md diff --git a/docs/zh/docs/advanced/events.md b/docs/zh/docs/advanced/events.md new file mode 100644 index 0000000000000..f62743b798255 --- /dev/null +++ b/docs/zh/docs/advanced/events.md @@ -0,0 +1,162 @@ +# Lifespan Events + +You can define logic (code) that should be executed before the application **starts up**. This means that this code will be executed **once**, **before** the application **starts receiving requests**. + +The same way, you can define logic (code) that should be executed when the application is **shutting down**. In this case, this code will be executed **once**, **after** having handled possibly **many requests**. + +Because this code is executed before the application **starts** taking requests, and right after it **finishes** handling requests, it covers the whole application **lifespan** (the word "lifespan" will be important in a second 😉). + +This can be very useful for setting up **resources** that you need to use for the whole app, and that are **shared** among requests, and/or that you need to **clean up** afterwards. For example, a database connection pool, or loading a shared machine learning model. + +## Use Case + +Let's start with an example **use case** and then see how to solve it with this. + +Let's imagine that you have some **machine learning models** that you want to use to handle requests. 🤖 + +The same models are shared among requests, so, it's not one model per request, or one per user or something similar. + +Let's imagine that loading the model can **take quite some time**, because it has to read a lot of **data from disk**. So you don't want to do it for every request. + +You could load it at the top level of the module/file, but that would also mean that it would **load the model** even if you are just running a simple automated test, then that test would be **slow** because it would have to wait for the model to load before being able to run an independent part of the code. + +That's what we'll solve, let's load the model before the requests are handled, but only right before the application starts receiving requests, not while the code is being loaded. + +## Lifespan + +You can define this *startup* and *shutdown* logic using the `lifespan` parameter of the `FastAPI` app, and a "context manager" (I'll show you what that is in a second). + +Let's start with an example and then see it in detail. + +We create an async function `lifespan()` with `yield` like this: + +```Python hl_lines="16 19" +{!../../../docs_src/events/tutorial003.py!} +``` + +Here we are simulating the expensive *startup* operation of loading the model by putting the (fake) model function in the dictionary with machine learning models before the `yield`. This code will be executed **before** the application **starts taking requests**, during the *startup*. + +And then, right after the `yield`, we unload the model. This code will be executed **after** the application **finishes handling requests**, right before the *shutdown*. This could, for example, release resources like memory or a GPU. + +!!! tip + The `shutdown` would happen when you are **stopping** the application. + + Maybe you need to start a new version, or you just got tired of running it. 🤷 + +### Lifespan function + +The first thing to notice, is that we are defining an async function with `yield`. This is very similar to Dependencies with `yield`. + +```Python hl_lines="14-19" +{!../../../docs_src/events/tutorial003.py!} +``` + +The first part of the function, before the `yield`, will be executed **before** the application starts. + +And the part after the `yield` will be executed **after** the application has finished. + +### Async Context Manager + +If you check, the function is decorated with an `@asynccontextmanager`. + +That converts the function into something called an "**async context manager**". + +```Python hl_lines="1 13" +{!../../../docs_src/events/tutorial003.py!} +``` + +A **context manager** in Python is something that you can use in a `with` statement, for example, `open()` can be used as a context manager: + +```Python +with open("file.txt") as file: + file.read() +``` + +In recent versions of Python, there's also an **async context manager**. You would use it with `async with`: + +```Python +async with lifespan(app): + await do_stuff() +``` + +When you create a context manager or an async context manager like above, what it does is that, before entering the `with` block, it will execute the code before the `yield`, and after exiting the `with` block, it will execute the code after the `yield`. + +In our code example above, we don't use it directly, but we pass it to FastAPI for it to use it. + +The `lifespan` parameter of the `FastAPI` app takes an **async context manager**, so we can pass our new `lifespan` async context manager to it. + +```Python hl_lines="22" +{!../../../docs_src/events/tutorial003.py!} +``` + +## Alternative Events (deprecated) + +!!! warning + The recommended way to handle the *startup* and *shutdown* is using the `lifespan` parameter of the `FastAPI` app as described above. + + You can probably skip this part. + +There's an alternative way to define this logic to be executed during *startup* and during *shutdown*. + +You can define event handlers (functions) that need to be executed before the application starts up, or when the application is shutting down. + +These functions can be declared with `async def` or normal `def`. + +### `startup` event + +To add a function that should be run before the application starts, declare it with the event `"startup"`: + +```Python hl_lines="8" +{!../../../docs_src/events/tutorial001.py!} +``` + +In this case, the `startup` event handler function will initialize the items "database" (just a `dict`) with some values. + +You can add more than one event handler function. + +And your application won't start receiving requests until all the `startup` event handlers have completed. + +### `shutdown` event + +To add a function that should be run when the application is shutting down, declare it with the event `"shutdown"`: + +```Python hl_lines="6" +{!../../../docs_src/events/tutorial002.py!} +``` + +Here, the `shutdown` event handler function will write a text line `"Application shutdown"` to a file `log.txt`. + +!!! info + In the `open()` function, the `mode="a"` means "append", so, the line will be added after whatever is on that file, without overwriting the previous contents. + +!!! tip + Notice that in this case we are using a standard Python `open()` function that interacts with a file. + + So, it involves I/O (input/output), that requires "waiting" for things to be written to disk. + + But `open()` doesn't use `async` and `await`. + + So, we declare the event handler function with standard `def` instead of `async def`. + +### `startup` and `shutdown` together + +There's a high chance that the logic for your *startup* and *shutdown* is connected, you might want to start something and then finish it, acquire a resource and then release it, etc. + +Doing that in separated functions that don't share logic or variables together is more difficult as you would need to store values in global variables or similar tricks. + +Because of that, it's now recommended to instead use the `lifespan` as explained above. + +## Technical Details + +Just a technical detail for the curious nerds. 🤓 + +Underneath, in the ASGI technical specification, this is part of the Lifespan Protocol, and it defines events called `startup` and `shutdown`. + +!!! info + You can read more about the Starlette `lifespan` handlers in Starlette's Lifespan' docs. + + Including how to handle lifespan state that can be used in other areas of your code. + +## Sub Applications + +🚨 Have in mind that these lifespan events (startup and shutdown) will only be executed for the main application, not for [Sub Applications - Mounts](./sub-applications.md){.internal-link target=_blank}. From d207dce4aa7c98067a557a1f9328d827d058aaa3 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:37 +0800 Subject: [PATCH 012/163] New translations extending-openapi.md (Chinese Simplified) --- docs/zh/docs/advanced/extending-openapi.md | 318 +++++++++++++++++++++ 1 file changed, 318 insertions(+) create mode 100644 docs/zh/docs/advanced/extending-openapi.md diff --git a/docs/zh/docs/advanced/extending-openapi.md b/docs/zh/docs/advanced/extending-openapi.md new file mode 100644 index 0000000000000..fe3f649f93230 --- /dev/null +++ b/docs/zh/docs/advanced/extending-openapi.md @@ -0,0 +1,318 @@ +# Extending OpenAPI + +!!! warning + This is a rather advanced feature. You probably can skip it. + + If you are just following the tutorial - user guide, you can probably skip this section. + + If you already know that you need to modify the generated OpenAPI schema, continue reading. + +There are some cases where you might need to modify the generated OpenAPI schema. + +In this section you will see how. + +## The normal process + +The normal (default) process, is as follows. + +A `FastAPI` application (instance) has an `.openapi()` method that is expected to return the OpenAPI schema. + +As part of the application object creation, a *path operation* for `/openapi.json` (or for whatever you set your `openapi_url`) is registered. + +It just returns a JSON response with the result of the application's `.openapi()` method. + +By default, what the method `.openapi()` does is check the property `.openapi_schema` to see if it has contents and return them. + +If it doesn't, it generates them using the utility function at `fastapi.openapi.utils.get_openapi`. + +And that function `get_openapi()` receives as parameters: + +* `title`: The OpenAPI title, shown in the docs. +* `version`: The version of your API, e.g. `2.5.0`. +* `openapi_version`: The version of the OpenAPI specification used. By default, the latest: `3.1.0`. +* `summary`: A short summary of the API. +* `description`: The description of your API, this can include markdown and will be shown in the docs. +* `routes`: A list of routes, these are each of the registered *path operations*. They are taken from `app.routes`. + +!!! info + The parameter `summary` is available in OpenAPI 3.1.0 and above, supported by FastAPI 0.99.0 and above. + +## Overriding the defaults + +Using the information above, you can use the same utility function to generate the OpenAPI schema and override each part that you need. + +For example, let's add ReDoc's OpenAPI extension to include a custom logo. + +### Normal **FastAPI** + +First, write all your **FastAPI** application as normally: + +```Python hl_lines="1 4 7-9" +{!../../../docs_src/extending_openapi/tutorial001.py!} +``` + +### Generate the OpenAPI schema + +Then, use the same utility function to generate the OpenAPI schema, inside a `custom_openapi()` function: + +```Python hl_lines="2 15-21" +{!../../../docs_src/extending_openapi/tutorial001.py!} +``` + +### Modify the OpenAPI schema + +Now you can add the ReDoc extension, adding a custom `x-logo` to the `info` "object" in the OpenAPI schema: + +```Python hl_lines="22-24" +{!../../../docs_src/extending_openapi/tutorial001.py!} +``` + +### Cache the OpenAPI schema + +You can use the property `.openapi_schema` as a "cache", to store your generated schema. + +That way, your application won't have to generate the schema every time a user opens your API docs. + +It will be generated only once, and then the same cached schema will be used for the next requests. + +```Python hl_lines="13-14 25-26" +{!../../../docs_src/extending_openapi/tutorial001.py!} +``` + +### Override the method + +Now you can replace the `.openapi()` method with your new function. + +```Python hl_lines="29" +{!../../../docs_src/extending_openapi/tutorial001.py!} +``` + +### Check it + +Once you go to http://127.0.0.1:8000/redoc you will see that you are using your custom logo (in this example, **FastAPI**'s logo): + + + +## Self-hosting JavaScript and CSS for docs + +The API docs use **Swagger UI** and **ReDoc**, and each of those need some JavaScript and CSS files. + +By default, those files are served from a CDN. + +But it's possible to customize it, you can set a specific CDN, or serve the files yourself. + +That's useful, for example, if you need your app to keep working even while offline, without open Internet access, or in a local network. + +Here you'll see how to serve those files yourself, in the same FastAPI app, and configure the docs to use them. + +### Project file structure + +Let's say your project file structure looks like this: + +``` +. +├── app +│ ├── __init__.py +│ ├── main.py +``` + +Now create a directory to store those static files. + +Your new file structure could look like this: + +``` +. +├── app +│   ├── __init__.py +│   ├── main.py +└── static/ +``` + +### Download the files + +Download the static files needed for the docs and put them on that `static/` directory. + +You can probably right-click each link and select an option similar to `Save link as...`. + +**Swagger UI** uses the files: + +* `swagger-ui-bundle.js` +* `swagger-ui.css` + +And **ReDoc** uses the file: + +* `redoc.standalone.js` + +After that, your file structure could look like: + +``` +. +├── app +│   ├── __init__.py +│   ├── main.py +└── static + ├── redoc.standalone.js + ├── swagger-ui-bundle.js + └── swagger-ui.css +``` + +### Serve the static files + +* Import `StaticFiles`. +* "Mount" a `StaticFiles()` instance in a specific path. + +```Python hl_lines="7 11" +{!../../../docs_src/extending_openapi/tutorial002.py!} +``` + +### Test the static files + +Start your application and go to http://127.0.0.1:8000/static/redoc.standalone.js. + +You should see a very long JavaScript file for **ReDoc**. + +It could start with something like: + +```JavaScript +/*! + * ReDoc - OpenAPI/Swagger-generated API Reference Documentation + * ------------------------------------------------------------- + * Version: "2.0.0-rc.18" + * Repo: https://github.com/Redocly/redoc + */ +!function(e,t){"object"==typeof exports&&"object"==typeof m + +... +``` + +That confirms that you are being able to serve static files from your app, and that you placed the static files for the docs in the correct place. + +Now we can configure the app to use those static files for the docs. + +### Disable the automatic docs + +The first step is to disable the automatic docs, as those use the CDN by default. + +To disable them, set their URLs to `None` when creating your `FastAPI` app: + +```Python hl_lines="9" +{!../../../docs_src/extending_openapi/tutorial002.py!} +``` + +### Include the custom docs + +Now you can create the *path operations* for the custom docs. + +You can re-use FastAPI's internal functions to create the HTML pages for the docs, and pass them the needed arguments: + +* `openapi_url`: the URL where the HTML page for the docs can get the OpenAPI schema for your API. You can use here the attribute `app.openapi_url`. +* `title`: the title of your API. +* `oauth2_redirect_url`: you can use `app.swagger_ui_oauth2_redirect_url` here to use the default. +* `swagger_js_url`: the URL where the HTML for your Swagger UI docs can get the **JavaScript** file. This is the one that your own app is now serving. +* `swagger_css_url`: the URL where the HTML for your Swagger UI docs can get the **CSS** file. This is the one that your own app is now serving. + +And similarly for ReDoc... + +```Python hl_lines="2-6 14-22 25-27 30-36" +{!../../../docs_src/extending_openapi/tutorial002.py!} +``` + +!!! tip + The *path operation* for `swagger_ui_redirect` is a helper for when you use OAuth2. + + If you integrate your API with an OAuth2 provider, you will be able to authenticate and come back to the API docs with the acquired credentials. And interact with it using the real OAuth2 authentication. + + Swagger UI will handle it behind the scenes for you, but it needs this "redirect" helper. + +### Create a *path operation* to test it + +Now, to be able to test that everything works, create a *path operation*: + +```Python hl_lines="39-41" +{!../../../docs_src/extending_openapi/tutorial002.py!} +``` + +### Test it + +Now, you should be able to disconnect your WiFi, go to your docs at http://127.0.0.1:8000/docs, and reload the page. + +And even without Internet, you would be able to see the docs for your API and interact with it. + +## Configuring Swagger UI + +You can configure some extra Swagger UI parameters. + +To configure them, pass the `swagger_ui_parameters` argument when creating the `FastAPI()` app object or to the `get_swagger_ui_html()` function. + +`swagger_ui_parameters` receives a dictionary with the configurations passed to Swagger UI directly. + +FastAPI converts the configurations to **JSON** to make them compatible with JavaScript, as that's what Swagger UI needs. + +### Disable Syntax Highlighting + +For example, you could disable syntax highlighting in Swagger UI. + +Without changing the settings, syntax highlighting is enabled by default: + + + +But you can disable it by setting `syntaxHighlight` to `False`: + +```Python hl_lines="3" +{!../../../docs_src/extending_openapi/tutorial003.py!} +``` + +...and then Swagger UI won't show the syntax highlighting anymore: + + + +### Change the Theme + +The same way you could set the syntax highlighting theme with the key `"syntaxHighlight.theme"` (notice that it has a dot in the middle): + +```Python hl_lines="3" +{!../../../docs_src/extending_openapi/tutorial004.py!} +``` + +That configuration would change the syntax highlighting color theme: + + + +### Change Default Swagger UI Parameters + +FastAPI includes some default configuration parameters appropriate for most of the use cases. + +It includes these default configurations: + +```Python +{!../../../fastapi/openapi/docs.py[ln:7-13]!} +``` + +You can override any of them by setting a different value in the argument `swagger_ui_parameters`. + +For example, to disable `deepLinking` you could pass these settings to `swagger_ui_parameters`: + +```Python hl_lines="3" +{!../../../docs_src/extending_openapi/tutorial005.py!} +``` + +### Other Swagger UI Parameters + +To see all the other possible configurations you can use, read the official docs for Swagger UI parameters. + +### JavaScript-only settings + +Swagger UI also allows other configurations to be **JavaScript-only** objects (for example, JavaScript functions). + +FastAPI also includes these JavaScript-only `presets` settings: + +```JavaScript +presets: [ + SwaggerUIBundle.presets.apis, + SwaggerUIBundle.SwaggerUIStandalonePreset +] +``` + +These are **JavaScript** objects, not strings, so you can't pass them from Python code directly. + +If you need to use JavaScript-only configurations like those, you can use one of the methods above. Override all the Swagger UI *path operation* and manually write any JavaScript you need. From 862544547cbc05b97425313456d93567954ac269 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:38 +0800 Subject: [PATCH 013/163] New translations generate-clients.md (Chinese Simplified) --- docs/zh/docs/advanced/generate-clients.md | 266 ++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 docs/zh/docs/advanced/generate-clients.md diff --git a/docs/zh/docs/advanced/generate-clients.md b/docs/zh/docs/advanced/generate-clients.md new file mode 100644 index 0000000000000..5edfccb23b1a5 --- /dev/null +++ b/docs/zh/docs/advanced/generate-clients.md @@ -0,0 +1,266 @@ +# Generate Clients + +As **FastAPI** is based on the OpenAPI specification, you get automatic compatibility with many tools, including the automatic API docs (provided by Swagger UI). + +One particular advantage that is not necessarily obvious is that you can **generate clients** (sometimes called **SDKs** ) for your API, for many different **programming languages**. + +## OpenAPI Client Generators + +There are many tools to generate clients from **OpenAPI**. + +A common tool is OpenAPI Generator. + +If you are building a **frontend**, a very interesting alternative is openapi-typescript-codegen. + +## Generate a TypeScript Frontend Client + +Let's start with a simple FastAPI application: + +=== "Python 3.9+" + + ```Python hl_lines="7-9 12-13 16-17 21" + {!> ../../../docs_src/generate_clients/tutorial001_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9-11 14-15 18 19 23" + {!> ../../../docs_src/generate_clients/tutorial001.py!} + ``` + +Notice that the *path operations* define the models they use for request payload and response payload, using the models `Item` and `ResponseMessage`. + +### API Docs + +If you go to the API docs, you will see that it has the **schemas** for the data to be sent in requests and received in responses: + + + +You can see those schemas because they were declared with the models in the app. + +That information is available in the app's **OpenAPI schema**, and then shown in the API docs (by Swagger UI). + +And that same information from the models that is included in OpenAPI is what can be used to **generate the client code**. + +### Generate a TypeScript Client + +Now that we have the app with the models, we can generate the client code for the frontend. + +#### Install `openapi-typescript-codegen` + +You can install `openapi-typescript-codegen` in your frontend code with: + +
+ +```console +$ npm install openapi-typescript-codegen --save-dev + +---> 100% +``` + +
+ +#### Generate Client Code + +To generate the client code you can use the command line application `openapi` that would now be installed. + +Because it is installed in the local project, you probably wouldn't be able to call that command directly, but you would put it on your `package.json` file. + +It could look like this: + +```JSON hl_lines="7" +{ + "name": "frontend-app", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "generate-client": "openapi --input http://localhost:8000/openapi.json --output ./src/client --client axios" + }, + "author": "", + "license": "", + "devDependencies": { + "openapi-typescript-codegen": "^0.20.1", + "typescript": "^4.6.2" + } +} +``` + +After having that NPM `generate-client` script there, you can run it with: + +
+ +```console +$ npm run generate-client + +frontend-app@1.0.0 generate-client /home/user/code/frontend-app +> openapi --input http://localhost:8000/openapi.json --output ./src/client --client axios +``` + +
+ +That command will generate code in `./src/client` and will use `axios` (the frontend HTTP library) internally. + +### Try Out the Client Code + +Now you can import and use the client code, it could look like this, notice that you get autocompletion for the methods: + + + +You will also get autocompletion for the payload to send: + + + +!!! tip + Notice the autocompletion for `name` and `price`, that was defined in the FastAPI application, in the `Item` model. + +You will have inline errors for the data that you send: + + + +The response object will also have autocompletion: + + + +## FastAPI App with Tags + +In many cases your FastAPI app will be bigger, and you will probably use tags to separate different groups of *path operations*. + +For example, you could have a section for **items** and another section for **users**, and they could be separated by tags: + +=== "Python 3.9+" + + ```Python hl_lines="21 26 34" + {!> ../../../docs_src/generate_clients/tutorial002_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="23 28 36" + {!> ../../../docs_src/generate_clients/tutorial002.py!} + ``` + +### Generate a TypeScript Client with Tags + +If you generate a client for a FastAPI app using tags, it will normally also separate the client code based on the tags. + +This way you will be able to have things ordered and grouped correctly for the client code: + + + +In this case you have: + +* `ItemsService` +* `UsersService` + +### Client Method Names + +Right now the generated method names like `createItemItemsPost` don't look very clean: + +```TypeScript +ItemsService.createItemItemsPost({name: "Plumbus", price: 5}) +``` + +...that's because the client generator uses the OpenAPI internal **operation ID** for each *path operation*. + +OpenAPI requires that each operation ID is unique across all the *path operations*, so FastAPI uses the **function name**, the **path**, and the **HTTP method/operation** to generate that operation ID, because that way it can make sure that the operation IDs are unique. + +But I'll show you how to improve that next. 🤓 + +## Custom Operation IDs and Better Method Names + +You can **modify** the way these operation IDs are **generated** to make them simpler and have **simpler method names** in the clients. + +In this case you will have to ensure that each operation ID is **unique** in some other way. + +For example, you could make sure that each *path operation* has a tag, and then generate the operation ID based on the **tag** and the *path operation* **name** (the function name). + +### Custom Generate Unique ID Function + +FastAPI uses a **unique ID** for each *path operation*, it is used for the **operation ID** and also for the names of any needed custom models, for requests or responses. + +You can customize that function. It takes an `APIRoute` and outputs a string. + +For example, here it is using the first tag (you will probably have only one tag) and the *path operation* name (the function name). + +You can then pass that custom function to **FastAPI** as the `generate_unique_id_function` parameter: + +=== "Python 3.9+" + + ```Python hl_lines="6-7 10" + {!> ../../../docs_src/generate_clients/tutorial003_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="8-9 12" + {!> ../../../docs_src/generate_clients/tutorial003.py!} + ``` + +### Generate a TypeScript Client with Custom Operation IDs + +Now if you generate the client again, you will see that it has the improved method names: + + + +As you see, the method names now have the tag and then the function name, now they don't include information from the URL path and the HTTP operation. + +### Preprocess the OpenAPI Specification for the Client Generator + +The generated code still has some **duplicated information**. + +We already know that this method is related to the **items** because that word is in the `ItemsService` (taken from the tag), but we still have the tag name prefixed in the method name too. 😕 + +We will probably still want to keep it for OpenAPI in general, as that will ensure that the operation IDs are **unique**. + +But for the generated client we could **modify** the OpenAPI operation IDs right before generating the clients, just to make those method names nicer and **cleaner**. + +We could download the OpenAPI JSON to a file `openapi.json` and then we could **remove that prefixed tag** with a script like this: + +```Python +{!../../../docs_src/generate_clients/tutorial004.py!} +``` + +With that, the operation IDs would be renamed from things like `items-get_items` to just `get_items`, that way the client generator can generate simpler method names. + +### Generate a TypeScript Client with the Preprocessed OpenAPI + +Now as the end result is in a file `openapi.json`, you would modify the `package.json` to use that local file, for example: + +```JSON hl_lines="7" +{ + "name": "frontend-app", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "generate-client": "openapi --input ./openapi.json --output ./src/client --client axios" + }, + "author": "", + "license": "", + "devDependencies": { + "openapi-typescript-codegen": "^0.20.1", + "typescript": "^4.6.2" + } +} +``` + +After generating the new client, you would now have **clean method names**, with all the **autocompletion**, **inline errors**, etc: + + + +## Benefits + +When using the automatically generated clients you would **autocompletion** for: + +* Methods. +* Request payloads in the body, query parameters, etc. +* Response payloads. + +You would also have **inline errors** for everything. + +And whenever you update the backend code, and **regenerate** the frontend, it would have any new *path operations* available as methods, the old ones removed, and any other change would be reflected on the generated code. 🤓 + +This also means that if something changed it will be **reflected** on the client code automatically. And if you **build** the client it will error out if you have any **mismatch** in the data used. + +So, you would **detect many errors** very early in the development cycle instead of having to wait for the errors to show up to your final users in production and then trying to debug where the problem is. ✨ From 6ca3045289127468499ef324ff5655750e447071 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:39 +0800 Subject: [PATCH 014/163] New translations graphql.md (Chinese Simplified) --- docs/zh/docs/advanced/graphql.md | 59 ++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 docs/zh/docs/advanced/graphql.md diff --git a/docs/zh/docs/advanced/graphql.md b/docs/zh/docs/advanced/graphql.md new file mode 100644 index 0000000000000..1d2753fc4ce1b --- /dev/null +++ b/docs/zh/docs/advanced/graphql.md @@ -0,0 +1,59 @@ +# GraphQL + +As **FastAPI** is based on the **ASGI** standard, it's very easy to integrate any **GraphQL** library also compatible with ASGI. + +You can combine normal FastAPI *path operations* with GraphQL on the same application. + +!!! tip + **GraphQL** solves some very specific use cases. + + It has **advantages** and **disadvantages** when compared to common **web APIs**. + + Make sure you evaluate if the **benefits** for your use case compensate the **drawbacks**. 🤓 + +## GraphQL Libraries + +Here are some of the **GraphQL** libraries that have **ASGI** support. You could use them with **FastAPI**: + +* Strawberry 🍓 + * With docs for FastAPI +* Ariadne + + * With docs for Starlette (that also apply to FastAPI) +* Tartiflette + + * With Tartiflette ASGI to provide ASGI integration +* Graphene + + * With starlette-graphene3 + +## GraphQL with Strawberry + +If you need or want to work with **GraphQL**, **Strawberry** is the **recommended** library as it has the design closest to **FastAPI's** design, it's all based on **type annotations**. + +Depending on your use case, you might prefer to use a different library, but if you asked me, I would probably suggest you try **Strawberry**. + +Here's a small preview of how you could integrate Strawberry with FastAPI: + +```Python hl_lines="3 22 25-26" +{!../../../docs_src/graphql/tutorial001.py!} +``` + +You can learn more about Strawberry in the Strawberry documentation. + +And also the docs about Strawberry with FastAPI. + +## Older `GraphQLApp` from Starlette + +Previous versions of Starlette included a `GraphQLApp` class to integrate with Graphene. + +It was deprecated from Starlette, but if you have code that used it, you can easily **migrate** to starlette-graphene3, that covers the same use case and has an **almost identical interface**. + +!!! tip + If you need GraphQL, I still would recommend you check out Strawberry, as it's based on type annotations instead of custom classes and types. + +## Learn More + +You can learn more about **GraphQL** in the official GraphQL documentation. + +You can also read more about each those libraries described above in their links. From 66fbb1ca3c142a11fe436577150753e52ea97f6b Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:40 +0800 Subject: [PATCH 015/163] New translations index.md (Chinese Simplified) --- docs/zh/docs/advanced/index.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/docs/zh/docs/advanced/index.md b/docs/zh/docs/advanced/index.md index 824f91f47ba90..467f0833e60a6 100644 --- a/docs/zh/docs/advanced/index.md +++ b/docs/zh/docs/advanced/index.md @@ -1,18 +1,24 @@ -# 高级用户指南 +# Advanced User Guide -## 额外特性 +## Additional Features -主要的教程 [教程 - 用户指南](../tutorial/){.internal-link target=_blank} 应该足以让你了解 **FastAPI** 的所有主要特性。 +The main [Tutorial - User Guide](../tutorial/){.internal-link target=_blank} should be enough to give you a tour through all the main features of **FastAPI**. -你会在接下来的章节中了解到其他的选项、配置以及额外的特性。 +In the next sections you will see other options, configurations, and additional features. !!! tip - 接下来的章节**并不一定是**「高级的」。 + The next sections are **not necessarily "advanced"**. - 而且对于你的使用场景来说,解决方案很可能就在其中。 + And it's possible that for your use case, the solution is in one of them. -## 先阅读教程 +## Read the Tutorial first -你可能仍会用到 **FastAPI** 主教程 [教程 - 用户指南](../tutorial/){.internal-link target=_blank} 中的大多数特性。 +You could still use most of the features in **FastAPI** with the knowledge from the main [Tutorial - User Guide](../tutorial/){.internal-link target=_blank}. -接下来的章节我们认为你已经读过 [教程 - 用户指南](../tutorial/){.internal-link target=_blank},并且假设你已经知晓其中主要思想。 +And the next sections assume you already read it, and assume that you know those main ideas. + +## TestDriven.io course + +If you would like to take an advanced-beginner course to complement this section of the docs, you might want to check: Test-Driven Development with FastAPI and Docker by **TestDriven.io**. + +They are currently donating 10% of all profits to the development of **FastAPI**. 🎉 😄 From c48d45af7d131e6f6d632c307943a6e9172a12c4 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:41 +0800 Subject: [PATCH 016/163] New translations middleware.md (Chinese Simplified) --- docs/zh/docs/advanced/middleware.md | 99 +++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 docs/zh/docs/advanced/middleware.md diff --git a/docs/zh/docs/advanced/middleware.md b/docs/zh/docs/advanced/middleware.md new file mode 100644 index 0000000000000..9219f1d2cb35d --- /dev/null +++ b/docs/zh/docs/advanced/middleware.md @@ -0,0 +1,99 @@ +# Advanced Middleware + +In the main tutorial you read how to add [Custom Middleware](../tutorial/middleware.md){.internal-link target=_blank} to your application. + +And then you also read how to handle [CORS with the `CORSMiddleware`](../tutorial/cors.md){.internal-link target=_blank}. + +In this section we'll see how to use other middlewares. + +## Adding ASGI middlewares + +As **FastAPI** is based on Starlette and implements the ASGI specification, you can use any ASGI middleware. + +A middleware doesn't have to be made for FastAPI or Starlette to work, as long as it follows the ASGI spec. + +In general, ASGI middlewares are classes that expect to receive an ASGI app as the first argument. + +So, in the documentation for third-party ASGI middlewares they will probably tell you to do something like: + +```Python +from unicorn import UnicornMiddleware + +app = SomeASGIApp() + +new_app = UnicornMiddleware(app, some_config="rainbow") +``` + +But FastAPI (actually Starlette) provides a simpler way to do it that makes sure that the internal middlewares to handle server errors and custom exception handlers work properly. + +For that, you use `app.add_middleware()` (as in the example for CORS). + +```Python +from fastapi import FastAPI +from unicorn import UnicornMiddleware + +app = FastAPI() + +app.add_middleware(UnicornMiddleware, some_config="rainbow") +``` + +`app.add_middleware()` receives a middleware class as the first argument and any additional arguments to be passed to the middleware. + +## Integrated middlewares + +**FastAPI** includes several middlewares for common use cases, we'll see next how to use them. + +!!! note "Technical Details" + For the next examples, you could also use `from starlette.middleware.something import SomethingMiddleware`. + + **FastAPI** provides several middlewares in `fastapi.middleware` just as a convenience for you, the developer. But most of the available middlewares come directly from Starlette. + +## `HTTPSRedirectMiddleware` + +Enforces that all incoming requests must either be `https` or `wss`. + +Any incoming requests to `http` or `ws` will be redirected to the secure scheme instead. + +```Python hl_lines="2 6" +{!../../../docs_src/advanced_middleware/tutorial001.py!} +``` + +## `TrustedHostMiddleware` + +Enforces that all incoming requests have a correctly set `Host` header, in order to guard against HTTP Host Header attacks. + +```Python hl_lines="2 6-8" +{!../../../docs_src/advanced_middleware/tutorial002.py!} +``` + +The following arguments are supported: + +* `allowed_hosts` - A list of domain names that should be allowed as hostnames. Wildcard domains such as `*.example.com` are supported for matching subdomains. To allow any hostname either use `allowed_hosts=["*"]` or omit the middleware. + +If an incoming request does not validate correctly then a `400` response will be sent. + +## `GZipMiddleware` + +Handles GZip responses for any request that includes `"gzip"` in the `Accept-Encoding` header. + +The middleware will handle both standard and streaming responses. + +```Python hl_lines="2 6" +{!../../../docs_src/advanced_middleware/tutorial003.py!} +``` + +The following arguments are supported: + +* `minimum_size` - Do not GZip responses that are smaller than this minimum size in bytes. Defaults to `500`. + +## Other middlewares + +There are many other ASGI middlewares. + +For example: + +* Sentry +* Uvicorn's `ProxyHeadersMiddleware` +* MessagePack + +To see other available middlewares check Starlette's Middleware docs and the ASGI Awesome List. From 47303c22cd196b33cb6801d2037de6d2192bb337 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:42 +0800 Subject: [PATCH 017/163] New translations nosql-databases.md (Chinese Simplified) --- docs/zh/docs/advanced/nosql-databases.md | 163 +++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 docs/zh/docs/advanced/nosql-databases.md diff --git a/docs/zh/docs/advanced/nosql-databases.md b/docs/zh/docs/advanced/nosql-databases.md new file mode 100644 index 0000000000000..f97f986c4261e --- /dev/null +++ b/docs/zh/docs/advanced/nosql-databases.md @@ -0,0 +1,163 @@ +# NoSQL (Distributed / Big Data) Databases + +!!! info + These docs are about to be updated. 🎉 + + The current version assumes Pydantic v1. + + The new docs will hopefully use Pydantic v2 and will use ODMantic with MongoDB. + +**FastAPI** can also be integrated with any NoSQL. + +Here we'll see an example using **Couchbase**, a document based NoSQL database. + +You can adapt it to any other NoSQL database like: + +* **MongoDB** +* **Cassandra** +* **CouchDB** +* **ArangoDB** +* **ElasticSearch**, etc. + +!!! tip + There is an official project generator with **FastAPI** and **Couchbase**, all based on **Docker**, including a frontend and more tools: https://github.com/tiangolo/full-stack-fastapi-couchbase + +## Import Couchbase components + +For now, don't pay attention to the rest, only the imports: + +```Python hl_lines="3-5" +{!../../../docs_src/nosql_databases/tutorial001.py!} +``` + +## Define a constant to use as a "document type" + +We will use it later as a fixed field `type` in our documents. + +This is not required by Couchbase, but is a good practice that will help you afterwards. + +```Python hl_lines="9" +{!../../../docs_src/nosql_databases/tutorial001.py!} +``` + +## Add a function to get a `Bucket` + +In **Couchbase**, a bucket is a set of documents, that can be of different types. + +They are generally all related to the same application. + +The analogy in the relational database world would be a "database" (a specific database, not the database server). + +The analogy in **MongoDB** would be a "collection". + +In the code, a `Bucket` represents the main entrypoint of communication with the database. + +This utility function will: + +* Connect to a **Couchbase** cluster (that might be a single machine). + * Set defaults for timeouts. +* Authenticate in the cluster. +* Get a `Bucket` instance. + * Set defaults for timeouts. +* Return it. + +```Python hl_lines="12-21" +{!../../../docs_src/nosql_databases/tutorial001.py!} +``` + +## Create Pydantic models + +As **Couchbase** "documents" are actually just "JSON objects", we can model them with Pydantic. + +### `User` model + +First, let's create a `User` model: + +```Python hl_lines="24-28" +{!../../../docs_src/nosql_databases/tutorial001.py!} +``` + +We will use this model in our *path operation function*, so, we don't include in it the `hashed_password`. + +### `UserInDB` model + +Now, let's create a `UserInDB` model. + +This will have the data that is actually stored in the database. + +We don't create it as a subclass of Pydantic's `BaseModel` but as a subclass of our own `User`, because it will have all the attributes in `User` plus a couple more: + +```Python hl_lines="31-33" +{!../../../docs_src/nosql_databases/tutorial001.py!} +``` + +!!! note + Notice that we have a `hashed_password` and a `type` field that will be stored in the database. + + But it is not part of the general `User` model (the one we will return in the *path operation*). + +## Get the user + +Now create a function that will: + +* Take a username. +* Generate a document ID from it. +* Get the document with that ID. +* Put the contents of the document in a `UserInDB` model. + +By creating a function that is only dedicated to getting your user from a `username` (or any other parameter) independent of your *path operation function*, you can more easily re-use it in multiple parts and also add unit tests for it: + +```Python hl_lines="36-42" +{!../../../docs_src/nosql_databases/tutorial001.py!} +``` + +### f-strings + +If you are not familiar with the `f"userprofile::{username}"`, it is a Python "f-string". + +Any variable that is put inside of `{}` in an f-string will be expanded / injected in the string. + +### `dict` unpacking + +If you are not familiar with the `UserInDB(**result.value)`, it is using `dict` "unpacking". + +It will take the `dict` at `result.value`, and take each of its keys and values and pass them as key-values to `UserInDB` as keyword arguments. + +So, if the `dict` contains: + +```Python +{ + "username": "johndoe", + "hashed_password": "some_hash", +} +``` + +It will be passed to `UserInDB` as: + +```Python +UserInDB(username="johndoe", hashed_password="some_hash") +``` + +## Create your **FastAPI** code + +### Create the `FastAPI` app + +```Python hl_lines="46" +{!../../../docs_src/nosql_databases/tutorial001.py!} +``` + +### Create the *path operation function* + +As our code is calling Couchbase and we are not using the experimental Python await support, we should declare our function with normal `def` instead of `async def`. + +Also, Couchbase recommends not using a single `Bucket` object in multiple "threads", so, we can just get the bucket directly and pass it to our utility functions: + +```Python hl_lines="49-53" +{!../../../docs_src/nosql_databases/tutorial001.py!} +``` + +## Recap + +You can integrate any third party NoSQL database, just using their standard packages. + +The same applies to any other external tool, system or API. From 52f74c0423eeb6d479cb9534ea5332e2d1c6aa5c Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:43 +0800 Subject: [PATCH 018/163] New translations openapi-callbacks.md (Chinese Simplified) --- docs/zh/docs/advanced/openapi-callbacks.md | 179 +++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 docs/zh/docs/advanced/openapi-callbacks.md diff --git a/docs/zh/docs/advanced/openapi-callbacks.md b/docs/zh/docs/advanced/openapi-callbacks.md new file mode 100644 index 0000000000000..14004d948e430 --- /dev/null +++ b/docs/zh/docs/advanced/openapi-callbacks.md @@ -0,0 +1,179 @@ +# OpenAPI Callbacks + +You could create an API with a *path operation* that could trigger a request to an *external API* created by someone else (probably the same developer that would be *using* your API). + +The process that happens when your API app calls the *external API* is named a "callback". Because the software that the external developer wrote sends a request to your API and then your API *calls back*, sending a request to an *external API* (that was probably created by the same developer). + +In this case, you could want to document how that external API *should* look like. What *path operation* it should have, what body it should expect, what response it should return, etc. + +## An app with callbacks + +Let's see all this with an example. + +Imagine you develop an app that allows creating invoices. + +These invoices will have an `id`, `title` (optional), `customer`, and `total`. + +The user of your API (an external developer) will create an invoice in your API with a POST request. + +Then your API will (let's imagine): + +* Send the invoice to some customer of the external developer. +* Collect the money. +* Send a notification back to the API user (the external developer). + * This will be done by sending a POST request (from *your API*) to some *external API* provided by that external developer (this is the "callback"). + +## The normal **FastAPI** app + +Let's first see how the normal API app would look like before adding the callback. + +It will have a *path operation* that will receive an `Invoice` body, and a query parameter `callback_url` that will contain the URL for the callback. + +This part is pretty normal, most of the code is probably already familiar to you: + +```Python hl_lines="9-13 36-53" +{!../../../docs_src/openapi_callbacks/tutorial001.py!} +``` + +!!! tip + The `callback_url` query parameter uses a Pydantic URL type. + +The only new thing is the `callbacks=messages_callback_router.routes` as an argument to the *path operation decorator*. We'll see what that is next. + +## Documenting the callback + +The actual callback code will depend heavily on your own API app. + +And it will probably vary a lot from one app to the next. + +It could be just one or two lines of code, like: + +```Python +callback_url = "https://example.com/api/v1/invoices/events/" +httpx.post(callback_url, json={"description": "Invoice paid", "paid": True}) +``` + +But possibly the most important part of the callback is making sure that your API user (the external developer) implements the *external API* correctly, according to the data that *your API* is going to send in the request body of the callback, etc. + +So, what we will do next is add the code to document how that *external API* should look like to receive the callback from *your API*. + +That documentation will show up in the Swagger UI at `/docs` in your API, and it will let external developers know how to build the *external API*. + +This example doesn't implement the callback itself (that could be just a line of code), only the documentation part. + +!!! tip + The actual callback is just an HTTP request. + + When implementing the callback yourself, you could use something like HTTPX or Requests. + +## Write the callback documentation code + +This code won't be executed in your app, we only need it to *document* how that *external API* should look like. + +But, you already know how to easily create automatic documentation for an API with **FastAPI**. + +So we are going to use that same knowledge to document how the *external API* should look like... by creating the *path operation(s)* that the external API should implement (the ones your API will call). + +!!! tip + When writing the code to document a callback, it might be useful to imagine that you are that *external developer*. And that you are currently implementing the *external API*, not *your API*. + + Temporarily adopting this point of view (of the *external developer*) can help you feel like it's more obvious where to put the parameters, the Pydantic model for the body, for the response, etc. for that *external API*. + +### Create a callback `APIRouter` + +First create a new `APIRouter` that will contain one or more callbacks. + +```Python hl_lines="3 25" +{!../../../docs_src/openapi_callbacks/tutorial001.py!} +``` + +### Create the callback *path operation* + +To create the callback *path operation* use the same `APIRouter` you created above. + +It should look just like a normal FastAPI *path operation*: + +* It should probably have a declaration of the body it should receive, e.g. `body: InvoiceEvent`. +* And it could also have a declaration of the response it should return, e.g. `response_model=InvoiceEventReceived`. + +```Python hl_lines="16-18 21-22 28-32" +{!../../../docs_src/openapi_callbacks/tutorial001.py!} +``` + +There are 2 main differences from a normal *path operation*: + +* It doesn't need to have any actual code, because your app will never call this code. It's only used to document the *external API*. So, the function could just have `pass`. +* The *path* can contain an OpenAPI 3 expression (see more below) where it can use variables with parameters and parts of the original request sent to *your API*. + +### The callback path expression + +The callback *path* can have an OpenAPI 3 expression that can contain parts of the original request sent to *your API*. + +In this case, it's the `str`: + +```Python +"{$callback_url}/invoices/{$request.body.id}" +``` + +So, if your API user (the external developer) sends a request to *your API* to: + +``` +https://yourapi.com/invoices/?callback_url=https://www.external.org/events +``` + +with a JSON body of: + +```JSON +{ + "id": "2expen51ve", + "customer": "Mr. Richie Rich", + "total": "9999" +} +``` + +Then *your API* will process the invoice, and at some point later, send a callback request to the `callback_url` (the *external API*): + +``` +https://www.external.org/events/invoices/2expen51ve +``` + +with a JSON body containing something like: + +```JSON +{ + "description": "Payment celebration", + "paid": true +} +``` + +and it would expect a response from that *external API* with a JSON body like: + +```JSON +{ + "ok": true +} +``` + +!!! tip + Notice how the callback URL used contains the URL received as a query parameter in `callback_url` (`https://www.external.org/events`) and also the invoice `id` from inside of the JSON body (`2expen51ve`). + +### Add the callback router + +At this point you have the *callback path operation(s)* needed (the one(s) that the *external developer* should implement in the *external API*) in the callback router you created above. + +Now use the parameter `callbacks` in *your API's path operation decorator* to pass the attribute `.routes` (that's actually just a `list` of routes/*path operations*) from that callback router: + +```Python hl_lines="35" +{!../../../docs_src/openapi_callbacks/tutorial001.py!} +``` + +!!! tip + Notice that you are not passing the router itself (`invoices_callback_router`) to `callback=`, but the attribute `.routes`, as in `invoices_callback_router.routes`. + +### Check the docs + +Now you can start your app with Uvicorn and go to http://127.0.0.1:8000/docs. + +You will see your docs including a "Callback" section for your *path operation* that shows how the *external API* should look like: + + From b450dc8fcd9ec33881f3e92a834df491c628255c Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:44 +0800 Subject: [PATCH 019/163] New translations openapi-webhooks.md (Chinese Simplified) --- docs/zh/docs/advanced/openapi-webhooks.md | 51 +++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 docs/zh/docs/advanced/openapi-webhooks.md diff --git a/docs/zh/docs/advanced/openapi-webhooks.md b/docs/zh/docs/advanced/openapi-webhooks.md new file mode 100644 index 0000000000000..a41f8c820490d --- /dev/null +++ b/docs/zh/docs/advanced/openapi-webhooks.md @@ -0,0 +1,51 @@ +# OpenAPI Webhooks + +There are cases where you want to tell your API **users** that your app could call *their* app (sending a request) with some data, normally to **notify** of some type of **event**. + +This means that instead of the normal process of your users sending requests to your API, it's **your API** (or your app) that could **send requests to their system** (to their API, their app). + +This is normally called a **webhook**. + +## Webhooks steps + +The process normally is that **you define** in your code what is the message that you will send, the **body of the request**. + +You also define in some way at which **moments** your app will send those requests or events. + +And **your users** define in some way (for example in a web dashboard somewhere) the **URL** where your app should send those requests. + +All the **logic** about how to register the URLs for webhooks and the code to actually send those requests is up to you. You write it however you want to in **your own code**. + +## Documenting webhooks with **FastAPI** and OpenAPI + +With **FastAPI**, using OpenAPI, you can define the names of these webhooks, the types of HTTP operations that your app can send (e.g. `POST`, `PUT`, etc.) and the request **bodies** that your app would send. + +This can make it a lot easier for your users to **implement their APIs** to receive your **webhook** requests, they might even be able to autogenerate some of their own API code. + +!!! info + Webhooks are available in OpenAPI 3.1.0 and above, supported by FastAPI `0.99.0` and above. + +## An app with webhooks + +When you create a **FastAPI** application, there is a `webhooks` attribute that you can use to define *webhooks*, the same way you would define *path operations*, for example with `@app.webhooks.post()`. + +```Python hl_lines="9-13 36-53" +{!../../../docs_src/openapi_webhooks/tutorial001.py!} +``` + +The webhooks that you define will end up in the **OpenAPI** schema and the automatic **docs UI**. + +!!! info + The `app.webhooks` object is actually just an `APIRouter`, the same type you would use when structuring your app with multiple files. + +Notice that with webhooks you are actually not declaring a *path* (like `/items/`), the text you pass there is just an **identifier** of the webhook (the name of the event), for example in `@app.webhooks.post("new-subscription")`, the webhook name is `new-subscription`. + +This is because it is expected that **your users** would define the actual **URL path** where they want to receive the webhook request in some other way (e.g. a web dashboard). + +### Check the docs + +Now you can start your app with Uvicorn and go to http://127.0.0.1:8000/docs. + +You will see your docs have the normal *path operations* and now also some **webhooks**: + + From 1d3a76ee649eb2f513a40807b2afa17d8160cdef Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:44 +0800 Subject: [PATCH 020/163] New translations path-operation-advanced-configuration.md (Chinese Simplified) --- .../path-operation-advanced-configuration.md | 179 ++++++++++++++++-- 1 file changed, 159 insertions(+), 20 deletions(-) diff --git a/docs/zh/docs/advanced/path-operation-advanced-configuration.md b/docs/zh/docs/advanced/path-operation-advanced-configuration.md index 7da9f251e30db..f8bffadf4ffcf 100644 --- a/docs/zh/docs/advanced/path-operation-advanced-configuration.md +++ b/docs/zh/docs/advanced/path-operation-advanced-configuration.md @@ -1,53 +1,192 @@ -# 路径操作的高级配置 +# Path Operation Advanced Configuration -## OpenAPI 的 operationId +## OpenAPI operationId !!! warning - 如果你并非 OpenAPI 的「专家」,你可能不需要这部分内容。 + If you are not an "expert" in OpenAPI, you probably don't need this. -你可以在路径操作中通过参数 `operation_id` 设置要使用的 OpenAPI `operationId`。 +You can set the OpenAPI `operationId` to be used in your *path operation* with the parameter `operation_id`. -务必确保每个操作路径的 `operation_id` 都是唯一的。 +You would have to make sure that it is unique for each operation. ```Python hl_lines="6" {!../../../docs_src/path_operation_advanced_configuration/tutorial001.py!} ``` -### 使用 *路径操作函数* 的函数名作为 operationId +### Using the *path operation function* name as the operationId -如果你想用你的 API 的函数名作为 `operationId` 的名字,你可以遍历一遍 API 的函数名,然后使用他们的 `APIRoute.name` 重写每个 *路径操作* 的 `operation_id`。 +If you want to use your APIs' function names as `operationId`s, you can iterate over all of them and override each *path operation's* `operation_id` using their `APIRoute.name`. -你应该在添加了所有 *路径操作* 之后执行此操作。 +You should do it after adding all your *path operations*. -```Python hl_lines="2 12 13 14 15 16 17 18 19 20 21 24" +```Python hl_lines="2 12-21 24" {!../../../docs_src/path_operation_advanced_configuration/tutorial002.py!} ``` !!! tip - 如果你手动调用 `app.openapi()`,你应该在此之前更新 `operationId`。 + If you manually call `app.openapi()`, you should update the `operationId`s before that. !!! warning - 如果你这样做,务必确保你的每个 *路径操作函数* 的名字唯一。 + If you do this, you have to make sure each one of your *path operation functions* has a unique name. - 即使它们在不同的模块中(Python 文件)。 + Even if they are in different modules (Python files). -## 从 OpenAPI 中排除 +## Exclude from OpenAPI -使用参数 `include_in_schema` 并将其设置为 `False` ,来从生成的 OpenAPI 方案中排除一个 *路径操作*(这样一来,就从自动化文档系统中排除掉了)。 +To exclude a *path operation* from the generated OpenAPI schema (and thus, from the automatic documentation systems), use the parameter `include_in_schema` and set it to `False`: ```Python hl_lines="6" {!../../../docs_src/path_operation_advanced_configuration/tutorial003.py!} ``` -## docstring 的高级描述 +## Advanced description from docstring -你可以限制 *路径操作函数* 的 `docstring` 中用于 OpenAPI 的行数。 +You can limit the lines used from the docstring of a *path operation function* for OpenAPI. -添加一个 `\f` (一个「换页」的转义字符)可以使 **FastAPI** 在那一位置截断用于 OpenAPI 的输出。 +Adding an `\f` (an escaped "form feed" character) causes **FastAPI** to truncate the output used for OpenAPI at this point. -剩余部分不会出现在文档中,但是其他工具(比如 Sphinx)可以使用剩余部分。 +It won't show up in the documentation, but other tools (such as Sphinx) will be able to use the rest. - -```Python hl_lines="19 20 21 22 23 24 25 26 27 28 29" +```Python hl_lines="19-29" {!../../../docs_src/path_operation_advanced_configuration/tutorial004.py!} ``` + +## Additional Responses + +You probably have seen how to declare the `response_model` and `status_code` for a *path operation*. + +That defines the metadata about the main response of a *path operation*. + +You can also declare additional responses with their models, status codes, etc. + +There's a whole chapter here in the documentation about it, you can read it at [Additional Responses in OpenAPI](./additional-responses.md){.internal-link target=_blank}. + +## OpenAPI Extra + +When you declare a *path operation* in your application, **FastAPI** automatically generates the relevant metadata about that *path operation* to be included in the OpenAPI schema. + +!!! note "Technical details" + In the OpenAPI specification it is called the Operation Object. + +It has all the information about the *path operation* and is used to generate the automatic documentation. + +It includes the `tags`, `parameters`, `requestBody`, `responses`, etc. + +This *path operation*-specific OpenAPI schema is normally generated automatically by **FastAPI**, but you can also extend it. + +!!! tip + This is a low level extension point. + + If you only need to declare additional responses, a more convenient way to do it is with [Additional Responses in OpenAPI](./additional-responses.md){.internal-link target=_blank}. + +You can extend the OpenAPI schema for a *path operation* using the parameter `openapi_extra`. + +### OpenAPI Extensions + +This `openapi_extra` can be helpful, for example, to declare [OpenAPI Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions): + +```Python hl_lines="6" +{!../../../docs_src/path_operation_advanced_configuration/tutorial005.py!} +``` + +If you open the automatic API docs, your extension will show up at the bottom of the specific *path operation*. + + + +And if you see the resulting OpenAPI (at `/openapi.json` in your API), you will see your extension as part of the specific *path operation* too: + +```JSON hl_lines="22" +{ + "openapi": "3.1.0", + "info": { + "title": "FastAPI", + "version": "0.1.0" + }, + "paths": { + "/items/": { + "get": { + "summary": "Read Items", + "operationId": "read_items_items__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "x-aperture-labs-portal": "blue" + } + } + } +} +``` + +### Custom OpenAPI *path operation* schema + +The dictionary in `openapi_extra` will be deeply merged with the automatically generated OpenAPI schema for the *path operation*. + +So, you could add additional data to the automatically generated schema. + +For example, you could decide to read and validate the request with your own code, without using the automatic features of FastAPI with Pydantic, but you could still want to define the request in the OpenAPI schema. + +You could do that with `openapi_extra`: + +```Python hl_lines="20-37 39-40" +{!../../../docs_src/path_operation_advanced_configuration/tutorial006.py!} +``` + +In this example, we didn't declare any Pydantic model. In fact, the request body is not even parsed as JSON, it is read directly as `bytes`, and the function `magic_data_reader()` would be in charge of parsing it in some way. + +Nevertheless, we can declare the expected schema for the request body. + +### Custom OpenAPI content type + +Using this same trick, you could use a Pydantic model to define the JSON Schema that is then included in the custom OpenAPI schema section for the *path operation*. + +And you could do this even if the data type in the request is not JSON. + +For example, in this application we don't use FastAPI's integrated functionality to extract the JSON Schema from Pydantic models nor the automatic validation for JSON. In fact, we are declaring the request content type as YAML, not JSON: + +=== "Pydantic v2" + + ```Python hl_lines="17-22 24" + {!> ../../../docs_src/path_operation_advanced_configuration/tutorial007.py!} + ``` + +=== "Pydantic v1" + + ```Python hl_lines="17-22 24" + {!> ../../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py!} + ``` + +!!! info + In Pydantic version 1 the method to get the JSON Schema for a model was called `Item.schema()`, in Pydantic version 2, the method is called `Item.model_schema_json()`. + +Nevertheless, although we are not using the default integrated functionality, we are still using a Pydantic model to manually generate the JSON Schema for the data that we want to receive in YAML. + +Then we use the request directly, and extract the body as `bytes`. This means that FastAPI won't even try to parse the request payload as JSON. + +And then in our code, we parse that YAML content directly, and then we are again using the same Pydantic model to validate the YAML content: + +=== "Pydantic v2" + + ```Python hl_lines="26-33" + {!> ../../../docs_src/path_operation_advanced_configuration/tutorial007.py!} + ``` + +=== "Pydantic v1" + + ```Python hl_lines="26-33" + {!> ../../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py!} + ``` + +!!! info + In Pydantic version 1 the method to parse and validate an object was `Item.parse_obj()`, in Pydantic version 2, the method is called `Item.model_validate()`. + +!!! tip + Here we re-use the same Pydantic model. + + But the same way, we could have validated it in some other way. From 439b6faea51984ed9f871016a676bc941f989be4 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:45 +0800 Subject: [PATCH 021/163] New translations response-change-status-code.md (Chinese Simplified) --- .../advanced/response-change-status-code.md | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/zh/docs/advanced/response-change-status-code.md b/docs/zh/docs/advanced/response-change-status-code.md index a289cf20178f0..979cef3f05367 100644 --- a/docs/zh/docs/advanced/response-change-status-code.md +++ b/docs/zh/docs/advanced/response-change-status-code.md @@ -1,31 +1,33 @@ -# 响应 - 更改状态码 +# Response - Change Status Code -你可能之前已经了解到,你可以设置默认的[响应状态码](../tutorial/response-status-code.md){.internal-link target=_blank}。 +You probably read before that you can set a default [Response Status Code](../tutorial/response-status-code.md){.internal-link target=_blank}. -但在某些情况下,你需要返回一个不同于默认值的状态码。 +But in some cases you need to return a different status code than the default. -## 使用场景 +## Use case -例如,假设你想默认返回一个HTTP状态码为“OK”`200`。 +For example, imagine that you want to return an HTTP status code of "OK" `200` by default. -但如果数据不存在,你想创建它,并返回一个HTTP状态码为“CREATED”`201`。 +But if the data didn't exist, you want to create it, and return an HTTP status code of "CREATED" `201`. -但你仍然希望能够使用`response_model`过滤和转换你返回的数据。 +But you still want to be able to filter and convert the data you return with a `response_model`. -对于这些情况,你可以使用一个`Response`参数。 +For those cases, you can use a `Response` parameter. -## 使用 `Response` 参数 +## Use a `Response` parameter -你可以在你的*路径操作函数*中声明一个`Response`类型的参数(就像你可以为cookies和头部做的那样)。 +You can declare a parameter of type `Response` in your *path operation function* (as you can do for cookies and headers). -然后你可以在这个*临时*响应对象中设置`status_code`。 +And then you can set the `status_code` in that *temporal* response object. ```Python hl_lines="1 9 12" {!../../../docs_src/response_change_status_code/tutorial001.py!} ``` -然后你可以像平常一样返回任何你需要的对象(例如一个`dict`或者一个数据库模型)。如果你声明了一个`response_model`,它仍然会被用来过滤和转换你返回的对象。 +And then you can return any object you need, as you normally would (a `dict`, a database model, etc). -**FastAPI**将使用这个临时响应来提取状态码(也包括cookies和头部),并将它们放入包含你返回的值的最终响应中,该响应由任何`response_model`过滤。 +And if you declared a `response_model`, it will still be used to filter and convert the object you returned. -你也可以在依赖项中声明`Response`参数,并在其中设置状态码。但请注意,最后设置的状态码将会生效。 +**FastAPI** will use that *temporal* response to extract the status code (also cookies and headers), and will put them in the final response that contains the value you returned, filtered by any `response_model`. + +You can also declare the `Response` parameter in dependencies, and set the status code in them. But have in mind that the last one to be set will win. From af622ebd3d31a84075f70d5465b4287fc6206c2d Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:46 +0800 Subject: [PATCH 022/163] New translations response-cookies.md (Chinese Simplified) --- docs/zh/docs/advanced/response-cookies.md | 46 ++++++++++++----------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/docs/zh/docs/advanced/response-cookies.md b/docs/zh/docs/advanced/response-cookies.md index 3e53c53191a4c..328094c9b5f7a 100644 --- a/docs/zh/docs/advanced/response-cookies.md +++ b/docs/zh/docs/advanced/response-cookies.md @@ -1,47 +1,49 @@ -# 响应Cookies +# Response Cookies -## 使用 `Response` 参数 +## Use a `Response` parameter -你可以在 *路径函数* 中定义一个类型为 `Response`的参数,这样你就可以在这个临时响应对象中设置cookie了。 +You can declare a parameter of type `Response` in your *path operation function*. + +And then you can set cookies in that *temporal* response object. ```Python hl_lines="1 8-9" {!../../../docs_src/response_cookies/tutorial002.py!} ``` -而且你还可以根据你的需要响应不同的对象,比如常用的 `dict`,数据库model等。 +And then you can return any object you need, as you normally would (a `dict`, a database model, etc). -如果你定义了 `response_model`,程序会自动根据`response_model`来过滤和转换你响应的对象。 +And if you declared a `response_model`, it will still be used to filter and convert the object you returned. -**FastAPI** 会使用这个 *临时* 响应对象去装在这些cookies信息 (同样还有headers和状态码等信息), 最终会将这些信息和通过`response_model`转化过的数据合并到最终的响应里。 +**FastAPI** will use that *temporal* response to extract the cookies (also headers and status code), and will put them in the final response that contains the value you returned, filtered by any `response_model`. -你也可以在depend中定义`Response`参数,并设置cookie和header。 +You can also declare the `Response` parameter in dependencies, and set cookies (and headers) in them. -## 直接响应 `Response` +## Return a `Response` directly -你还可以在直接响应`Response`时直接创建cookies。 +You can also create cookies when returning a `Response` directly in your code. -你可以参考[Return a Response Directly](response-directly.md){.internal-link target=_blank}来创建response +To do that, you can create a response as described in [Return a Response Directly](response-directly.md){.internal-link target=_blank}. -然后设置Cookies,并返回: +Then set Cookies in it, and then return it: ```Python hl_lines="10-12" {!../../../docs_src/response_cookies/tutorial001.py!} ``` !!! tip - 需要注意,如果你直接反馈一个response对象,而不是使用`Response`入参,FastAPI则会直接反馈你封装的response对象。 - - 所以你需要确保你响应数据类型的正确性,如:你可以使用`JSONResponse`来兼容JSON的场景。 - - 同时,你也应当仅反馈通过`response_model`过滤过的数据。 + Have in mind that if you return a response directly instead of using the `Response` parameter, FastAPI will return it directly. -### 更多信息 + So, you will have to make sure your data is of the correct type. E.g. it is compatible with JSON, if you are returning a `JSONResponse`. + + And also that you are not sending any data that should have been filtered by a `response_model`. -!!! note "技术细节" - 你也可以使用`from starlette.responses import Response` 或者 `from starlette.responses import JSONResponse`。 +### More info - 为了方便开发者,**FastAPI** 封装了相同数据类型,如`starlette.responses` 和 `fastapi.responses`。不过大部分response对象都是直接引用自Starlette。 +!!! note "Technical Details" + You could also use `from starlette.responses import Response` or `from starlette.responses import JSONResponse`. - 因为`Response`对象可以非常便捷的设置headers和cookies,所以 **FastAPI** 同时也封装了`fastapi.Response`。 + **FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. + + And as the `Response` can be used frequently to set headers and cookies, **FastAPI** also provides it at `fastapi.Response`. -如果你想查看所有可用的参数和选项,可以参考 Starlette帮助文档 +To see all the available parameters and options, check the documentation in Starlette. From 751f7cf362db13f8a2d4d93b56e2ce5f30ffa971 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:47 +0800 Subject: [PATCH 023/163] New translations response-directly.md (Chinese Simplified) --- docs/zh/docs/advanced/response-directly.md | 61 +++++++++++----------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/docs/zh/docs/advanced/response-directly.md b/docs/zh/docs/advanced/response-directly.md index 797a878eb923b..8836140ecf7ee 100644 --- a/docs/zh/docs/advanced/response-directly.md +++ b/docs/zh/docs/advanced/response-directly.md @@ -1,64 +1,63 @@ -# 直接返回响应 +# Return a Response Directly -当你创建一个 **FastAPI** *路径操作* 时,你可以正常返回以下任意一种数据:`dict`,`list`,Pydantic 模型,数据库模型等等。 +When you create a **FastAPI** *path operation* you can normally return any data from it: a `dict`, a `list`, a Pydantic model, a database model, etc. -**FastAPI** 默认会使用 `jsonable_encoder` 将这些类型的返回值转换成 JSON 格式,`jsonable_encoder` 在 [JSON 兼容编码器](../tutorial/encoder.md){.internal-link target=_blank} 中有阐述。 +By default, **FastAPI** would automatically convert that return value to JSON using the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}. -然后,**FastAPI** 会在后台将这些兼容 JSON 的数据(比如字典)放到一个 `JSONResponse` 中,该 `JSONResponse` 会用来发送响应给客户端。 +Then, behind the scenes, it would put that JSON-compatible data (e.g. a `dict`) inside of a `JSONResponse` that would be used to send the response to the client. -但是你可以在你的 *路径操作* 中直接返回一个 `JSONResponse`。 +But you can return a `JSONResponse` directly from your *path operations*. -直接返回响应可能会有用处,比如返回自定义的响应头和 cookies。 +It might be useful, for example, to return custom headers or cookies. -## 返回 `Response` +## Return a `Response` -事实上,你可以返回任意 `Response` 或者任意 `Response` 的子类。 +In fact, you can return any `Response` or any sub-class of it. -!!! tip "小贴士" - `JSONResponse` 本身是一个 `Response` 的子类。 +!!! tip + `JSONResponse` itself is a sub-class of `Response`. -当你返回一个 `Response` 时,**FastAPI** 会直接传递它。 +And when you return a `Response`, **FastAPI** will pass it directly. -**FastAPI** 不会用 Pydantic 模型做任何数据转换,不会将响应内容转换成任何类型,等等。 +It won't do any data conversion with Pydantic models, it won't convert the contents to any type, etc. -这种特性给你极大的可扩展性。你可以返回任何数据类型,重写任何数据声明或者校验,等等。 +This gives you a lot of flexibility. You can return any data type, override any data declaration or validation, etc. -## 在 `Response` 中使用 `jsonable_encoder` +## Using the `jsonable_encoder` in a `Response` -由于 **FastAPI** 并未对你返回的 `Response` 做任何改变,你必须确保你已经准备好响应内容。 +Because **FastAPI** doesn't do any change to a `Response` you return, you have to make sure it's contents are ready for it. -例如,如果不首先将 Pydantic 模型转换为 `dict`,并将所有数据类型(如 `datetime`、`UUID` 等)转换为兼容 JSON 的类型,则不能将其放入JSONResponse中。 +For example, you cannot put a Pydantic model in a `JSONResponse` without first converting it to a `dict` with all the data types (like `datetime`, `UUID`, etc) converted to JSON-compatible types. -对于这些情况,在将数据传递给响应之前,你可以使用 `jsonable_encoder` 来转换你的数据。 +For those cases, you can use the `jsonable_encoder` to convert your data before passing it to a response: - -```Python hl_lines="4 6 20 21" +```Python hl_lines="6-7 21-22" {!../../../docs_src/response_directly/tutorial001.py!} ``` -!!! note "技术细节" - 你也可以使用 `from starlette.responses import JSONResponse`。 +!!! note "Technical Details" + You could also use `from starlette.responses import JSONResponse`. - 出于方便,**FastAPI** 会提供与 `starlette.responses` 相同的 `fastapi.responses` 给开发者。但是大多数可用的响应都直接来自 Starlette。 + **FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. -## 返回自定义 `Response` +## Returning a custom `Response` -上面的例子展示了需要的所有部分,但还不够实用,因为你本可以只是直接返回 `item`,而**FastAPI** 默认帮你把这个 `item` 放到 `JSONResponse` 中,又默认将其转换成了 `dict`等等。 +The example above shows all the parts you need, but it's not very useful yet, as you could have just returned the `item` directly, and **FastAPI** would put it in a `JSONResponse` for you, converting it to a `dict`, etc. All that by default. -现在,让我们看看你如何才能返回一个自定义的响应。 +Now, let's see how you could use that to return a custom response. -假设你想要返回一个 XML 响应。 +Let's say that you want to return an XML response. -你可以把你的 XML 内容放到一个字符串中,放到一个 `Response` 中,然后返回。 +You could put your XML content in a string, put it in a `Response`, and return it: ```Python hl_lines="1 18" {!../../../docs_src/response_directly/tutorial002.py!} ``` -## 说明 +## Notes -当你直接返回 `Response` 时,它的数据既没有校验,又不会进行转换(序列化),也不会自动生成文档。 +When you return a `Response` directly its data is not validated, converted (serialized), nor documented automatically. -但是你仍可以参考 [OpenApI 中的额外响应](additional-responses.md){.internal-link target=_blank} 给响应编写文档。 +But you can still document it as described in [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}. -在后续的章节中你可以了解到如何使用/声明这些自定义的 `Response` 的同时还保留自动化的数据转换和文档等。 +You can see in later sections how to use/declare these custom `Response`s while still having automatic data conversion, documentation, etc. From 1f9f7f9760c4d435c18221d348cdde815b4ab501 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:49 +0800 Subject: [PATCH 024/163] New translations response-headers.md (Chinese Simplified) --- docs/zh/docs/advanced/response-headers.md | 41 ++++++++++++----------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/docs/zh/docs/advanced/response-headers.md b/docs/zh/docs/advanced/response-headers.md index 85dab15ac092f..99c280024f59d 100644 --- a/docs/zh/docs/advanced/response-headers.md +++ b/docs/zh/docs/advanced/response-headers.md @@ -1,39 +1,42 @@ -# 响应头 +# Response Headers -## 使用 `Response` 参数 +## Use a `Response` parameter -你可以在你的*路径操作函数*中声明一个`Response`类型的参数(就像你可以为cookies做的那样)。 +You can declare a parameter of type `Response` in your *path operation function* (as you can do for cookies). + +And then you can set headers in that *temporal* response object. -然后你可以在这个*临时*响应对象中设置头部。 ```Python hl_lines="1 7-8" {!../../../docs_src/response_headers/tutorial002.py!} ``` -然后你可以像平常一样返回任何你需要的对象(例如一个`dict`或者一个数据库模型)。如果你声明了一个`response_model`,它仍然会被用来过滤和转换你返回的对象。 +And then you can return any object you need, as you normally would (a `dict`, a database model, etc). + +And if you declared a `response_model`, it will still be used to filter and convert the object you returned. + +**FastAPI** will use that *temporal* response to extract the headers (also cookies and status code), and will put them in the final response that contains the value you returned, filtered by any `response_model`. -**FastAPI**将使用这个临时响应来提取头部(也包括cookies和状态码),并将它们放入包含你返回的值的最终响应中,该响应由任何`response_model`过滤。 +You can also declare the `Response` parameter in dependencies, and set headers (and cookies) in them. -你也可以在依赖项中声明`Response`参数,并在其中设置头部(和cookies)。 +## Return a `Response` directly -## 直接返回 `Response` +You can also add headers when you return a `Response` directly. -你也可以在直接返回`Response`时添加头部。 +Create a response as described in [Return a Response Directly](response-directly.md){.internal-link target=_blank} and pass the headers as an additional parameter: -按照[直接返回响应](response-directly.md){.internal-link target=_blank}中所述创建响应,并将头部作为附加参数传递: ```Python hl_lines="10-12" {!../../../docs_src/response_headers/tutorial001.py!} ``` +!!! note "Technical Details" + You could also use `from starlette.responses import Response` or `from starlette.responses import JSONResponse`. -!!! 注意 "技术细节" - 你也可以使用`from starlette.responses import Response`或`from starlette.responses import JSONResponse`。 - - **FastAPI**提供了与`fastapi.responses`相同的`starlette.responses`,只是为了方便开发者。但是,大多数可用的响应都直接来自Starlette。 - - 由于`Response`经常用于设置头部和cookies,因此**FastAPI**还在`fastapi.Response`中提供了它。 + **FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. + + And as the `Response` can be used frequently to set headers and cookies, **FastAPI** also provides it at `fastapi.Response`. -## 自定义头部 +## Custom Headers -请注意,可以使用'X-'前缀添加自定义专有头部。 +Have in mind that custom proprietary headers can be added using the 'X-' prefix. -但是,如果你有自定义头部,你希望浏览器中的客户端能够看到它们,你需要将它们添加到你的CORS配置中(在[CORS(跨源资源共享)](../tutorial/cors.md){.internal-link target=_blank}中阅读更多),使用在Starlette的CORS文档中记录的`expose_headers`参数。 +But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your CORS configurations (read more in [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}), using the parameter `expose_headers` documented in Starlette's CORS docs. From 659550f9ae5495ce797a9042f713670936e14114 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:49 +0800 Subject: [PATCH 025/163] New translations http-basic-auth.md (Chinese Simplified) --- .../docs/advanced/security/http-basic-auth.md | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 docs/zh/docs/advanced/security/http-basic-auth.md diff --git a/docs/zh/docs/advanced/security/http-basic-auth.md b/docs/zh/docs/advanced/security/http-basic-auth.md new file mode 100644 index 0000000000000..c24dfc8c7e25d --- /dev/null +++ b/docs/zh/docs/advanced/security/http-basic-auth.md @@ -0,0 +1,164 @@ +# HTTP Basic Auth + +For the simplest cases, you can use HTTP Basic Auth. + +In HTTP Basic Auth, the application expects a header that contains a username and a password. + +If it doesn't receive it, it returns an HTTP 401 "Unauthorized" error. + +And returns a header `WWW-Authenticate` with a value of `Basic`, and an optional `realm` parameter. + +That tells the browser to show the integrated prompt for a username and password. + +Then, when you type that username and password, the browser sends them in the header automatically. + +## Simple HTTP Basic Auth + +* Import `HTTPBasic` and `HTTPBasicCredentials`. +* Create a "`security` scheme" using `HTTPBasic`. +* Use that `security` with a dependency in your *path operation*. +* It returns an object of type `HTTPBasicCredentials`: + * It contains the `username` and `password` sent. + +=== "Python 3.9+" + + ```Python hl_lines="4 8 12" + {!> ../../../docs_src/security/tutorial006_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="2 7 11" + {!> ../../../docs_src/security/tutorial006_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="2 6 10" + {!> ../../../docs_src/security/tutorial006.py!} + ``` + +When you try to open the URL for the first time (or click the "Execute" button in the docs) the browser will ask you for your username and password: + + + +## Check the username + +Here's a more complete example. + +Use a dependency to check if the username and password are correct. + +For this, use the Python standard module `secrets` to check the username and password. + +`secrets.compare_digest()` needs to take `bytes` or a `str` that only contains ASCII characters (the ones in English), this means it wouldn't work with characters like `á`, as in `Sebastián`. + +To handle that, we first convert the `username` and `password` to `bytes` encoding them with UTF-8. + +Then we can use `secrets.compare_digest()` to ensure that `credentials.username` is `"stanleyjobson"`, and that `credentials.password` is `"swordfish"`. + +=== "Python 3.9+" + + ```Python hl_lines="1 12-24" + {!> ../../../docs_src/security/tutorial007_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="1 12-24" + {!> ../../../docs_src/security/tutorial007_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="1 11-21" + {!> ../../../docs_src/security/tutorial007.py!} + ``` + +This would be similar to: + +```Python +if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"): + # Return some error + ... +``` + +But by using the `secrets.compare_digest()` it will be secure against a type of attacks called "timing attacks". + +### Timing Attacks + +But what's a "timing attack"? + +Let's imagine some attackers are trying to guess the username and password. + +And they send a request with a username `johndoe` and a password `love123`. + +Then the Python code in your application would be equivalent to something like: + +```Python +if "johndoe" == "stanleyjobson" and "love123" == "swordfish": + ... +``` + +But right at the moment Python compares the first `j` in `johndoe` to the first `s` in `stanleyjobson`, it will return `False`, because it already knows that those two strings are not the same, thinking that "there's no need to waste more computation comparing the rest of the letters". And your application will say "incorrect user or password". + +But then the attackers try with username `stanleyjobsox` and password `love123`. + +And your application code does something like: + +```Python +if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish": + ... +``` + +Python will have to compare the whole `stanleyjobso` in both `stanleyjobsox` and `stanleyjobson` before realizing that both strings are not the same. So it will take some extra microseconds to reply back "incorrect user or password". + +#### The time to answer helps the attackers + +At that point, by noticing that the server took some microseconds longer to send the "incorrect user or password" response, the attackers will know that they got _something_ right, some of the initial letters were right. + +And then they can try again knowing that it's probably something more similar to `stanleyjobsox` than to `johndoe`. + +#### A "professional" attack + +Of course, the attackers would not try all this by hand, they would write a program to do it, possibly with thousands or millions of tests per second. And would get just one extra correct letter at a time. + +But doing that, in some minutes or hours the attackers would have guessed the correct username and password, with the "help" of our application, just using the time taken to answer. + +#### Fix it with `secrets.compare_digest()` + +But in our code we are actually using `secrets.compare_digest()`. + +In short, it will take the same time to compare `stanleyjobsox` to `stanleyjobson` than it takes to compare `johndoe` to `stanleyjobson`. And the same for the password. + +That way, using `secrets.compare_digest()` in your application code, it will be safe against this whole range of security attacks. + +### Return the error + +After detecting that the credentials are incorrect, return an `HTTPException` with a status code 401 (the same returned when no credentials are provided) and add the header `WWW-Authenticate` to make the browser show the login prompt again: + +=== "Python 3.9+" + + ```Python hl_lines="26-30" + {!> ../../../docs_src/security/tutorial007_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="26-30" + {!> ../../../docs_src/security/tutorial007_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="23-27" + {!> ../../../docs_src/security/tutorial007.py!} + ``` From 17b5f9b84b7cf112d6974ff9112872a5b7f3882c Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:50 +0800 Subject: [PATCH 026/163] New translations index.md (Chinese Simplified) --- docs/zh/docs/advanced/security/index.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/zh/docs/advanced/security/index.md b/docs/zh/docs/advanced/security/index.md index fdc8075c765ba..c18baf64b0d27 100644 --- a/docs/zh/docs/advanced/security/index.md +++ b/docs/zh/docs/advanced/security/index.md @@ -1,16 +1,16 @@ -# 高级安全 +# Advanced Security -## 附加特性 +## Additional Features -除 [教程 - 用户指南: 安全性](../../tutorial/security/){.internal-link target=_blank} 中涵盖的功能之外,还有一些额外的功能来处理安全性. +There are some extra features to handle security apart from the ones covered in the [Tutorial - User Guide: Security](../../tutorial/security/){.internal-link target=_blank}. -!!! tip "小贴士" - 接下来的章节 **并不一定是 "高级的"**. +!!! tip + The next sections are **not necessarily "advanced"**. - 而且对于你的使用场景来说,解决方案很可能就在其中。 + And it's possible that for your use case, the solution is in one of them. -## 先阅读教程 +## Read the Tutorial first -接下来的部分假设你已经阅读了主要的 [教程 - 用户指南: 安全性](../../tutorial/security/){.internal-link target=_blank}. +The next sections assume you already read the main [Tutorial - User Guide: Security](../../tutorial/security/){.internal-link target=_blank}. -它们都基于相同的概念,但支持一些额外的功能. +They are all based on the same concepts, but allow some extra functionalities. From 5076bd2a2257dfc1dbc9e628900408825f55debb Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:51 +0800 Subject: [PATCH 027/163] New translations oauth2-scopes.md (Chinese Simplified) --- .../docs/advanced/security/oauth2-scopes.md | 598 ++++++++++++++++++ 1 file changed, 598 insertions(+) create mode 100644 docs/zh/docs/advanced/security/oauth2-scopes.md diff --git a/docs/zh/docs/advanced/security/oauth2-scopes.md b/docs/zh/docs/advanced/security/oauth2-scopes.md new file mode 100644 index 0000000000000..3f230e9e74627 --- /dev/null +++ b/docs/zh/docs/advanced/security/oauth2-scopes.md @@ -0,0 +1,598 @@ +# OAuth2 scopes + +You can use OAuth2 scopes directly with **FastAPI**, they are integrated to work seamlessly. + +This would allow you to have a more fine-grained permission system, following the OAuth2 standard, integrated into your OpenAPI application (and the API docs). + +OAuth2 with scopes is the mechanism used by many big authentication providers, like Facebook, Google, GitHub, Microsoft, Twitter, etc. They use it to provide specific permissions to users and applications. + +Every time you "log in with" Facebook, Google, GitHub, Microsoft, Twitter, that application is using OAuth2 with scopes. + +In this section you will see how to manage authentication and authorization with the same OAuth2 with scopes in your **FastAPI** application. + +!!! warning + This is a more or less advanced section. If you are just starting, you can skip it. + + You don't necessarily need OAuth2 scopes, and you can handle authentication and authorization however you want. + + But OAuth2 with scopes can be nicely integrated into your API (with OpenAPI) and your API docs. + + Nevertheless, you still enforce those scopes, or any other security/authorization requirement, however you need, in your code. + + In many cases, OAuth2 with scopes can be an overkill. + + But if you know you need it, or you are curious, keep reading. + +## OAuth2 scopes and OpenAPI + +The OAuth2 specification defines "scopes" as a list of strings separated by spaces. + +The content of each of these strings can have any format, but should not contain spaces. + +These scopes represent "permissions". + +In OpenAPI (e.g. the API docs), you can define "security schemes". + +When one of these security schemes uses OAuth2, you can also declare and use scopes. + +Each "scope" is just a string (without spaces). + +They are normally used to declare specific security permissions, for example: + +* `users:read` or `users:write` are common examples. +* `instagram_basic` is used by Facebook / Instagram. +* `https://www.googleapis.com/auth/drive` is used by Google. + +!!! info + In OAuth2 a "scope" is just a string that declares a specific permission required. + + It doesn't matter if it has other characters like `:` or if it is a URL. + + Those details are implementation specific. + + For OAuth2 they are just strings. + +## Global view + +First, let's quickly see the parts that change from the examples in the main **Tutorial - User Guide** for [OAuth2 with Password (and hashing), Bearer with JWT tokens](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. Now using OAuth2 scopes: + +=== "Python 3.10+" + + ```Python hl_lines="4 8 12 46 64 105 107-115 121-124 128-134 139 155" + {!> ../../../docs_src/security/tutorial005_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="2 4 8 12 46 64 105 107-115 121-124 128-134 139 155" + {!> ../../../docs_src/security/tutorial005_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="2 4 8 12 47 65 106 108-116 122-125 129-135 140 156" + {!> ../../../docs_src/security/tutorial005_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="3 7 11 45 63 104 106-114 120-123 127-133 138 152" + {!> ../../../docs_src/security/tutorial005_py310.py!} + ``` + +=== "Python 3.9+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="2 4 8 12 46 64 105 107-115 121-124 128-134 139 153" + {!> ../../../docs_src/security/tutorial005_py39.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="2 4 8 12 46 64 105 107-115 121-124 128-134 139 153" + {!> ../../../docs_src/security/tutorial005.py!} + ``` + +Now let's review those changes step by step. + +## OAuth2 Security scheme + +The first change is that now we are declaring the OAuth2 security scheme with two available scopes, `me` and `items`. + +The `scopes` parameter receives a `dict` with each scope as a key and the description as the value: + +=== "Python 3.10+" + + ```Python hl_lines="62-65" + {!> ../../../docs_src/security/tutorial005_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="62-65" + {!> ../../../docs_src/security/tutorial005_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="63-66" + {!> ../../../docs_src/security/tutorial005_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="61-64" + {!> ../../../docs_src/security/tutorial005_py310.py!} + ``` + + +=== "Python 3.9+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="62-65" + {!> ../../../docs_src/security/tutorial005_py39.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="62-65" + {!> ../../../docs_src/security/tutorial005.py!} + ``` + +Because we are now declaring those scopes, they will show up in the API docs when you log-in/authorize. + +And you will be able to select which scopes you want to give access to: `me` and `items`. + +This is the same mechanism used when you give permissions while logging in with Facebook, Google, GitHub, etc: + + + +## JWT token with scopes + +Now, modify the token *path operation* to return the scopes requested. + +We are still using the same `OAuth2PasswordRequestForm`. It includes a property `scopes` with a `list` of `str`, with each scope it received in the request. + +And we return the scopes as part of the JWT token. + +!!! danger + For simplicity, here we are just adding the scopes received directly to the token. + + But in your application, for security, you should make sure you only add the scopes that the user is actually able to have, or the ones you have predefined. + +=== "Python 3.10+" + + ```Python hl_lines="155" + {!> ../../../docs_src/security/tutorial005_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="155" + {!> ../../../docs_src/security/tutorial005_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="156" + {!> ../../../docs_src/security/tutorial005_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="152" + {!> ../../../docs_src/security/tutorial005_py310.py!} + ``` + +=== "Python 3.9+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="153" + {!> ../../../docs_src/security/tutorial005_py39.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="153" + {!> ../../../docs_src/security/tutorial005.py!} + ``` + +## Declare scopes in *path operations* and dependencies + +Now we declare that the *path operation* for `/users/me/items/` requires the scope `items`. + +For this, we import and use `Security` from `fastapi`. + +You can use `Security` to declare dependencies (just like `Depends`), but `Security` also receives a parameter `scopes` with a list of scopes (strings). + +In this case, we pass a dependency function `get_current_active_user` to `Security` (the same way we would do with `Depends`). + +But we also pass a `list` of scopes, in this case with just one scope: `items` (it could have more). + +And the dependency function `get_current_active_user` can also declare sub-dependencies, not only with `Depends` but also with `Security`. Declaring its own sub-dependency function (`get_current_user`), and more scope requirements. + +In this case, it requires the scope `me` (it could require more than one scope). + +!!! note + You don't necessarily need to add different scopes in different places. + + We are doing it here to demonstrate how **FastAPI** handles scopes declared at different levels. + +=== "Python 3.10+" + + ```Python hl_lines="4 139 170" + {!> ../../../docs_src/security/tutorial005_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="4 139 170" + {!> ../../../docs_src/security/tutorial005_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="4 140 171" + {!> ../../../docs_src/security/tutorial005_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="3 138 165" + {!> ../../../docs_src/security/tutorial005_py310.py!} + ``` + +=== "Python 3.9+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="4 139 166" + {!> ../../../docs_src/security/tutorial005_py39.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="4 139 166" + {!> ../../../docs_src/security/tutorial005.py!} + ``` + +!!! info "Technical Details" + `Security` is actually a subclass of `Depends`, and it has just one extra parameter that we'll see later. + + But by using `Security` instead of `Depends`, **FastAPI** will know that it can declare security scopes, use them internally, and document the API with OpenAPI. + + But when you import `Query`, `Path`, `Depends`, `Security` and others from `fastapi`, those are actually functions that return special classes. + +## Use `SecurityScopes` + +Now update the dependency `get_current_user`. + +This is the one used by the dependencies above. + +Here's were we are using the same OAuth2 scheme we created before, declaring it as a dependency: `oauth2_scheme`. + +Because this dependency function doesn't have any scope requirements itself, we can use `Depends` with `oauth2_scheme`, we don't have to use `Security` when we don't need to specify security scopes. + +We also declare a special parameter of type `SecurityScopes`, imported from `fastapi.security`. + +This `SecurityScopes` class is similar to `Request` (`Request` was used to get the request object directly). + +=== "Python 3.10+" + + ```Python hl_lines="8 105" + {!> ../../../docs_src/security/tutorial005_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="8 105" + {!> ../../../docs_src/security/tutorial005_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="8 106" + {!> ../../../docs_src/security/tutorial005_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7 104" + {!> ../../../docs_src/security/tutorial005_py310.py!} + ``` + +=== "Python 3.9+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="8 105" + {!> ../../../docs_src/security/tutorial005_py39.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="8 105" + {!> ../../../docs_src/security/tutorial005.py!} + ``` + +## Use the `scopes` + +The parameter `security_scopes` will be of type `SecurityScopes`. + +It will have a property `scopes` with a list containing all the scopes required by itself and all the dependencies that use this as a sub-dependency. That means, all the "dependants"... this might sound confusing, it is explained again later below. + +The `security_scopes` object (of class `SecurityScopes`) also provides a `scope_str` attribute with a single string, containing those scopes separated by spaces (we are going to use it). + +We create an `HTTPException` that we can re-use (`raise`) later at several points. + +In this exception, we include the scopes required (if any) as a string separated by spaces (using `scope_str`). We put that string containing the scopes in the `WWW-Authenticate` header (this is part of the spec). + +=== "Python 3.10+" + + ```Python hl_lines="105 107-115" + {!> ../../../docs_src/security/tutorial005_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="105 107-115" + {!> ../../../docs_src/security/tutorial005_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="106 108-116" + {!> ../../../docs_src/security/tutorial005_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="104 106-114" + {!> ../../../docs_src/security/tutorial005_py310.py!} + ``` + +=== "Python 3.9+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="105 107-115" + {!> ../../../docs_src/security/tutorial005_py39.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="105 107-115" + {!> ../../../docs_src/security/tutorial005.py!} + ``` + +## Verify the `username` and data shape + +We verify that we get a `username`, and extract the scopes. + +And then we validate that data with the Pydantic model (catching the `ValidationError` exception), and if we get an error reading the JWT token or validating the data with Pydantic, we raise the `HTTPException` we created before. + +For that, we update the Pydantic model `TokenData` with a new property `scopes`. + +By validating the data with Pydantic we can make sure that we have, for example, exactly a `list` of `str` with the scopes and a `str` with the `username`. + +Instead of, for example, a `dict`, or something else, as it could break the application at some point later, making it a security risk. + +We also verify that we have a user with that username, and if not, we raise that same exception we created before. + +=== "Python 3.10+" + + ```Python hl_lines="46 116-127" + {!> ../../../docs_src/security/tutorial005_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="46 116-127" + {!> ../../../docs_src/security/tutorial005_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="47 117-128" + {!> ../../../docs_src/security/tutorial005_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="45 115-126" + {!> ../../../docs_src/security/tutorial005_py310.py!} + ``` + +=== "Python 3.9+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="46 116-127" + {!> ../../../docs_src/security/tutorial005_py39.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="46 116-127" + {!> ../../../docs_src/security/tutorial005.py!} + ``` + +## Verify the `scopes` + +We now verify that all the scopes required, by this dependency and all the dependants (including *path operations*), are included in the scopes provided in the token received, otherwise raise an `HTTPException`. + +For this, we use `security_scopes.scopes`, that contains a `list` with all these scopes as `str`. + +=== "Python 3.10+" + + ```Python hl_lines="128-134" + {!> ../../../docs_src/security/tutorial005_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="128-134" + {!> ../../../docs_src/security/tutorial005_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="129-135" + {!> ../../../docs_src/security/tutorial005_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="127-133" + {!> ../../../docs_src/security/tutorial005_py310.py!} + ``` + +=== "Python 3.9+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="128-134" + {!> ../../../docs_src/security/tutorial005_py39.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="128-134" + {!> ../../../docs_src/security/tutorial005.py!} + ``` + +## Dependency tree and scopes + +Let's review again this dependency tree and the scopes. + +As the `get_current_active_user` dependency has as a sub-dependency on `get_current_user`, the scope `"me"` declared at `get_current_active_user` will be included in the list of required scopes in the `security_scopes.scopes` passed to `get_current_user`. + +The *path operation* itself also declares a scope, `"items"`, so this will also be in the list of `security_scopes.scopes` passed to `get_current_user`. + +Here's how the hierarchy of dependencies and scopes looks like: + +* The *path operation* `read_own_items` has: + * Required scopes `["items"]` with the dependency: + * `get_current_active_user`: + * The dependency function `get_current_active_user` has: + * Required scopes `["me"]` with the dependency: + * `get_current_user`: + * The dependency function `get_current_user` has: + * No scopes required by itself. + * A dependency using `oauth2_scheme`. + * A `security_scopes` parameter of type `SecurityScopes`: + * This `security_scopes` parameter has a property `scopes` with a `list` containing all these scopes declared above, so: + * `security_scopes.scopes` will contain `["me", "items"]` for the *path operation* `read_own_items`. + * `security_scopes.scopes` will contain `["me"]` for the *path operation* `read_users_me`, because it is declared in the dependency `get_current_active_user`. + * `security_scopes.scopes` will contain `[]` (nothing) for the *path operation* `read_system_status`, because it didn't declare any `Security` with `scopes`, and its dependency, `get_current_user`, doesn't declare any `scope` either. + +!!! tip + The important and "magic" thing here is that `get_current_user` will have a different list of `scopes` to check for each *path operation*. + + All depending on the `scopes` declared in each *path operation* and each dependency in the dependency tree for that specific *path operation*. + +## More details about `SecurityScopes` + +You can use `SecurityScopes` at any point, and in multiple places, it doesn't have to be at the "root" dependency. + +It will always have the security scopes declared in the current `Security` dependencies and all the dependants for **that specific** *path operation* and **that specific** dependency tree. + +Because the `SecurityScopes` will have all the scopes declared by dependants, you can use it to verify that a token has the required scopes in a central dependency function, and then declare different scope requirements in different *path operations*. + +They will be checked independently for each *path operation*. + +## Check it + +If you open the API docs, you can authenticate and specify which scopes you want to authorize. + + + +If you don't select any scope, you will be "authenticated", but when you try to access `/users/me/` or `/users/me/items/` you will get an error saying that you don't have enough permissions. You will still be able to access `/status/`. + +And if you select the scope `me` but not the scope `items`, you will be able to access `/users/me/` but not `/users/me/items/`. + +That's what would happen to a third party application that tried to access one of these *path operations* with a token provided by a user, depending on how many permissions the user gave the application. + +## About third party integrations + +In this example we are using the OAuth2 "password" flow. + +This is appropriate when we are logging in to our own application, probably with our own frontend. + +Because we can trust it to receive the `username` and `password`, as we control it. + +But if you are building an OAuth2 application that others would connect to (i.e., if you are building an authentication provider equivalent to Facebook, Google, GitHub, etc.) you should use one of the other flows. + +The most common is the implicit flow. + +The most secure is the code flow, but is more complex to implement as it requires more steps. As it is more complex, many providers end up suggesting the implicit flow. + +!!! note + It's common that each authentication provider names their flows in a different way, to make it part of their brand. + + But in the end, they are implementing the same OAuth2 standard. + +**FastAPI** includes utilities for all these OAuth2 authentication flows in `fastapi.security.oauth2`. + +## `Security` in decorator `dependencies` + +The same way you can define a `list` of `Depends` in the decorator's `dependencies` parameter (as explained in [Dependencies in path operation decorators](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}), you could also use `Security` with `scopes` there. From 6fe3493caf80e4510a89993840c270fdd4c532d0 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:52 +0800 Subject: [PATCH 028/163] New translations settings.md (Chinese Simplified) --- docs/zh/docs/advanced/settings.md | 314 ++++++++++++++++++------------ 1 file changed, 185 insertions(+), 129 deletions(-) diff --git a/docs/zh/docs/advanced/settings.md b/docs/zh/docs/advanced/settings.md index 597e99a7793c3..a4a773b1fdeaf 100644 --- a/docs/zh/docs/advanced/settings.md +++ b/docs/zh/docs/advanced/settings.md @@ -1,34 +1,35 @@ -# 设置和环境变量 +# Settings and Environment Variables -在许多情况下,您的应用程序可能需要一些外部设置或配置,例如密钥、数据库凭据、电子邮件服务的凭据等等。 +In many cases your application could need some external settings or configurations, for example secret keys, database credentials, credentials for email services, etc. -这些设置中的大多数是可变的(可以更改的),比如数据库的 URL。而且许多设置可能是敏感的,比如密钥。 +Most of these settings are variable (can change), like database URLs. And many could be sensitive, like secrets. -因此,通常会将它们提供为由应用程序读取的环境变量。 +For this reason it's common to provide them in environment variables that are read by the application. -## 环境变量 +## Environment Variables !!! tip - 如果您已经知道什么是"环境变量"以及如何使用它们,请随意跳到下面的下一节。 + If you already know what "environment variables" are and how to use them, feel free to skip to the next section below. -环境变量(也称为"env var")是一种存在于 Python 代码之外、存在于操作系统中的变量,可以被您的 Python 代码(或其他程序)读取。 +An environment variable (also known as "env var") is a variable that lives outside of the Python code, in the operating system, and could be read by your Python code (or by other programs as well). -您可以在 shell 中创建和使用环境变量,而无需使用 Python: +You can create and use environment variables in the shell, without needing Python: -=== "Linux、macOS、Windows Bash" +=== "Linux, macOS, Windows Bash"
```console - // 您可以创建一个名为 MY_NAME 的环境变量 + // You could create an env var MY_NAME with $ export MY_NAME="Wade Wilson" - // 然后您可以与其他程序一起使用它,例如 + // Then you could use it with other programs, like $ echo "Hello $MY_NAME" Hello Wade Wilson ``` +
=== "Windows PowerShell" @@ -36,22 +37,23 @@
```console - // 创建一个名为 MY_NAME 的环境变量 + // Create an env var MY_NAME $ $Env:MY_NAME = "Wade Wilson" - // 与其他程序一起使用它,例如 + // Use it with other programs, like $ echo "Hello $Env:MY_NAME" Hello Wade Wilson ``` +
-### 在 Python 中读取环境变量 +### Read env vars in Python -您还可以在 Python 之外的地方(例如终端中或使用任何其他方法)创建环境变量,然后在 Python 中读取它们。 +You could also create environment variables outside of Python, in the terminal (or with any other method), and then read them in Python. -例如,您可以有一个名为 `main.py` 的文件,其中包含以下内容: +For example you could have a file `main.py` with: ```Python hl_lines="3" import os @@ -61,54 +63,52 @@ print(f"Hello {name} from Python") ``` !!! tip - `os.getenv()` 的第二个参数是要返回的默认值。 + The second argument to `os.getenv()` is the default value to return. - 如果没有提供默认值,默认为 `None`,此处我们提供了 `"World"` 作为要使用的默认值。 + If not provided, it's `None` by default, here we provide `"World"` as the default value to use. -然后,您可以调用该 Python 程序: +Then you could call that Python program:
```console -// 这里我们还没有设置环境变量 +// Here we don't set the env var yet $ python main.py -// 因为我们没有设置环境变量,所以我们得到默认值 +// As we didn't set the env var, we get the default value Hello World from Python -// 但是如果我们先创建一个环境变量 +// But if we create an environment variable first $ export MY_NAME="Wade Wilson" -// 然后再次调用程序 +// And then call the program again $ python main.py -// 现在它可以读取环境变量 +// Now it can read the environment variable Hello Wade Wilson from Python ```
-由于环境变量可以在代码之外设置,但可以由代码读取,并且不需要与其他文件一起存储(提交到 `git`),因此通常将它们用于配置或设置。 - +As environment variables can be set outside of the code, but can be read by the code, and don't have to be stored (committed to `git`) with the rest of the files, it's common to use them for configurations or settings. +You can also create an environment variable only for a specific program invocation, that is only available to that program, and only for its duration. -您还可以仅为特定程序调用创建一个环境变量,该环境变量仅对该程序可用,并且仅在其运行期间有效。 - -要做到这一点,在程序本身之前的同一行创建它: +To do that, create it right before the program itself, on the same line:
```console -// 在此程序调用行中创建一个名为 MY_NAME 的环境变量 +// Create an env var MY_NAME in line for this program call $ MY_NAME="Wade Wilson" python main.py -// 现在它可以读取环境变量 +// Now it can read the environment variable Hello Wade Wilson from Python -// 之后环境变量不再存在 +// The env var no longer exists afterwards $ python main.py Hello World from Python @@ -117,53 +117,91 @@ Hello World from Python
!!! tip - 您可以在 Twelve-Factor App: Config 中阅读更多相关信息。 + You can read more about it at The Twelve-Factor App: Config. -### 类型和验证 +### Types and validation -这些环境变量只能处理文本字符串,因为它们是外部于 Python 的,并且必须与其他程序和整个系统兼容(甚至与不同的操作系统,如 Linux、Windows、macOS)。 +These environment variables can only handle text strings, as they are external to Python and have to be compatible with other programs and the rest of the system (and even with different operating systems, as Linux, Windows, macOS). -这意味着从环境变量中在 Python 中读取的任何值都将是 `str` 类型,任何类型的转换或验证都必须在代码中完成。 +That means that any value read in Python from an environment variable will be a `str`, and any conversion to a different type or validation has to be done in code. -## Pydantic 的 `Settings` +## Pydantic `Settings` -幸运的是,Pydantic 提供了一个很好的工具来处理来自环境变量的设置,即Pydantic: Settings management。 +Fortunately, Pydantic provides a great utility to handle these settings coming from environment variables with Pydantic: Settings management. -### 创建 `Settings` 对象 +### Install `pydantic-settings` -从 Pydantic 导入 `BaseSettings` 并创建一个子类,与 Pydantic 模型非常相似。 +First, install the `pydantic-settings` package: -与 Pydantic 模型一样,您使用类型注释声明类属性,还可以指定默认值。 +
-您可以使用与 Pydantic 模型相同的验证功能和工具,比如不同的数据类型和使用 `Field()` 进行附加验证。 +```console +$ pip install pydantic-settings +---> 100% +``` -```Python hl_lines="2 5-8 11" -{!../../../docs_src/settings/tutorial001.py!} +
+ +It also comes included when you install the `all` extras with: + +
+ +```console +$ pip install "fastapi[all]" +---> 100% ``` +
+ +!!! info + In Pydantic v1 it came included with the main package. Now it is distributed as this independent package so that you can choose to install it or not if you don't need that functionality. + +### Create the `Settings` object + +Import `BaseSettings` from Pydantic and create a sub-class, very much like with a Pydantic model. + +The same way as with Pydantic models, you declare class attributes with type annotations, and possibly default values. + +You can use all the same validation features and tools you use for Pydantic models, like different data types and additional validations with `Field()`. + +=== "Pydantic v2" + + ```Python hl_lines="2 5-8 11" + {!> ../../../docs_src/settings/tutorial001.py!} + ``` + +=== "Pydantic v1" + + !!! info + In Pydantic v1 you would import `BaseSettings` directly from `pydantic` instead of from `pydantic_settings`. + + ```Python hl_lines="2 5-8 11" + {!> ../../../docs_src/settings/tutorial001_pv1.py!} + ``` + !!! tip - 如果您需要一个快速的复制粘贴示例,请不要使用此示例,而应使用下面的最后一个示例。 + If you want something quick to copy and paste, don't use this example, use the last one below. -然后,当您创建该 `Settings` 类的实例(在此示例中是 `settings` 对象)时,Pydantic 将以不区分大小写的方式读取环境变量,因此,大写的变量 `APP_NAME` 仍将为属性 `app_name` 读取。 +Then, when you create an instance of that `Settings` class (in this case, in the `settings` object), Pydantic will read the environment variables in a case-insensitive way, so, an upper-case variable `APP_NAME` will still be read for the attribute `app_name`. -然后,它将转换和验证数据。因此,当您使用该 `settings` 对象时,您将获得您声明的类型的数据(例如 `items_per_user` 将为 `int` 类型)。 +Next it will convert and validate the data. So, when you use that `settings` object, you will have data of the types you declared (e.g. `items_per_user` will be an `int`). -### 使用 `settings` +### Use the `settings` -然后,您可以在应用程序中使用新的 `settings` 对象: +Then you can use the new `settings` object in your application: ```Python hl_lines="18-20" {!../../../docs_src/settings/tutorial001.py!} ``` -### 运行服务器 +### Run the server -接下来,您将运行服务器,并将配置作为环境变量传递。例如,您可以设置一个 `ADMIN_EMAIL` 和 `APP_NAME`,如下所示: +Next, you would run the server passing the configurations as environment variables, for example you could set an `ADMIN_EMAIL` and `APP_NAME` with:
```console -$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp"uvicorn main:app +$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" uvicorn main:app INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ``` @@ -171,51 +209,52 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp"uvicorn main:app
!!! tip - 要为单个命令设置多个环境变量,只需用空格分隔它们,并将它们全部放在命令之前。 + To set multiple env vars for a single command just separate them with a space, and put them all before the command. -然后,`admin_email` 设置将为 `"deadpool@example.com"`。 +And then the `admin_email` setting would be set to `"deadpool@example.com"`. -`app_name` 将为 `"ChimichangApp"`。 +The `app_name` would be `"ChimichangApp"`. -而 `items_per_user` 将保持其默认值为 `50`。 +And the `items_per_user` would keep its default value of `50`. -## 在另一个模块中设置 +## Settings in another module -您可以将这些设置放在另一个模块文件中,就像您在[Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}中所见的那样。 +You could put those settings in another module file as you saw in [Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}. -例如,您可以创建一个名为 `config.py` 的文件,其中包含以下内容: +For example, you could have a file `config.py` with: ```Python {!../../../docs_src/settings/app01/config.py!} ``` -然后在一个名为 `main.py` 的文件中使用它: +And then use it in a file `main.py`: ```Python hl_lines="3 11-13" {!../../../docs_src/settings/app01/main.py!} ``` + !!! tip - 您还需要一个名为 `__init__.py` 的文件,就像您在[Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}中看到的那样。 + You would also need a file `__init__.py` as you saw on [Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}. -## 在依赖项中使用设置 +## Settings in a dependency -在某些情况下,从依赖项中提供设置可能比在所有地方都使用全局对象 `settings` 更有用。 +In some occasions it might be useful to provide the settings from a dependency, instead of having a global object with `settings` that is used everywhere. -这在测试期间尤其有用,因为很容易用自定义设置覆盖依赖项。 +This could be especially useful during testing, as it's very easy to override a dependency with your own custom settings. -### 配置文件 +### The config file -根据前面的示例,您的 `config.py` 文件可能如下所示: +Coming from the previous example, your `config.py` file could look like: ```Python hl_lines="10" {!../../../docs_src/settings/app02/config.py!} ``` -请注意,现在我们不创建默认实例 `settings = Settings()`。 +Notice that now we don't create a default instance `settings = Settings()`. -### 主应用程序文件 +### The main app file -现在我们创建一个依赖项,返回一个新的 `config.Settings()`。 +Now we create a dependency that returns a new `config.Settings()`. === "Python 3.9+" @@ -229,21 +268,21 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp"uvicorn main:app {!> ../../../docs_src/settings/app02_an/main.py!} ``` -=== "Python 3.6+ 非注解版本" +=== "Python 3.6+ non-Annotated" !!! tip - 如果可能,请尽量使用 `Annotated` 版本。 + Prefer to use the `Annotated` version if possible. ```Python hl_lines="5 11-12" {!> ../../../docs_src/settings/app02/main.py!} ``` !!! tip - 我们稍后会讨论 `@lru_cache()`。 + We'll discuss the `@lru_cache()` in a bit. - 目前,您可以将 `get_settings()` 视为普通函数。 + For now you can assume `get_settings()` is a normal function. -然后,我们可以将其作为依赖项从“路径操作函数”中引入,并在需要时使用它。 +And then we can require it from the *path operation function* as a dependency and use it anywhere we need it. === "Python 3.9+" @@ -257,87 +296,103 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp"uvicorn main:app {!> ../../../docs_src/settings/app02_an/main.py!} ``` -=== "Python 3.6+ 非注解版本" +=== "Python 3.6+ non-Annotated" !!! tip - 如果可能,请尽量使用 `Annotated` 版本。 + Prefer to use the `Annotated` version if possible. ```Python hl_lines="16 18-20" {!> ../../../docs_src/settings/app02/main.py!} ``` -### 设置和测试 +### Settings and testing -然后,在测试期间,通过创建 `get_settings` 的依赖项覆盖,很容易提供一个不同的设置对象: +Then it would be very easy to provide a different settings object during testing by creating a dependency override for `get_settings`: ```Python hl_lines="9-10 13 21" {!../../../docs_src/settings/app02/test_main.py!} ``` -在依赖项覆盖中,我们在创建新的 `Settings` 对象时为 `admin_email` 设置了一个新值,然后返回该新对象。 +In the dependency override we set a new value for the `admin_email` when creating the new `Settings` object, and then we return that new object. -然后,我们可以测试它是否被使用。 +Then we can test that it is used. -## 从 `.env` 文件中读取设置 +## Reading a `.env` file -如果您有许多可能经常更改的设置,可能在不同的环境中,将它们放在一个文件中,然后从该文件中读取它们,就像它们是环境变量一样,可能非常有用。 +If you have many settings that possibly change a lot, maybe in different environments, it might be useful to put them on a file and then read them from it as if they were environment variables. -这种做法相当常见,有一个名称,这些环境变量通常放在一个名为 `.env` 的文件中,该文件被称为“dotenv”。 +This practice is common enough that it has a name, these environment variables are commonly placed in a file `.env`, and the file is called a "dotenv". !!! tip - 以点 (`.`) 开头的文件是 Unix-like 系统(如 Linux 和 macOS)中的隐藏文件。 + A file starting with a dot (`.`) is a hidden file in Unix-like systems, like Linux and macOS. - 但是,dotenv 文件实际上不一定要具有确切的文件名。 + But a dotenv file doesn't really have to have that exact filename. -Pydantic 支持使用外部库从这些类型的文件中读取。您可以在Pydantic 设置: Dotenv (.env) 支持中阅读更多相关信息。 +Pydantic has support for reading from these types of files using an external library. You can read more at Pydantic Settings: Dotenv (.env) support. !!! tip - 要使其工作,您需要执行 `pip install python-dotenv`。 + For this to work, you need to `pip install python-dotenv`. -### `.env` 文件 +### The `.env` file -您可以使用以下内容创建一个名为 `.env` 的文件: +You could have a `.env` file with: ```bash ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" ``` -### 从 `.env` 文件中读取设置 +### Read settings from `.env` -然后,您可以使用以下方式更新您的 `config.py`: +And then update your `config.py` with: -```Python hl_lines="9-10" -{!../../../docs_src/settings/app03/config.py!} -``` +=== "Pydantic v2" -在这里,我们在 Pydantic 的 `Settings` 类中创建了一个名为 `Config` 的类,并将 `env_file` 设置为我们想要使用的 dotenv 文件的文件名。 + ```Python hl_lines="9" + {!> ../../../docs_src/settings/app03_an/config.py!} + ``` -!!! tip - `Config` 类仅用于 Pydantic 配置。您可以在Pydantic Model Config中阅读更多相关信息。 -### 使用 `lru_cache` 仅创建一次 `Settings` + !!! tip + The `model_config` attribute is used just for Pydantic configuration. You can read more at Pydantic Model Config. -从磁盘中读取文件通常是一项耗时的(慢)操作,因此您可能希望仅在首次读取后并重复使用相同的设置对象,而不是为每个请求都读取它。 +=== "Pydantic v1" -但是,每次执行以下操作: + ```Python hl_lines="9-10" + {!> ../../../docs_src/settings/app03_an/config_pv1.py!} + ``` + + + !!! tip + The `Config` class is used just for Pydantic configuration. You can read more at Pydantic Model Config. + +!!! info + In Pydantic version 1 the configuration was done in an internal class `Config`, in Pydantic version 2 it's done in an attribute `model_config`. This attribute takes a `dict`, and to get autocompletion and inline errors you can import and use `SettingsConfigDict` to define that `dict`. + +Here we define the config `env_file` inside of your Pydantic `Settings` class, and set the value to the filename with the dotenv file we want to use. + +### Creating the `Settings` only once with `lru_cache` + +Reading a file from disk is normally a costly (slow) operation, so you probably want to do it only once and then re-use the same settings object, instead of reading it for each request. + +But every time we do: ```Python Settings() ``` -都会创建一个新的 `Settings` 对象,并且在创建时会再次读取 `.env` 文件。 +a new `Settings` object would be created, and at creation it would read the `.env` file again. -如果依赖项函数只是这样的: +If the dependency function was just like: ```Python def get_settings(): return Settings() ``` -我们将为每个请求创建该对象,并且将在每个请求中读取 `.env` 文件。 ⚠️ +we would create that object for each request, and we would be reading the `.env` file for each request. ⚠️ -但是,由于我们在顶部使用了 `@lru_cache()` 装饰器,因此只有在第一次调用它时,才会创建 `Settings` 对象一次。 ✔️ +But as we are using the `@lru_cache()` decorator on top, the `Settings` object will be created only once, the first time it's called. ✔️ === "Python 3.9+" @@ -351,31 +406,32 @@ def get_settings(): {!> ../../../docs_src/settings/app03_an/main.py!} ``` -=== "Python 3.6+ 非注解版本" +=== "Python 3.6+ non-Annotated" !!! tip - 如果可能,请尽量使用 `Annotated` 版本。 + Prefer to use the `Annotated` version if possible. ```Python hl_lines="1 10" {!> ../../../docs_src/settings/app03/main.py!} ``` -然后,在下一次请求的依赖项中对 `get_settings()` 进行任何后续调用时,它不会执行 `get_settings()` 的内部代码并创建新的 `Settings` 对象,而是返回在第一次调用时返回的相同对象,一次又一次。 +Then for any subsequent calls of `get_settings()` in the dependencies for the next requests, instead of executing the internal code of `get_settings()` and creating a new `Settings` object, it will return the same object that was returned on the first call, again and again. + +#### `lru_cache` Technical Details -#### `lru_cache` 技术细节 +`@lru_cache()` modifies the function it decorates to return the same value that was returned the first time, instead of computing it again, executing the code of the function every time. -`@lru_cache()` 修改了它所装饰的函数,以返回第一次返回的相同值,而不是再次计算它,每次都执行函数的代码。 +So, the function below it will be executed once for each combination of arguments. And then the values returned by each of those combinations of arguments will be used again and again whenever the function is called with exactly the same combination of arguments. -因此,下面的函数将对每个参数组合执行一次。然后,每个参数组合返回的值将在使用完全相同的参数组合调用函数时再次使用。 +For example, if you have a function: -例如,如果您有一个函数: ```Python @lru_cache() def say_hi(name: str, salutation: str = "Ms."): return f"Hello {salutation} {name}" ``` -您的程序可以像这样执行: +your program could execute like this: ```mermaid sequenceDiagram @@ -386,48 +442,48 @@ participant execute as Execute function rect rgba(0, 255, 0, .1) code ->> function: say_hi(name="Camila") - function ->> execute: 执行函数代码 - execute ->> code: 返回结果 + function ->> execute: execute function code + execute ->> code: return the result end rect rgba(0, 255, 255, .1) code ->> function: say_hi(name="Camila") - function ->> code: 返回存储的结果 + function ->> code: return stored result end rect rgba(0, 255, 0, .1) code ->> function: say_hi(name="Rick") - function ->> execute: 执行函数代码 - execute ->> code: 返回结果 + function ->> execute: execute function code + execute ->> code: return the result end rect rgba(0, 255, 0, .1) code ->> function: say_hi(name="Rick", salutation="Mr.") - function ->> execute: 执行函数代码 - execute ->> code: 返回结果 + function ->> execute: execute function code + execute ->> code: return the result end rect rgba(0, 255, 255, .1) code ->> function: say_hi(name="Rick") - function ->> code: 返回存储的结果 + function ->> code: return stored result end rect rgba(0, 255, 255, .1) code ->> function: say_hi(name="Camila") - function ->> code: 返回存储的结果 + function ->> code: return stored result end ``` -对于我们的依赖项 `get_settings()`,该函数甚至不接受任何参数,因此它始终返回相同的值。 +In the case of our dependency `get_settings()`, the function doesn't even take any arguments, so it always returns the same value. -这样,它的行为几乎就像是一个全局变量。但是由于它使用了依赖项函数,因此我们可以轻松地进行测试时的覆盖。 +That way, it behaves almost as if it was just a global variable. But as it uses a dependency function, then we can override it easily for testing. -`@lru_cache()` 是 `functools` 的一部分,它是 Python 标准库的一部分,您可以在Python 文档中了解有关 `@lru_cache()` 的更多信息。 +`@lru_cache()` is part of `functools` which is part of Python's standard library, you can read more about it in the Python docs for `@lru_cache()`. -## 小结 +## Recap -您可以使用 Pydantic 设置处理应用程序的设置或配置,利用 Pydantic 模型的所有功能。 +You can use Pydantic Settings to handle the settings or configurations for your application, with all the power of Pydantic models. -* 通过使用依赖项,您可以简化测试。 -* 您可以使用 `.env` 文件。 -* 使用 `@lru_cache()` 可以避免为每个请求重复读取 dotenv 文件,同时允许您在测试时进行覆盖。 +* By using a dependency you can simplify testing. +* You can use `.env` files with it. +* Using `@lru_cache()` lets you avoid reading the dotenv file again and again for each request, while allowing you to override it during testing. From 2db2c6df33cd0c8c7c69af1e29753b6ad4dfac7e Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:53 +0800 Subject: [PATCH 029/163] New translations sql-databases-peewee.md (Chinese Simplified) --- docs/zh/docs/advanced/sql-databases-peewee.md | 536 ++++++++++++++++++ 1 file changed, 536 insertions(+) create mode 100644 docs/zh/docs/advanced/sql-databases-peewee.md diff --git a/docs/zh/docs/advanced/sql-databases-peewee.md b/docs/zh/docs/advanced/sql-databases-peewee.md new file mode 100644 index 0000000000000..4243e96e3e948 --- /dev/null +++ b/docs/zh/docs/advanced/sql-databases-peewee.md @@ -0,0 +1,536 @@ +# SQL (Relational) Databases with Peewee + +!!! warning + If you are just starting, the tutorial [SQL (Relational) Databases](../tutorial/sql-databases.md){.internal-link target=_blank} that uses SQLAlchemy should be enough. + + Feel free to skip this. + + Peewee is not recommended with FastAPI as it doesn't play well with anything async Python. There are several better alternatives. + +!!! info + These docs assume Pydantic v1. + + Because Pewee doesn't play well with anything async and there are better alternatives, I won't update these docs for Pydantic v2, they are kept for now only for historical purposes. + +If you are starting a project from scratch, you are probably better off with SQLAlchemy ORM ([SQL (Relational) Databases](../tutorial/sql-databases.md){.internal-link target=_blank}), or any other async ORM. + +If you already have a code base that uses Peewee ORM, you can check here how to use it with **FastAPI**. + +!!! warning "Python 3.7+ required" + You will need Python 3.7 or above to safely use Peewee with FastAPI. + +## Peewee for async + +Peewee was not designed for async frameworks, or with them in mind. + +Peewee has some heavy assumptions about its defaults and about how it should be used. + +If you are developing an application with an older non-async framework, and can work with all its defaults, **it can be a great tool**. + +But if you need to change some of the defaults, support more than one predefined database, work with an async framework (like FastAPI), etc, you will need to add quite some complex extra code to override those defaults. + +Nevertheless, it's possible to do it, and here you'll see exactly what code you have to add to be able to use Peewee with FastAPI. + +!!! note "Technical Details" + You can read more about Peewee's stand about async in Python in the docs, an issue, a PR. + +## The same app + +We are going to create the same application as in the SQLAlchemy tutorial ([SQL (Relational) Databases](../tutorial/sql-databases.md){.internal-link target=_blank}). + +Most of the code is actually the same. + +So, we are going to focus only on the differences. + +## File structure + +Let's say you have a directory named `my_super_project` that contains a sub-directory called `sql_app` with a structure like this: + +``` +. +└── sql_app + ├── __init__.py + ├── crud.py + ├── database.py + ├── main.py + └── schemas.py +``` + +This is almost the same structure as we had for the SQLAlchemy tutorial. + +Now let's see what each file/module does. + +## Create the Peewee parts + +Let's refer to the file `sql_app/database.py`. + +### The standard Peewee code + +Let's first check all the normal Peewee code, create a Peewee database: + +```Python hl_lines="3 5 22" +{!../../../docs_src/sql_databases_peewee/sql_app/database.py!} +``` + +!!! tip + Have in mind that if you wanted to use a different database, like PostgreSQL, you couldn't just change the string. You would need to use a different Peewee database class. + +#### Note + +The argument: + +```Python +check_same_thread=False +``` + +is equivalent to the one in the SQLAlchemy tutorial: + +```Python +connect_args={"check_same_thread": False} +``` + +...it is needed only for `SQLite`. + +!!! info "Technical Details" + + Exactly the same technical details as in [SQL (Relational) Databases](../tutorial/sql-databases.md#note){.internal-link target=_blank} apply. + +### Make Peewee async-compatible `PeeweeConnectionState` + +The main issue with Peewee and FastAPI is that Peewee relies heavily on Python's `threading.local`, and it doesn't have a direct way to override it or let you handle connections/sessions directly (as is done in the SQLAlchemy tutorial). + +And `threading.local` is not compatible with the new async features of modern Python. + +!!! note "Technical Details" + `threading.local` is used to have a "magic" variable that has a different value for each thread. + + This was useful in older frameworks designed to have one single thread per request, no more, no less. + + Using this, each request would have its own database connection/session, which is the actual final goal. + + But FastAPI, using the new async features, could handle more than one request on the same thread. And at the same time, for a single request, it could run multiple things in different threads (in a threadpool), depending on if you use `async def` or normal `def`. This is what gives all the performance improvements to FastAPI. + +But Python 3.7 and above provide a more advanced alternative to `threading.local`, that can also be used in the places where `threading.local` would be used, but is compatible with the new async features. + +We are going to use that. It's called `contextvars`. + +We are going to override the internal parts of Peewee that use `threading.local` and replace them with `contextvars`, with the corresponding updates. + +This might seem a bit complex (and it actually is), you don't really need to completely understand how it works to use it. + +We will create a `PeeweeConnectionState`: + +```Python hl_lines="10-19" +{!../../../docs_src/sql_databases_peewee/sql_app/database.py!} +``` + +This class inherits from a special internal class used by Peewee. + +It has all the logic to make Peewee use `contextvars` instead of `threading.local`. + +`contextvars` works a bit differently than `threading.local`. But the rest of Peewee's internal code assumes that this class works with `threading.local`. + +So, we need to do some extra tricks to make it work as if it was just using `threading.local`. The `__init__`, `__setattr__`, and `__getattr__` implement all the required tricks for this to be used by Peewee without knowing that it is now compatible with FastAPI. + +!!! tip + This will just make Peewee behave correctly when used with FastAPI. Not randomly opening or closing connections that are being used, creating errors, etc. + + But it doesn't give Peewee async super-powers. You should still use normal `def` functions and not `async def`. + +### Use the custom `PeeweeConnectionState` class + +Now, overwrite the `._state` internal attribute in the Peewee database `db` object using the new `PeeweeConnectionState`: + +```Python hl_lines="24" +{!../../../docs_src/sql_databases_peewee/sql_app/database.py!} +``` + +!!! tip + Make sure you overwrite `db._state` *after* creating `db`. + +!!! tip + You would do the same for any other Peewee database, including `PostgresqlDatabase`, `MySQLDatabase`, etc. + +## Create the database models + +Let's now see the file `sql_app/models.py`. + +### Create Peewee models for our data + +Now create the Peewee models (classes) for `User` and `Item`. + +This is the same you would do if you followed the Peewee tutorial and updated the models to have the same data as in the SQLAlchemy tutorial. + +!!! tip + Peewee also uses the term "**model**" to refer to these classes and instances that interact with the database. + + But Pydantic also uses the term "**model**" to refer to something different, the data validation, conversion, and documentation classes and instances. + +Import `db` from `database` (the file `database.py` from above) and use it here. + +```Python hl_lines="3 6-12 15-21" +{!../../../docs_src/sql_databases_peewee/sql_app/models.py!} +``` + +!!! tip + Peewee creates several magic attributes. + + It will automatically add an `id` attribute as an integer to be the primary key. + + It will chose the name of the tables based on the class names. + + For the `Item`, it will create an attribute `owner_id` with the integer ID of the `User`. But we don't declare it anywhere. + +## Create the Pydantic models + +Now let's check the file `sql_app/schemas.py`. + +!!! tip + To avoid confusion between the Peewee *models* and the Pydantic *models*, we will have the file `models.py` with the Peewee models, and the file `schemas.py` with the Pydantic models. + + These Pydantic models define more or less a "schema" (a valid data shape). + + So this will help us avoiding confusion while using both. + +### Create the Pydantic *models* / schemas + +Create all the same Pydantic models as in the SQLAlchemy tutorial: + +```Python hl_lines="16-18 21-22 25-30 34-35 38-39 42-48" +{!../../../docs_src/sql_databases_peewee/sql_app/schemas.py!} +``` + +!!! tip + Here we are creating the models with an `id`. + + We didn't explicitly specify an `id` attribute in the Peewee models, but Peewee adds one automatically. + + We are also adding the magic `owner_id` attribute to `Item`. + +### Create a `PeeweeGetterDict` for the Pydantic *models* / schemas + +When you access a relationship in a Peewee object, like in `some_user.items`, Peewee doesn't provide a `list` of `Item`. + +It provides a special custom object of class `ModelSelect`. + +It's possible to create a `list` of its items with `list(some_user.items)`. + +But the object itself is not a `list`. And it's also not an actual Python generator. Because of this, Pydantic doesn't know by default how to convert it to a `list` of Pydantic *models* / schemas. + +But recent versions of Pydantic allow providing a custom class that inherits from `pydantic.utils.GetterDict`, to provide the functionality used when using the `orm_mode = True` to retrieve the values for ORM model attributes. + +We are going to create a custom `PeeweeGetterDict` class and use it in all the same Pydantic *models* / schemas that use `orm_mode`: + +```Python hl_lines="3 8-13 31 49" +{!../../../docs_src/sql_databases_peewee/sql_app/schemas.py!} +``` + +Here we are checking if the attribute that is being accessed (e.g. `.items` in `some_user.items`) is an instance of `peewee.ModelSelect`. + +And if that's the case, just return a `list` with it. + +And then we use it in the Pydantic *models* / schemas that use `orm_mode = True`, with the configuration variable `getter_dict = PeeweeGetterDict`. + +!!! tip + We only need to create one `PeeweeGetterDict` class, and we can use it in all the Pydantic *models* / schemas. + +## CRUD utils + +Now let's see the file `sql_app/crud.py`. + +### Create all the CRUD utils + +Create all the same CRUD utils as in the SQLAlchemy tutorial, all the code is very similar: + +```Python hl_lines="1 4-5 8-9 12-13 16-20 23-24 27-30" +{!../../../docs_src/sql_databases_peewee/sql_app/crud.py!} +``` + +There are some differences with the code for the SQLAlchemy tutorial. + +We don't pass a `db` attribute around. Instead we use the models directly. This is because the `db` object is a global object, that includes all the connection logic. That's why we had to do all the `contextvars` updates above. + +Aso, when returning several objects, like in `get_users`, we directly call `list`, like in: + +```Python +list(models.User.select()) +``` + +This is for the same reason that we had to create a custom `PeeweeGetterDict`. But by returning something that is already a `list` instead of the `peewee.ModelSelect` the `response_model` in the *path operation* with `List[models.User]` (that we'll see later) will work correctly. + +## Main **FastAPI** app + +And now in the file `sql_app/main.py` let's integrate and use all the other parts we created before. + +### Create the database tables + +In a very simplistic way create the database tables: + +```Python hl_lines="9-11" +{!../../../docs_src/sql_databases_peewee/sql_app/main.py!} +``` + +### Create a dependency + +Create a dependency that will connect the database right at the beginning of a request and disconnect it at the end: + +```Python hl_lines="23-29" +{!../../../docs_src/sql_databases_peewee/sql_app/main.py!} +``` + +Here we have an empty `yield` because we are actually not using the database object directly. + +It is connecting to the database and storing the connection data in an internal variable that is independent for each request (using the `contextvars` tricks from above). + +Because the database connection is potentially I/O blocking, this dependency is created with a normal `def` function. + +And then, in each *path operation function* that needs to access the database we add it as a dependency. + +But we are not using the value given by this dependency (it actually doesn't give any value, as it has an empty `yield`). So, we don't add it to the *path operation function* but to the *path operation decorator* in the `dependencies` parameter: + +```Python hl_lines="32 40 47 59 65 72" +{!../../../docs_src/sql_databases_peewee/sql_app/main.py!} +``` + +### Context variable sub-dependency + +For all the `contextvars` parts to work, we need to make sure we have an independent value in the `ContextVar` for each request that uses the database, and that value will be used as the database state (connection, transactions, etc) for the whole request. + +For that, we need to create another `async` dependency `reset_db_state()` that is used as a sub-dependency in `get_db()`. It will set the value for the context variable (with just a default `dict`) that will be used as the database state for the whole request. And then the dependency `get_db()` will store in it the database state (connection, transactions, etc). + +```Python hl_lines="18-20" +{!../../../docs_src/sql_databases_peewee/sql_app/main.py!} +``` + +For the **next request**, as we will reset that context variable again in the `async` dependency `reset_db_state()` and then create a new connection in the `get_db()` dependency, that new request will have its own database state (connection, transactions, etc). + +!!! tip + As FastAPI is an async framework, one request could start being processed, and before finishing, another request could be received and start processing as well, and it all could be processed in the same thread. + + But context variables are aware of these async features, so, a Peewee database state set in the `async` dependency `reset_db_state()` will keep its own data throughout the entire request. + + And at the same time, the other concurrent request will have its own database state that will be independent for the whole request. + +#### Peewee Proxy + +If you are using a Peewee Proxy, the actual database is at `db.obj`. + +So, you would reset it with: + +```Python hl_lines="3-4" +async def reset_db_state(): + database.db.obj._state._state.set(db_state_default.copy()) + database.db.obj._state.reset() +``` + +### Create your **FastAPI** *path operations* + +Now, finally, here's the standard **FastAPI** *path operations* code. + +```Python hl_lines="32-37 40-43 46-53 56-62 65-68 71-79" +{!../../../docs_src/sql_databases_peewee/sql_app/main.py!} +``` + +### About `def` vs `async def` + +The same as with SQLAlchemy, we are not doing something like: + +```Python +user = await models.User.select().first() +``` + +...but instead we are using: + +```Python +user = models.User.select().first() +``` + +So, again, we should declare the *path operation functions* and the dependency without `async def`, just with a normal `def`, as: + +```Python hl_lines="2" +# Something goes here +def read_users(skip: int = 0, limit: int = 100): + # Something goes here +``` + +## Testing Peewee with async + +This example includes an extra *path operation* that simulates a long processing request with `time.sleep(sleep_time)`. + +It will have the database connection open at the beginning and will just wait some seconds before replying back. And each new request will wait one second less. + +This will easily let you test that your app with Peewee and FastAPI is behaving correctly with all the stuff about threads. + +If you want to check how Peewee would break your app if used without modification, go the the `sql_app/database.py` file and comment the line: + +```Python +# db._state = PeeweeConnectionState() +``` + +And in the file `sql_app/main.py` file, comment the body of the `async` dependency `reset_db_state()` and replace it with a `pass`: + +```Python +async def reset_db_state(): +# database.db._state._state.set(db_state_default.copy()) +# database.db._state.reset() + pass +``` + +Then run your app with Uvicorn: + +
+ +```console +$ uvicorn sql_app.main:app --reload + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +Open your browser at http://127.0.0.1:8000/docs and create a couple of users. + +Then open 10 tabs at http://127.0.0.1:8000/docs#/default/read_slow_users_slowusers__get at the same time. + +Go to the *path operation* "Get `/slowusers/`" in all of the tabs. Use the "Try it out" button and execute the request in each tab, one right after the other. + +The tabs will wait for a bit and then some of them will show `Internal Server Error`. + +### What happens + +The first tab will make your app create a connection to the database and wait for some seconds before replying back and closing the database connection. + +Then, for the request in the next tab, your app will wait for one second less, and so on. + +This means that it will end up finishing some of the last tabs' requests earlier than some of the previous ones. + +Then one the last requests that wait less seconds will try to open a database connection, but as one of those previous requests for the other tabs will probably be handled in the same thread as the first one, it will have the same database connection that is already open, and Peewee will throw an error and you will see it in the terminal, and the response will have an `Internal Server Error`. + +This will probably happen for more than one of those tabs. + +If you had multiple clients talking to your app exactly at the same time, this is what could happen. + +And as your app starts to handle more and more clients at the same time, the waiting time in a single request needs to be shorter and shorter to trigger the error. + +### Fix Peewee with FastAPI + +Now go back to the file `sql_app/database.py`, and uncomment the line: + +```Python +db._state = PeeweeConnectionState() +``` + +And in the file `sql_app/main.py` file, uncomment the body of the `async` dependency `reset_db_state()`: + +```Python +async def reset_db_state(): + database.db._state._state.set(db_state_default.copy()) + database.db._state.reset() +``` + +Terminate your running app and start it again. + +Repeat the same process with the 10 tabs. This time all of them will wait and you will get all the results without errors. + +...You fixed it! + +## Review all the files + + Remember you should have a directory named `my_super_project` (or however you want) that contains a sub-directory called `sql_app`. + +`sql_app` should have the following files: + +* `sql_app/__init__.py`: is an empty file. + +* `sql_app/database.py`: + +```Python +{!../../../docs_src/sql_databases_peewee/sql_app/database.py!} +``` + +* `sql_app/models.py`: + +```Python +{!../../../docs_src/sql_databases_peewee/sql_app/models.py!} +``` + +* `sql_app/schemas.py`: + +```Python +{!../../../docs_src/sql_databases_peewee/sql_app/schemas.py!} +``` + +* `sql_app/crud.py`: + +```Python +{!../../../docs_src/sql_databases_peewee/sql_app/crud.py!} +``` + +* `sql_app/main.py`: + +```Python +{!../../../docs_src/sql_databases_peewee/sql_app/main.py!} +``` + +## Technical Details + +!!! warning + These are very technical details that you probably don't need. + +### The problem + +Peewee uses `threading.local` by default to store it's database "state" data (connection, transactions, etc). + +`threading.local` creates a value exclusive to the current thread, but an async framework would run all the code (e.g. for each request) in the same thread, and possibly not in order. + +On top of that, an async framework could run some sync code in a threadpool (using `asyncio.run_in_executor`), but belonging to the same request. + +This means that, with Peewee's current implementation, multiple tasks could be using the same `threading.local` variable and end up sharing the same connection and data (that they shouldn't), and at the same time, if they execute sync I/O-blocking code in a threadpool (as with normal `def` functions in FastAPI, in *path operations* and dependencies), that code won't have access to the database state variables, even while it's part of the same request and it should be able to get access to the same database state. + +### Context variables + +Python 3.7 has `contextvars` that can create a local variable very similar to `threading.local`, but also supporting these async features. + +There are several things to have in mind. + +The `ContextVar` has to be created at the top of the module, like: + +```Python +some_var = ContextVar("some_var", default="default value") +``` + +To set a value used in the current "context" (e.g. for the current request) use: + +```Python +some_var.set("new value") +``` + +To get a value anywhere inside of the context (e.g. in any part handling the current request) use: + +```Python +some_var.get() +``` + +### Set context variables in the `async` dependency `reset_db_state()` + +If some part of the async code sets the value with `some_var.set("updated in function")` (e.g. like the `async` dependency), the rest of the code in it and the code that goes after (including code inside of `async` functions called with `await`) will see that new value. + +So, in our case, if we set the Peewee state variable (with a default `dict`) in the `async` dependency, all the rest of the internal code in our app will see this value and will be able to reuse it for the whole request. + +And the context variable would be set again for the next request, even if they are concurrent. + +### Set database state in the dependency `get_db()` + +As `get_db()` is a normal `def` function, **FastAPI** will make it run in a threadpool, with a *copy* of the "context", holding the same value for the context variable (the `dict` with the reset database state). Then it can add database state to that `dict`, like the connection, etc. + +But if the value of the context variable (the default `dict`) was set in that normal `def` function, it would create a new value that would stay only in that thread of the threadpool, and the rest of the code (like the *path operation functions*) wouldn't have access to it. In `get_db()` we can only set values in the `dict`, but not the entire `dict` itself. + +So, we need to have the `async` dependency `reset_db_state()` to set the `dict` in the context variable. That way, all the code has access to the same `dict` for the database state for a single request. + +### Connect and disconnect in the dependency `get_db()` + +Then the next question would be, why not just connect and disconnect the database in the `async` dependency itself, instead of in `get_db()`? + +The `async` dependency has to be `async` for the context variable to be preserved for the rest of the request, but creating and closing the database connection is potentially blocking, so it could degrade performance if it was there. + +So we also need the normal `def` dependency `get_db()`. From 72897d3476e2030c8eae99d34c5bacea12687a47 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:54 +0800 Subject: [PATCH 030/163] New translations sub-applications.md (Chinese Simplified) --- docs/zh/docs/advanced/sub-applications.md | 73 +++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 docs/zh/docs/advanced/sub-applications.md diff --git a/docs/zh/docs/advanced/sub-applications.md b/docs/zh/docs/advanced/sub-applications.md new file mode 100644 index 0000000000000..0d04db29dcf52 --- /dev/null +++ b/docs/zh/docs/advanced/sub-applications.md @@ -0,0 +1,73 @@ +# Sub Applications - Mounts + +If you need to have two independent FastAPI applications, with their own independent OpenAPI and their own docs UIs, you can have a main app and "mount" one (or more) sub-application(s). + +## Mounting a **FastAPI** application + +"Mounting" means adding a completely "independent" application in a specific path, that then takes care of handling everything under that path, with the _path operations_ declared in that sub-application. + +### Top-level application + +First, create the main, top-level, **FastAPI** application, and its *path operations*: + +```Python hl_lines="3 6-8" +{!../../../docs_src/sub_applications/tutorial001.py!} +``` + +### Sub-application + +Then, create your sub-application, and its *path operations*. + +This sub-application is just another standard FastAPI application, but this is the one that will be "mounted": + +```Python hl_lines="11 14-16" +{!../../../docs_src/sub_applications/tutorial001.py!} +``` + +### Mount the sub-application + +In your top-level application, `app`, mount the sub-application, `subapi`. + +In this case, it will be mounted at the path `/subapi`: + +```Python hl_lines="11 19" +{!../../../docs_src/sub_applications/tutorial001.py!} +``` + +### Check the automatic API docs + +Now, run `uvicorn` with the main app, if your file is `main.py`, it would be: + +
+ +```console +$ uvicorn main:app --reload + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +And open the docs at http://127.0.0.1:8000/docs. + +You will see the automatic API docs for the main app, including only its own _path operations_: + + + +And then, open the docs for the sub-application, at http://127.0.0.1:8000/subapi/docs. + +You will see the automatic API docs for the sub-application, including only its own _path operations_, all under the correct sub-path prefix `/subapi`: + + + +If you try interacting with any of the two user interfaces, they will work correctly, because the browser will be able to talk to each specific app or sub-app. + +### Technical Details: `root_path` + +When you mount a sub-application as described above, FastAPI will take care of communicating the mount path for the sub-application using a mechanism from the ASGI specification called a `root_path`. + +That way, the sub-application will know to use that path prefix for the docs UI. + +And the sub-application could also have its own mounted sub-applications and everything would work correctly, because FastAPI handles all these `root_path`s automatically. + +You will learn more about the `root_path` and how to use it explicitly in the section about [Behind a Proxy](./behind-a-proxy.md){.internal-link target=_blank}. From e0368590211fe3af0a84eb324b50bc0fd6fa874b Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:55 +0800 Subject: [PATCH 031/163] New translations templates.md (Chinese Simplified) --- docs/zh/docs/advanced/templates.md | 77 ++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 docs/zh/docs/advanced/templates.md diff --git a/docs/zh/docs/advanced/templates.md b/docs/zh/docs/advanced/templates.md new file mode 100644 index 0000000000000..38618aeeb09cd --- /dev/null +++ b/docs/zh/docs/advanced/templates.md @@ -0,0 +1,77 @@ +# Templates + +You can use any template engine you want with **FastAPI**. + +A common choice is Jinja2, the same one used by Flask and other tools. + +There are utilities to configure it easily that you can use directly in your **FastAPI** application (provided by Starlette). + +## Install dependencies + +Install `jinja2`: + +
+ +```console +$ pip install jinja2 + +---> 100% +``` + +
+ +## Using `Jinja2Templates` + +* Import `Jinja2Templates`. +* Create a `templates` object that you can re-use later. +* Declare a `Request` parameter in the *path operation* that will return a template. +* Use the `templates` you created to render and return a `TemplateResponse`, passing the `request` as one of the key-value pairs in the Jinja2 "context". + +```Python hl_lines="4 11 15-16" +{!../../../docs_src/templates/tutorial001.py!} +``` + +!!! note + Notice that you have to pass the `request` as part of the key-value pairs in the context for Jinja2. So, you also have to declare it in your *path operation*. + +!!! tip + By declaring `response_class=HTMLResponse` the docs UI will be able to know that the response will be HTML. + +!!! note "Technical Details" + You could also use `from starlette.templating import Jinja2Templates`. + + **FastAPI** provides the same `starlette.templating` as `fastapi.templating` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. The same with `Request` and `StaticFiles`. + +## Writing templates + +Then you can write a template at `templates/item.html` with: + +```jinja hl_lines="7" +{!../../../docs_src/templates/templates/item.html!} +``` + +It will show the `id` taken from the "context" `dict` you passed: + +```Python +{"request": request, "id": id} +``` + +## Templates and static files + +And you can also use `url_for()` inside of the template, and use it, for example, with the `StaticFiles` you mounted. + +```jinja hl_lines="4" +{!../../../docs_src/templates/templates/item.html!} +``` + +In this example, it would link to a CSS file at `static/styles.css` with: + +```CSS hl_lines="4" +{!../../../docs_src/templates/static/styles.css!} +``` + +And because you are using `StaticFiles`, that CSS file would be served automatically by your **FastAPI** application at the URL `/static/styles.css`. + +## More details + +For more details, including how to test templates, check Starlette's docs on templates. From bb3c0d8d388bea0974157da290821d61001b5614 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:56 +0800 Subject: [PATCH 032/163] New translations testing-database.md (Chinese Simplified) --- docs/zh/docs/advanced/testing-database.md | 102 ++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 docs/zh/docs/advanced/testing-database.md diff --git a/docs/zh/docs/advanced/testing-database.md b/docs/zh/docs/advanced/testing-database.md new file mode 100644 index 0000000000000..3d66c7c9c7aa8 --- /dev/null +++ b/docs/zh/docs/advanced/testing-database.md @@ -0,0 +1,102 @@ +# Testing a Database + +!!! info + These docs are about to be updated. 🎉 + + The current version assumes Pydantic v1, and SQLAlchemy versions less than 2.0. + + The new docs will include Pydantic v2 and will use SQLModel (which is also based on SQLAlchemy) once it is updated to use Pydantic v2 as well. + +You can use the same dependency overrides from [Testing Dependencies with Overrides](testing-dependencies.md){.internal-link target=_blank} to alter a database for testing. + +You could want to set up a different database for testing, rollback the data after the tests, pre-fill it with some testing data, etc. + +The main idea is exactly the same you saw in that previous chapter. + +## Add tests for the SQL app + +Let's update the example from [SQL (Relational) Databases](../tutorial/sql-databases.md){.internal-link target=_blank} to use a testing database. + +All the app code is the same, you can go back to that chapter check how it was. + +The only changes here are in the new testing file. + +Your normal dependency `get_db()` would return a database session. + +In the test, you could use a dependency override to return your *custom* database session instead of the one that would be used normally. + +In this example we'll create a temporary database only for the tests. + +## File structure + +We create a new file at `sql_app/tests/test_sql_app.py`. + +So the new file structure looks like: + +``` hl_lines="9-11" +. +└── sql_app + ├── __init__.py + ├── crud.py + ├── database.py + ├── main.py + ├── models.py + ├── schemas.py + └── tests + ├── __init__.py + └── test_sql_app.py +``` + +## Create the new database session + +First, we create a new database session with the new database. + +We'll use an in-memory database that persists during the tests instead of the local file `sql_app.db`. + +But the rest of the session code is more or less the same, we just copy it. + +```Python hl_lines="8-13" +{!../../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} +``` + +!!! tip + You could reduce duplication in that code by putting it in a function and using it from both `database.py` and `tests/test_sql_app.py`. + + For simplicity and to focus on the specific testing code, we are just copying it. + +## Create the database + +Because now we are going to use a new database in a new file, we need to make sure we create the database with: + +```Python +Base.metadata.create_all(bind=engine) +``` + +That is normally called in `main.py`, but the line in `main.py` uses the database file `sql_app.db`, and we need to make sure we create `test.db` for the tests. + +So we add that line here, with the new file. + +```Python hl_lines="16" +{!../../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} +``` + +## Dependency override + +Now we create the dependency override and add it to the overrides for our app. + +```Python hl_lines="19-24 27" +{!../../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} +``` + +!!! tip + The code for `override_get_db()` is almost exactly the same as for `get_db()`, but in `override_get_db()` we use the `TestingSessionLocal` for the testing database instead. + +## Test the app + +Then we can just test the app as normally. + +```Python hl_lines="32-47" +{!../../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} +``` + +And all the modifications we made in the database during the tests will be in the `test.db` database instead of the main `sql_app.db`. From 181289519e13fcf57abbcd20df60d0d437eec1bf Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:56 +0800 Subject: [PATCH 033/163] New translations testing-dependencies.md (Chinese Simplified) --- docs/zh/docs/advanced/testing-dependencies.md | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 docs/zh/docs/advanced/testing-dependencies.md diff --git a/docs/zh/docs/advanced/testing-dependencies.md b/docs/zh/docs/advanced/testing-dependencies.md new file mode 100644 index 0000000000000..acc6283db3609 --- /dev/null +++ b/docs/zh/docs/advanced/testing-dependencies.md @@ -0,0 +1,81 @@ +# Testing Dependencies with Overrides + +## Overriding dependencies during testing + +There are some scenarios where you might want to override a dependency during testing. + +You don't want the original dependency to run (nor any of the sub-dependencies it might have). + +Instead, you want to provide a different dependency that will be used only during tests (possibly only some specific tests), and will provide a value that can be used where the value of the original dependency was used. + +### Use cases: external service + +An example could be that you have an external authentication provider that you need to call. + +You send it a token and it returns an authenticated user. + +This provider might be charging you per request, and calling it might take some extra time than if you had a fixed mock user for tests. + +You probably want to test the external provider once, but not necessarily call it for every test that runs. + +In this case, you can override the dependency that calls that provider, and use a custom dependency that returns a mock user, only for your tests. + +### Use the `app.dependency_overrides` attribute + +For these cases, your **FastAPI** application has an attribute `app.dependency_overrides`, it is a simple `dict`. + +To override a dependency for testing, you put as a key the original dependency (a function), and as the value, your dependency override (another function). + +And then **FastAPI** will call that override instead of the original dependency. + +=== "Python 3.10+" + + ```Python hl_lines="26-27 30" + {!> ../../../docs_src/dependency_testing/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="28-29 32" + {!> ../../../docs_src/dependency_testing/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="29-30 33" + {!> ../../../docs_src/dependency_testing/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="24-25 28" + {!> ../../../docs_src/dependency_testing/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="28-29 32" + {!> ../../../docs_src/dependency_testing/tutorial001.py!} + ``` + +!!! tip + You can set a dependency override for a dependency used anywhere in your **FastAPI** application. + + The original dependency could be used in a *path operation function*, a *path operation decorator* (when you don't use the return value), a `.include_router()` call, etc. + + FastAPI will still be able to override it. + +Then you can reset your overrides (remove them) by setting `app.dependency_overrides` to be an empty `dict`: + +```Python +app.dependency_overrides = {} +``` + +!!! tip + If you want to override a dependency only during some tests, you can set the override at the beginning of the test (inside the test function) and reset it at the end (at the end of the test function). From 3c9811fda337f3e7fd7dce32774e7556c086157a Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:57 +0800 Subject: [PATCH 034/163] New translations testing-events.md (Chinese Simplified) --- docs/zh/docs/advanced/testing-events.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 docs/zh/docs/advanced/testing-events.md diff --git a/docs/zh/docs/advanced/testing-events.md b/docs/zh/docs/advanced/testing-events.md new file mode 100644 index 0000000000000..b24a2ccfe8546 --- /dev/null +++ b/docs/zh/docs/advanced/testing-events.md @@ -0,0 +1,7 @@ +# Testing Events: startup - shutdown + +When you need your event handlers (`startup` and `shutdown`) to run in your tests, you can use the `TestClient` with a `with` statement: + +```Python hl_lines="9-12 20-24" +{!../../../docs_src/app_testing/tutorial003.py!} +``` From 9ea57dd5438ed0619364a6a7fdbe81e330560d1a Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:58 +0800 Subject: [PATCH 035/163] New translations testing-websockets.md (Chinese Simplified) --- docs/zh/docs/advanced/testing-websockets.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docs/zh/docs/advanced/testing-websockets.md diff --git a/docs/zh/docs/advanced/testing-websockets.md b/docs/zh/docs/advanced/testing-websockets.md new file mode 100644 index 0000000000000..4101e5a16b70a --- /dev/null +++ b/docs/zh/docs/advanced/testing-websockets.md @@ -0,0 +1,12 @@ +# Testing WebSockets + +You can use the same `TestClient` to test WebSockets. + +For this, you use the `TestClient` in a `with` statement, connecting to the WebSocket: + +```Python hl_lines="27-31" +{!../../../docs_src/app_testing/tutorial002.py!} +``` + +!!! note + For more details, check Starlette's documentation for testing WebSockets. From ff6bf9436beaab3716a8849f3b33b6da030a453f Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:36:59 +0800 Subject: [PATCH 036/163] New translations using-request-directly.md (Chinese Simplified) --- .../docs/advanced/using-request-directly.md | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 docs/zh/docs/advanced/using-request-directly.md diff --git a/docs/zh/docs/advanced/using-request-directly.md b/docs/zh/docs/advanced/using-request-directly.md new file mode 100644 index 0000000000000..968c511aa2870 --- /dev/null +++ b/docs/zh/docs/advanced/using-request-directly.md @@ -0,0 +1,52 @@ +# Using the Request Directly + +Up to now, you have been declaring the parts of the request that you need with their types. + +Taking data from: + +* The path as parameters. +* Headers. +* Cookies. +* etc. + +And by doing so, **FastAPI** is validating that data, converting it and generating documentation for your API automatically. + +But there are situations where you might need to access the `Request` object directly. + +## Details about the `Request` object + +As **FastAPI** is actually **Starlette** underneath, with a layer of several tools on top, you can use Starlette's `Request` object directly when you need to. + +It would also mean that if you get data from the `Request` object directly (for example, read the body) it won't be validated, converted or documented (with OpenAPI, for the automatic API user interface) by FastAPI. + +Although any other parameter declared normally (for example, the body with a Pydantic model) would still be validated, converted, annotated, etc. + +But there are specific cases where it's useful to get the `Request` object. + +## Use the `Request` object directly + +Let's imagine you want to get the client's IP address/host inside of your *path operation function*. + +For that you need to access the request directly. + +```Python hl_lines="1 7-8" +{!../../../docs_src/using_request_directly/tutorial001.py!} +``` + +By declaring a *path operation function* parameter with the type being the `Request` **FastAPI** will know to pass the `Request` in that parameter. + +!!! tip + Note that in this case, we are declaring a path parameter beside the request parameter. + + So, the path parameter will be extracted, validated, converted to the specified type and annotated with OpenAPI. + + The same way, you can declare any other parameter as normally, and additionally, get the `Request` too. + +## `Request` documentation + +You can read more details about the `Request` object in the official Starlette documentation site. + +!!! note "Technical Details" + You could also use `from starlette.requests import Request`. + + **FastAPI** provides it directly just as a convenience for you, the developer. But it comes directly from Starlette. From 608d3b8e85b2a609bd9a07bc72ab845d13f404d9 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:00 +0800 Subject: [PATCH 037/163] New translations websockets.md (Chinese Simplified) --- docs/zh/docs/advanced/websockets.md | 144 +++++++++++++++------------- 1 file changed, 77 insertions(+), 67 deletions(-) diff --git a/docs/zh/docs/advanced/websockets.md b/docs/zh/docs/advanced/websockets.md index a723487fdfcb4..e1ae3bf84d699 100644 --- a/docs/zh/docs/advanced/websockets.md +++ b/docs/zh/docs/advanced/websockets.md @@ -1,10 +1,12 @@ # WebSockets -您可以在 **FastAPI** 中使用 [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)。 +You can use WebSockets with **FastAPI**. -## 安装 `WebSockets` +## Install `WebSockets` -首先,您需要安装 `WebSockets`: +First you need to install `WebSockets`: + +
```console $ pip install websockets @@ -12,58 +14,62 @@ $ pip install websockets ---> 100% ``` -## WebSockets 客户端 +
+ +## WebSockets client -### 在生产环境中 +### In production -在您的生产系统中,您可能使用现代框架(如React、Vue.js或Angular)创建了一个前端。 +In your production system, you probably have a frontend created with a modern framework like React, Vue.js or Angular. -要使用 WebSockets 与后端进行通信,您可能会使用前端的工具。 +And to communicate using WebSockets with your backend you would probably use your frontend's utilities. -或者,您可能有一个原生移动应用程序,直接使用原生代码与 WebSocket 后端通信。 +Or you might have a native mobile application that communicates with your WebSocket backend directly, in native code. -或者,您可能有其他与 WebSocket 终端通信的方式。 +Or you might have any other way to communicate with the WebSocket endpoint. --- -但是,在本示例中,我们将使用一个非常简单的HTML文档,其中包含一些JavaScript,全部放在一个长字符串中。 +But for this example, we'll use a very simple HTML document with some JavaScript, all inside a long string. -当然,这并不是最优的做法,您不应该在生产环境中使用它。 +This, of course, is not optimal and you wouldn't use it for production. -在生产环境中,您应该选择上述任一选项。 +In production you would have one of the options above. -但这是一种专注于 WebSockets 的服务器端并提供一个工作示例的最简单方式: +But it's the simplest way to focus on the server-side of WebSockets and have a working example: ```Python hl_lines="2 6-38 41-43" {!../../../docs_src/websockets/tutorial001.py!} ``` -## 创建 `websocket` +## Create a `websocket` -在您的 **FastAPI** 应用程序中,创建一个 `websocket`: +In your **FastAPI** application, create a `websocket`: ```Python hl_lines="1 46-47" {!../../../docs_src/websockets/tutorial001.py!} ``` -!!! note "技术细节" - 您也可以使用 `from starlette.websockets import WebSocket`。 +!!! note "Technical Details" + You could also use `from starlette.websockets import WebSocket`. - **FastAPI** 直接提供了相同的 `WebSocket`,只是为了方便开发人员。但它直接来自 Starlette。 + **FastAPI** provides the same `WebSocket` directly just as a convenience for you, the developer. But it comes directly from Starlette. -## 等待消息并发送消息 +## Await for messages and send messages -在您的 WebSocket 路由中,您可以使用 `await` 等待消息并发送消息。 +In your WebSocket route you can `await` for messages and send messages. ```Python hl_lines="48-52" {!../../../docs_src/websockets/tutorial001.py!} ``` -您可以接收和发送二进制、文本和 JSON 数据。 +You can receive and send binary, text, and JSON data. + +## Try it -## 尝试一下 +If your file is named `main.py`, run your application with: -如果您的文件名为 `main.py`,请使用以下命令运行应用程序: +
```console $ uvicorn main:app --reload @@ -71,31 +77,31 @@ $ uvicorn main:app --reload INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ``` -在浏览器中打开 http://127.0.0.1:8000。 +
-您将看到一个简单的页面,如下所示: +Open your browser at http://127.0.0.1:8000. - +You will see a simple page like: -您可以在输入框中输入消息并发送: + - +You can type messages in the input box, and send them: -您的 **FastAPI** 应用程序将回复: + - +And your **FastAPI** application with WebSockets will respond back: -您可以发送(和接收)多条消息: + - +You can send (and receive) many messages: -所有这些消息都将使用同一个 WebSocket 连 + -接。 +And all of them will use the same WebSocket connection. -## 使用 `Depends` 和其他依赖项 +## Using `Depends` and others -在 WebSocket 端点中,您可以从 `fastapi` 导入并使用以下内容: +In WebSocket endpoints you can import from `fastapi` and use: * `Depends` * `Security` @@ -104,7 +110,7 @@ $ uvicorn main:app --reload * `Path` * `Query` -它们的工作方式与其他 FastAPI 端点/ *路径操作* 相同: +They work the same way as for other FastAPI endpoints/*path operations*: === "Python 3.10+" @@ -124,32 +130,34 @@ $ uvicorn main:app --reload {!> ../../../docs_src/websockets/tutorial002_an.py!} ``` -=== "Python 3.10+ 非带注解版本" +=== "Python 3.10+ non-Annotated" !!! tip - 如果可能,请尽量使用 `Annotated` 版本。 + Prefer to use the `Annotated` version if possible. ```Python hl_lines="66-67 79" {!> ../../../docs_src/websockets/tutorial002_py310.py!} ``` -=== "Python 3.6+ 非带注解版本" +=== "Python 3.6+ non-Annotated" !!! tip - 如果可能,请尽量使用 `Annotated` 版本。 + Prefer to use the `Annotated` version if possible. ```Python hl_lines="68-69 81" {!> ../../../docs_src/websockets/tutorial002.py!} ``` !!! info - 由于这是一个 WebSocket,抛出 `HTTPException` 并不是很合理,而是抛出 `WebSocketException`。 + As this is a WebSocket it doesn't really make sense to raise an `HTTPException`, instead we raise a `WebSocketException`. - 您可以使用规范中定义的有效代码。 + You can use a closing code from the valid codes defined in the specification. -### 尝试带有依赖项的 WebSockets +### Try the WebSockets with dependencies -如果您的文件名为 `main.py`,请使用以下命令运行应用程序: +If your file is named `main.py`, run your application with: + +
```console $ uvicorn main:app --reload @@ -157,23 +165,25 @@ $ uvicorn main:app --reload INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ``` -在浏览器中打开 http://127.0.0.1:8000。 +
+ +Open your browser at http://127.0.0.1:8000. -在页面中,您可以设置: +There you can set: -* "Item ID",用于路径。 -* "Token",作为查询参数。 +* The "Item ID", used in the path. +* The "Token" used as a query parameter. !!! tip - 注意,查询参数 `token` 将由依赖项处理。 + Notice that the query `token` will be handled by a dependency. -通过这样,您可以连接 WebSocket,然后发送和接收消息: +With that you can connect the WebSocket and then send and receive messages: - + -## 处理断开连接和多个客户端 +## Handling disconnections and multiple clients -当 WebSocket 连接关闭时,`await websocket.receive_text()` 将引发 `WebSocketDisconnect` 异常,您可以捕获并处理该异常,就像本示例中的示例一样。 +When a WebSocket connection is closed, the `await websocket.receive_text()` will raise a `WebSocketDisconnect` exception, which you can then catch and handle like in this example. === "Python 3.9+" @@ -187,28 +197,28 @@ $ uvicorn main:app --reload {!> ../../../docs_src/websockets/tutorial003.py!} ``` -尝试以下操作: +To try it out: -* 使用多个浏览器选项卡打开应用程序。 -* 从这些选项卡中发送消息。 -* 然后关闭其中一个选项卡。 +* Open the app with several browser tabs. +* Write messages from them. +* Then close one of the tabs. -这将引发 `WebSocketDisconnect` 异常,并且所有其他客户端都会收到类似以下的消息: +That will raise the `WebSocketDisconnect` exception, and all the other clients will receive a message like: ``` Client #1596980209979 left the chat ``` !!! tip - 上面的应用程序是一个最小和简单的示例,用于演示如何处理和向多个 WebSocket 连接广播消息。 - - 但请记住,由于所有内容都在内存中以单个列表的形式处理,因此它只能在进程运行时工作,并且只能使用单个进程。 + The app above is a minimal and simple example to demonstrate how to handle and broadcast messages to several WebSocket connections. - 如果您需要与 FastAPI 集成更简单但更强大的功能,支持 Redis、PostgreSQL 或其他功能,请查看 [encode/broadcaster](https://github.com/encode/broadcaster)。 + But have in mind that, as everything is handled in memory, in a single list, it will only work while the process is running, and will only work with a single process. + + If you need something easy to integrate with FastAPI but that is more robust, supported by Redis, PostgreSQL or others, check encode/broadcaster. -## 更多信息 +## More info -要了解更多选项,请查看 Starlette 的文档: +To learn more about the options, check Starlette's documentation for: -* [WebSocket 类](https://www.starlette.io/websockets/) -* [基于类的 WebSocket 处理](https://www.starlette.io/endpoints/#websocketendpoint)。 +* The `WebSocket` class. +* Class-based WebSocket handling. From 03a62c7bbcc5e4c9e5b64ed4ecc93b6bc18b6c4e Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:00 +0800 Subject: [PATCH 038/163] New translations wsgi.md (Chinese Simplified) --- docs/zh/docs/advanced/wsgi.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/zh/docs/advanced/wsgi.md b/docs/zh/docs/advanced/wsgi.md index ad71280fc6180..cfe3c78c11ca4 100644 --- a/docs/zh/docs/advanced/wsgi.md +++ b/docs/zh/docs/advanced/wsgi.md @@ -1,34 +1,34 @@ -# 包含 WSGI - Flask,Django,其它 +# Including WSGI - Flask, Django, others -您可以挂载多个 WSGI 应用,正如您在 [Sub Applications - Mounts](./sub-applications.md){.internal-link target=_blank}, [Behind a Proxy](./behind-a-proxy.md){.internal-link target=_blank} 中所看到的那样。 +You can mount WSGI applications as you saw with [Sub Applications - Mounts](./sub-applications.md){.internal-link target=_blank}, [Behind a Proxy](./behind-a-proxy.md){.internal-link target=_blank}. -为此, 您可以使用 `WSGIMiddleware` 来包装你的 WSGI 应用,如:Flask,Django,等等。 +For that, you can use the `WSGIMiddleware` and use it to wrap your WSGI application, for example, Flask, Django, etc. -## 使用 `WSGIMiddleware` +## Using `WSGIMiddleware` -您需要导入 `WSGIMiddleware`。 +You need to import `WSGIMiddleware`. -然后使用该中间件包装 WSGI 应用(例如 Flask)。 +Then wrap the WSGI (e.g. Flask) app with the middleware. -之后将其挂载到某一个路径下。 +And then mount that under a path. -```Python hl_lines="2-3 22" +```Python hl_lines="2-3 23" {!../../../docs_src/wsgi/tutorial001.py!} ``` -## 检查 +## Check it -现在,所有定义在 `/v1/` 路径下的请求将会被 Flask 应用处理。 +Now, every request under the path `/v1/` will be handled by the Flask application. -其余的请求则会被 **FastAPI** 处理。 +And the rest will be handled by **FastAPI**. -如果您使用 Uvicorn 运行应用实例并且访问 http://localhost:8000/v1/,您将会看到由 Flask 返回的响应: +If you run it with Uvicorn and go to http://localhost:8000/v1/ you will see the response from Flask: ```txt Hello, World from Flask! ``` -并且如果您访问 http://localhost:8000/v2,您将会看到由 FastAPI 返回的响应: +And if you go to http://localhost:8000/v2 you will see the response from FastAPI: ```JSON { From 66cb27e74e77fc9e849b7f7485301597a6faf3d4 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:01 +0800 Subject: [PATCH 039/163] New translations alternatives.md (Chinese Simplified) --- docs/zh/docs/alternatives.md | 398 +++++++++++++++++++++++++++++++++++ 1 file changed, 398 insertions(+) create mode 100644 docs/zh/docs/alternatives.md diff --git a/docs/zh/docs/alternatives.md b/docs/zh/docs/alternatives.md new file mode 100644 index 0000000000000..b9f184cccfe55 --- /dev/null +++ b/docs/zh/docs/alternatives.md @@ -0,0 +1,398 @@ +# Alternatives, Inspiration and Comparisons + +What inspired **FastAPI**, how it compares to other alternatives and what it learned from them. + +## Intro + +**FastAPI** wouldn't exist if not for the previous work of others. + +There have been many tools created before that have helped inspire its creation. + +I have been avoiding the creation of a new framework for several years. First I tried to solve all the features covered by **FastAPI** using many different frameworks, plug-ins, and tools. + +But at some point, there was no other option than creating something that provided all these features, taking the best ideas from previous tools, and combining them in the best way possible, using language features that weren't even available before (Python 3.6+ type hints). + +## Previous tools + +### Django + +It's the most popular Python framework and is widely trusted. It is used to build systems like Instagram. + +It's relatively tightly coupled with relational databases (like MySQL or PostgreSQL), so, having a NoSQL database (like Couchbase, MongoDB, Cassandra, etc) as the main store engine is not very easy. + +It was created to generate the HTML in the backend, not to create APIs used by a modern frontend (like React, Vue.js and Angular) or by other systems (like IoT devices) communicating with it. + +### Django REST Framework + +Django REST framework was created to be a flexible toolkit for building Web APIs using Django underneath, to improve its API capabilities. + +It is used by many companies including Mozilla, Red Hat and Eventbrite. + +It was one of the first examples of **automatic API documentation**, and this was specifically one of the first ideas that inspired "the search for" **FastAPI**. + +!!! note + Django REST Framework was created by Tom Christie. The same creator of Starlette and Uvicorn, on which **FastAPI** is based. + + +!!! check "Inspired **FastAPI** to" Have an automatic API documentation web user interface. + +### Flask + +Flask is a "microframework", it doesn't include database integrations nor many of the things that come by default in Django. + +This simplicity and flexibility allow doing things like using NoSQL databases as the main data storage system. + +As it is very simple, it's relatively intuitive to learn, although the documentation gets somewhat technical at some points. + +It is also commonly used for other applications that don't necessarily need a database, user management, or any of the many features that come pre-built in Django. Although many of these features can be added with plug-ins. + +This decoupling of parts, and being a "microframework" that could be extended to cover exactly what is needed was a key feature that I wanted to keep. + +Given the simplicity of Flask, it seemed like a good match for building APIs. The next thing to find was a "Django REST Framework" for Flask. + +!!! check "Inspired **FastAPI** to" Be a micro-framework. Making it easy to mix and match the tools and parts needed. + + Have a simple and easy to use routing system. + + +### Requests + +**FastAPI** is not actually an alternative to **Requests**. Their scope is very different. + +It would actually be common to use Requests *inside* of a FastAPI application. + +But still, FastAPI got quite some inspiration from Requests. + +**Requests** is a library to *interact* with APIs (as a client), while **FastAPI** is a library to *build* APIs (as a server). + +They are, more or less, at opposite ends, complementing each other. + +Requests has a very simple and intuitive design, it's very easy to use, with sensible defaults. But at the same time, it's very powerful and customizable. + +That's why, as said in the official website: + +> Requests is one of the most downloaded Python packages of all time + +The way you use it is very simple. For example, to do a `GET` request, you would write: + +```Python +response = requests.get("http://example.com/some/url") +``` + +The FastAPI counterpart API *path operation* could look like: + +```Python hl_lines="1" +@app.get("/some/url") +def read_url(): + return {"message": "Hello World"} +``` + +See the similarities in `requests.get(...)` and `@app.get(...)`. + +!!! check "Inspired **FastAPI** to" + * Have a simple and intuitive API. + * Use HTTP method names (operations) directly, in a straightforward and intuitive way. + * Have sensible defaults, but powerful customizations. + + +### Swagger / OpenAPI + +The main feature I wanted from Django REST Framework was the automatic API documentation. + +Then I found that there was a standard to document APIs, using JSON (or YAML, an extension of JSON) called Swagger. + +And there was a web user interface for Swagger APIs already created. So, being able to generate Swagger documentation for an API would allow using this web user interface automatically. + +At some point, Swagger was given to the Linux Foundation, to be renamed OpenAPI. + +That's why when talking about version 2.0 it's common to say "Swagger", and for version 3+ "OpenAPI". + +!!! check "Inspired **FastAPI** to" Adopt and use an open standard for API specifications, instead of a custom schema. + + And integrate standards-based user interface tools: + + * Swagger UI + * ReDoc + + These two were chosen for being fairly popular and stable, but doing a quick search, you could find dozens of additional alternative user interfaces for OpenAPI (that you can use with **FastAPI**). + +### Flask REST frameworks + +There are several Flask REST frameworks, but after investing the time and work into investigating them, I found that many are discontinued or abandoned, with several standing issues that made them unfit. + +### Marshmallow + +One of the main features needed by API systems is data "serialization" which is taking data from the code (Python) and converting it into something that can be sent through the network. For example, converting an object containing data from a database into a JSON object. Converting `datetime` objects into strings, etc. + +Another big feature needed by APIs is data validation, making sure that the data is valid, given certain parameters. For example, that some field is an `int`, and not some random string. This is especially useful for incoming data. + +Without a data validation system, you would have to do all the checks by hand, in code. + +These features are what Marshmallow was built to provide. It is a great library, and I have used it a lot before. + +But it was created before there existed Python type hints. So, to define every schema you need to use specific utils and classes provided by Marshmallow. + +!!! check "Inspired **FastAPI** to" Use code to define "schemas" that provide data types and validation, automatically. + +### Webargs + +Another big feature required by APIs is parsing data from incoming requests. + +Webargs is a tool that was made to provide that on top of several frameworks, including Flask. + +It uses Marshmallow underneath to do the data validation. And it was created by the same developers. + +It's a great tool and I have used it a lot too, before having **FastAPI**. + +!!! info + Webargs was created by the same Marshmallow developers. + +!!! check "Inspired **FastAPI** to" Have automatic validation of incoming request data. + +### APISpec + +Marshmallow and Webargs provide validation, parsing and serialization as plug-ins. + +But documentation is still missing. Then APISpec was created. + +It is a plug-in for many frameworks (and there's a plug-in for Starlette too). + +The way it works is that you write the definition of the schema using YAML format inside the docstring of each function handling a route. + +And it generates OpenAPI schemas. + +That's how it works in Flask, Starlette, Responder, etc. + +But then, we have again the problem of having a micro-syntax, inside of a Python string (a big YAML). + +The editor can't help much with that. And if we modify parameters or Marshmallow schemas and forget to also modify that YAML docstring, the generated schema would be obsolete. + +!!! info + APISpec was created by the same Marshmallow developers. + + +!!! check "Inspired **FastAPI** to" Support the open standard for APIs, OpenAPI. + +### Flask-apispec + +It's a Flask plug-in, that ties together Webargs, Marshmallow and APISpec. + +It uses the information from Webargs and Marshmallow to automatically generate OpenAPI schemas, using APISpec. + +It's a great tool, very under-rated. It should be way more popular than many Flask plug-ins out there. It might be due to its documentation being too concise and abstract. + +This solved having to write YAML (another syntax) inside of Python docstrings. + +This combination of Flask, Flask-apispec with Marshmallow and Webargs was my favorite backend stack until building **FastAPI**. + +Using it led to the creation of several Flask full-stack generators. These are the main stack I (and several external teams) have been using up to now: + +* https://github.com/tiangolo/full-stack +* https://github.com/tiangolo/full-stack-flask-couchbase +* https://github.com/tiangolo/full-stack-flask-couchdb + +And these same full-stack generators were the base of the [**FastAPI** Project Generators](project-generation.md){.internal-link target=_blank}. + +!!! info + Flask-apispec was created by the same Marshmallow developers. + +!!! check "Inspired **FastAPI** to" Generate the OpenAPI schema automatically, from the same code that defines serialization and validation. + +### NestJS (and Angular) + +This isn't even Python, NestJS is a JavaScript (TypeScript) NodeJS framework inspired by Angular. + +It achieves something somewhat similar to what can be done with Flask-apispec. + +It has an integrated dependency injection system, inspired by Angular two. It requires pre-registering the "injectables" (like all the other dependency injection systems I know), so, it adds to the verbosity and code repetition. + +As the parameters are described with TypeScript types (similar to Python type hints), editor support is quite good. + +But as TypeScript data is not preserved after compilation to JavaScript, it cannot rely on the types to define validation, serialization and documentation at the same time. Due to this and some design decisions, to get validation, serialization and automatic schema generation, it's needed to add decorators in many places. So, it becomes quite verbose. + +It can't handle nested models very well. So, if the JSON body in the request is a JSON object that has inner fields that in turn are nested JSON objects, it cannot be properly documented and validated. + +!!! check "Inspired **FastAPI** to" Use Python types to have great editor support. + + Have a powerful dependency injection system. Find a way to minimize code repetition. + +### Sanic + +It was one of the first extremely fast Python frameworks based on `asyncio`. It was made to be very similar to Flask. + +!!! note "Technical Details" + It used `uvloop` instead of the default Python `asyncio` loop. That's what made it so fast. + + It clearly inspired Uvicorn and Starlette, that are currently faster than Sanic in open benchmarks. + +!!! check "Inspired **FastAPI** to" Find a way to have a crazy performance. + + That's why **FastAPI** is based on Starlette, as it is the fastest framework available (tested by third-party benchmarks). + +### Falcon + +Falcon is another high performance Python framework, it is designed to be minimal, and work as the foundation of other frameworks like Hug. + +It is designed to have functions that receive two parameters, one "request" and one "response". Then you "read" parts from the request, and "write" parts to the response. Because of this design, it is not possible to declare request parameters and bodies with standard Python type hints as function parameters. + +So, data validation, serialization, and documentation, have to be done in code, not automatically. Or they have to be implemented as a framework on top of Falcon, like Hug. This same distinction happens in other frameworks that are inspired by Falcon's design, of having one request object and one response object as parameters. + +!!! check "Inspired **FastAPI** to" Find ways to get great performance. + + Along with Hug (as Hug is based on Falcon) inspired **FastAPI** to declare a `response` parameter in functions. + + Although in FastAPI it's optional, and is used mainly to set headers, cookies, and alternative status codes. + +### Molten + +I discovered Molten in the first stages of building **FastAPI**. And it has quite similar ideas: + +* Based on Python type hints. +* Validation and documentation from these types. +* Dependency Injection system. + +It doesn't use a data validation, serialization and documentation third-party library like Pydantic, it has its own. So, these data type definitions would not be reusable as easily. + +It requires a little bit more verbose configurations. And as it is based on WSGI (instead of ASGI), it is not designed to take advantage of the high-performance provided by tools like Uvicorn, Starlette and Sanic. + +The dependency injection system requires pre-registration of the dependencies and the dependencies are solved based on the declared types. So, it's not possible to declare more than one "component" that provides a certain type. + +Routes are declared in a single place, using functions declared in other places (instead of using decorators that can be placed right on top of the function that handles the endpoint). This is closer to how Django does it than to how Flask (and Starlette) does it. It separates in the code things that are relatively tightly coupled. + +!!! check "Inspired **FastAPI** to" Define extra validations for data types using the "default" value of model attributes. This improves editor support, and it was not available in Pydantic before. + + This actually inspired updating parts of Pydantic, to support the same validation declaration style (all this functionality is now already available in Pydantic). + +### Hug + +Hug was one of the first frameworks to implement the declaration of API parameter types using Python type hints. This was a great idea that inspired other tools to do the same. + +It used custom types in its declarations instead of standard Python types, but it was still a huge step forward. + +It also was one of the first frameworks to generate a custom schema declaring the whole API in JSON. + +It was not based on a standard like OpenAPI and JSON Schema. So it wouldn't be straightforward to integrate it with other tools, like Swagger UI. But again, it was a very innovative idea. + +It has an interesting, uncommon feature: using the same framework, it's possible to create APIs and also CLIs. + +As it is based on the previous standard for synchronous Python web frameworks (WSGI), it can't handle Websockets and other things, although it still has high performance too. + +!!! info + Hug was created by Timothy Crosley, the same creator of `isort`, a great tool to automatically sort imports in Python files. + +!!! check "Ideas inspired in **FastAPI**" Hug inspired parts of APIStar, and was one of the tools I found most promising, alongside APIStar. + + Hug helped inspiring **FastAPI** to use Python type hints to declare parameters, and to generate a schema defining the API automatically. + + Hug inspired **FastAPI** to declare a `response` parameter in functions to set headers and cookies. + +### APIStar (<= 0.5) + +Right before deciding to build **FastAPI** I found **APIStar** server. It had almost everything I was looking for and had a great design. + +It was one of the first implementations of a framework using Python type hints to declare parameters and requests that I ever saw (before NestJS and Molten). I found it more or less at the same time as Hug. But APIStar used the OpenAPI standard. + +It had automatic data validation, data serialization and OpenAPI schema generation based on the same type hints in several places. + +Body schema definitions didn't use the same Python type hints like Pydantic, it was a bit more similar to Marshmallow, so, editor support wouldn't be as good, but still, APIStar was the best available option. + +It had the best performance benchmarks at the time (only surpassed by Starlette). + +At first, it didn't have an automatic API documentation web UI, but I knew I could add Swagger UI to it. + +It had a dependency injection system. It required pre-registration of components, as other tools discussed above. But still, it was a great feature. + +I was never able to use it in a full project, as it didn't have security integration, so, I couldn't replace all the features I was having with the full-stack generators based on Flask-apispec. I had in my backlog of projects to create a pull request adding that functionality. + +But then, the project's focus shifted. + +It was no longer an API web framework, as the creator needed to focus on Starlette. + +Now APIStar is a set of tools to validate OpenAPI specifications, not a web framework. + +!!! info + APIStar was created by Tom Christie. The same guy that created: + + * Django REST Framework + * Starlette (in which **FastAPI** is based) + * Uvicorn (used by Starlette and **FastAPI**) + +!!! check "Inspired **FastAPI** to" Exist. + + The idea of declaring multiple things (data validation, serialization and documentation) with the same Python types, that at the same time provided great editor support, was something I considered a brilliant idea. + + And after searching for a long time for a similar framework and testing many different alternatives, APIStar was the best option available. + + Then APIStar stopped to exist as a server and Starlette was created, and was a new better foundation for such a system. That was the final inspiration to build **FastAPI**. + + I consider **FastAPI** a "spiritual successor" to APIStar, while improving and increasing the features, typing system, and other parts, based on the learnings from all these previous tools. + +## Used by **FastAPI** + +### Pydantic + +Pydantic is a library to define data validation, serialization and documentation (using JSON Schema) based on Python type hints. + +That makes it extremely intuitive. + +It is comparable to Marshmallow. Although it's faster than Marshmallow in benchmarks. And as it is based on the same Python type hints, the editor support is great. + +!!! check "**FastAPI** uses it to" Handle all the data validation, data serialization and automatic model documentation (based on JSON Schema). + + **FastAPI** then takes that JSON Schema data and puts it in OpenAPI, apart from all the other things it does. + +### Starlette + +Starlette is a lightweight ASGI framework/toolkit, which is ideal for building high-performance asyncio services. + +It is very simple and intuitive. It's designed to be easily extensible, and have modular components. + +It has: + +* Seriously impressive performance. +* WebSocket support. +* In-process background tasks. +* Startup and shutdown events. +* Test client built on HTTPX. +* CORS, GZip, Static Files, Streaming responses. +* Session and Cookie support. +* 100% test coverage. +* 100% type annotated codebase. +* Few hard dependencies. + +Starlette is currently the fastest Python framework tested. Only surpassed by Uvicorn, which is not a framework, but a server. + +Starlette provides all the basic web microframework functionality. + +But it doesn't provide automatic data validation, serialization or documentation. + +That's one of the main things that **FastAPI** adds on top, all based on Python type hints (using Pydantic). That, plus the dependency injection system, security utilities, OpenAPI schema generation, etc. + +!!! note "Technical Details" + ASGI is a new "standard" being developed by Django core team members. It is still not a "Python standard" (a PEP), although they are in the process of doing that. + + Nevertheless, it is already being used as a "standard" by several tools. This greatly improves interoperability, as you could switch Uvicorn for any other ASGI server (like Daphne or Hypercorn), or you could add ASGI compatible tools, like `python-socketio`. + +!!! check "**FastAPI** uses it to" Handle all the core web parts. Adding features on top. + + The class `FastAPI` itself inherits directly from the class `Starlette`. + + So, anything that you can do with Starlette, you can do it directly with **FastAPI**, as it is basically Starlette on steroids. + +### Uvicorn + +Uvicorn is a lightning-fast ASGI server, built on uvloop and httptools. + +It is not a web framework, but a server. For example, it doesn't provide tools for routing by paths. That's something that a framework like Starlette (or **FastAPI**) would provide on top. + +It is the recommended server for Starlette and **FastAPI**. + +!!! check "**FastAPI** recommends it as" The main web server to run **FastAPI** applications. + + You can combine it with Gunicorn, to have an asynchronous multi-process server. + + Check more details in the [Deployment](deployment/index.md){.internal-link target=_blank} section. + +## Benchmarks and speed + +To understand, compare, and see the difference between Uvicorn, Starlette and FastAPI, check the section about [Benchmarks](benchmarks.md){.internal-link target=_blank}. From 5137ad62c9b36e92c49332c679ce83527d3004b0 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:02 +0800 Subject: [PATCH 040/163] New translations async.md (Chinese Simplified) --- docs/zh/docs/async.md | 430 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 430 insertions(+) create mode 100644 docs/zh/docs/async.md diff --git a/docs/zh/docs/async.md b/docs/zh/docs/async.md new file mode 100644 index 0000000000000..3ac62a7795ea9 --- /dev/null +++ b/docs/zh/docs/async.md @@ -0,0 +1,430 @@ +# Concurrency and async / await + +Details about the `async def` syntax for *path operation functions* and some background about asynchronous code, concurrency, and parallelism. + +## In a hurry? + +TL;DR: + +If you are using third party libraries that tell you to call them with `await`, like: + +```Python +results = await some_library() +``` + +Then, declare your *path operation functions* with `async def` like: + +```Python hl_lines="2" +@app.get('/') +async def read_results(): + results = await some_library() + return results +``` + +!!! note + You can only use `await` inside of functions created with `async def`. + +--- + +If you are using a third party library that communicates with something (a database, an API, the file system, etc.) and doesn't have support for using `await`, (this is currently the case for most database libraries), then declare your *path operation functions* as normally, with just `def`, like: + +```Python hl_lines="2" +@app.get('/') +def results(): + results = some_library() + return results +``` + +--- + +If your application (somehow) doesn't have to communicate with anything else and wait for it to respond, use `async def`. + +--- + +If you just don't know, use normal `def`. + +--- + +**Note**: You can mix `def` and `async def` in your *path operation functions* as much as you need and define each one using the best option for you. FastAPI will do the right thing with them. + +Anyway, in any of the cases above, FastAPI will still work asynchronously and be extremely fast. + +But by following the steps above, it will be able to do some performance optimizations. + +## Technical Details + +Modern versions of Python have support for **"asynchronous code"** using something called **"coroutines"**, with **`async` and `await`** syntax. + +Let's see that phrase by parts in the sections below: + +* **Asynchronous Code** +* **`async` and `await`** +* **Coroutines** + +## Asynchronous Code + +Asynchronous code just means that the language 💬 has a way to tell the computer / program 🤖 that at some point in the code, it 🤖 will have to wait for *something else* to finish somewhere else. Let's say that *something else* is called "slow-file" 📝. + +So, during that time, the computer can go and do some other work, while "slow-file" 📝 finishes. + +Then the computer / program 🤖 will come back every time it has a chance because it's waiting again, or whenever it 🤖 finished all the work it had at that point. And it 🤖 will see if any of the tasks it was waiting for have already finished, doing whatever it had to do. + +Next, it 🤖 takes the first task to finish (let's say, our "slow-file" 📝) and continues whatever it had to do with it. + +That "wait for something else" normally refers to I/O operations that are relatively "slow" (compared to the speed of the processor and the RAM memory), like waiting for: + +* the data from the client to be sent through the network +* the data sent by your program to be received by the client through the network +* the contents of a file in the disk to be read by the system and given to your program +* the contents your program gave to the system to be written to disk +* a remote API operation +* a database operation to finish +* a database query to return the results +* etc. + +As the execution time is consumed mostly by waiting for I/O operations, they call them "I/O bound" operations. + +It's called "asynchronous" because the computer / program doesn't have to be "synchronized" with the slow task, waiting for the exact moment that the task finishes, while doing nothing, to be able to take the task result and continue the work. + +Instead of that, by being an "asynchronous" system, once finished, the task can wait in line a little bit (some microseconds) for the computer / program to finish whatever it went to do, and then come back to take the results and continue working with them. + +For "synchronous" (contrary to "asynchronous") they commonly also use the term "sequential", because the computer / program follows all the steps in sequence before switching to a different task, even if those steps involve waiting. + +### Concurrency and Burgers + +This idea of **asynchronous** code described above is also sometimes called **"concurrency"**. It is different from **"parallelism"**. + +**Concurrency** and **parallelism** both relate to "different things happening more or less at the same time". + +But the details between *concurrency* and *parallelism* are quite different. + +To see the difference, imagine the following story about burgers: + +### Concurrent Burgers + +You go with your crush to get fast food, you stand in line while the cashier takes the orders from the people in front of you. 😍 + + + +Then it's your turn, you place your order of 2 very fancy burgers for your crush and you. 🍔🍔 + + + +The cashier says something to the cook in the kitchen so they know they have to prepare your burgers (even though they are currently preparing the ones for the previous clients). + + + +You pay. 💸 + +The cashier gives you the number of your turn. + + + +While you are waiting, you go with your crush and pick a table, you sit and talk with your crush for a long time (as your burgers are very fancy and take some time to prepare). + +As you are sitting at the table with your crush, while you wait for the burgers, you can spend that time admiring how awesome, cute and smart your crush is ✨😍✨. + + + +While waiting and talking to your crush, from time to time, you check the number displayed on the counter to see if it's your turn already. + +Then at some point, it finally is your turn. You go to the counter, get your burgers and come back to the table. + + + +You and your crush eat the burgers and have a nice time. ✨ + + + +!!! info + Beautiful illustrations by Ketrina Thompson. 🎨 + +--- + +Imagine you are the computer / program 🤖 in that story. + +While you are at the line, you are just idle 😴, waiting for your turn, not doing anything very "productive". But the line is fast because the cashier is only taking the orders (not preparing them), so that's fine. + +Then, when it's your turn, you do actual "productive" work, you process the menu, decide what you want, get your crush's choice, pay, check that you give the correct bill or card, check that you are charged correctly, check that the order has the correct items, etc. + +But then, even though you still don't have your burgers, your work with the cashier is "on pause" ⏸, because you have to wait 🕙 for your burgers to be ready. + +But as you go away from the counter and sit at the table with a number for your turn, you can switch 🔀 your attention to your crush, and "work" ⏯ 🤓 on that. Then you are again doing something very "productive" as is flirting with your crush 😍. + +Then the cashier 💁 says "I'm finished with doing the burgers" by putting your number on the counter's display, but you don't jump like crazy immediately when the displayed number changes to your turn number. You know no one will steal your burgers because you have the number of your turn, and they have theirs. + +So you wait for your crush to finish the story (finish the current work ⏯ / task being processed 🤓), smile gently and say that you are going for the burgers ⏸. + +Then you go to the counter 🔀, to the initial task that is now finished ⏯, pick the burgers, say thanks and take them to the table. That finishes that step / task of interaction with the counter ⏹. That in turn, creates a new task, of "eating burgers" 🔀 ⏯, but the previous one of "getting burgers" is finished ⏹. + +### Parallel Burgers + +Now let's imagine these aren't "Concurrent Burgers", but "Parallel Burgers". + +You go with your crush to get parallel fast food. + +You stand in line while several (let's say 8) cashiers that at the same time are cooks take the orders from the people in front of you. + +Everyone before you is waiting for their burgers to be ready before leaving the counter because each of the 8 cashiers goes and prepares the burger right away before getting the next order. + + + +Then it's finally your turn, you place your order of 2 very fancy burgers for your crush and you. + +You pay 💸. + + + +The cashier goes to the kitchen. + +You wait, standing in front of the counter 🕙, so that no one else takes your burgers before you do, as there are no numbers for turns. + + + +As you and your crush are busy not letting anyone get in front of you and take your burgers whenever they arrive, you cannot pay attention to your crush. 😞 + +This is "synchronous" work, you are "synchronized" with the cashier/cook 👨‍🍳. You have to wait 🕙 and be there at the exact moment that the cashier/cook 👨‍🍳 finishes the burgers and gives them to you, or otherwise, someone else might take them. + + + +Then your cashier/cook 👨‍🍳 finally comes back with your burgers, after a long time waiting 🕙 there in front of the counter. + + + +You take your burgers and go to the table with your crush. + +You just eat them, and you are done. ⏹ + + + +There was not much talk or flirting as most of the time was spent waiting 🕙 in front of the counter. 😞 + +!!! info + Beautiful illustrations by Ketrina Thompson. 🎨 + +--- + +In this scenario of the parallel burgers, you are a computer / program 🤖 with two processors (you and your crush), both waiting 🕙 and dedicating their attention ⏯ to be "waiting on the counter" 🕙 for a long time. + +The fast food store has 8 processors (cashiers/cooks). While the concurrent burgers store might have had only 2 (one cashier and one cook). + +But still, the final experience is not the best. 😞 + +--- + +This would be the parallel equivalent story for burgers. 🍔 + +For a more "real life" example of this, imagine a bank. + +Up to recently, most of the banks had multiple cashiers 👨‍💼👨‍💼👨‍💼👨‍💼 and a big line 🕙🕙🕙🕙🕙🕙🕙🕙. + +All of the cashiers doing all the work with one client after the other 👨‍💼⏯. + +And you have to wait 🕙 in the line for a long time or you lose your turn. + +You probably wouldn't want to take your crush 😍 with you to do errands at the bank 🏦. + +### Burger Conclusion + +In this scenario of "fast food burgers with your crush", as there is a lot of waiting 🕙, it makes a lot more sense to have a concurrent system ⏸🔀⏯. + +This is the case for most of the web applications. + +Many, many users, but your server is waiting 🕙 for their not-so-good connection to send their requests. + +And then waiting 🕙 again for the responses to come back. + +This "waiting" 🕙 is measured in microseconds, but still, summing it all, it's a lot of waiting in the end. + +That's why it makes a lot of sense to use asynchronous ⏸🔀⏯ code for web APIs. + +This kind of asynchronicity is what made NodeJS popular (even though NodeJS is not parallel) and that's the strength of Go as a programming language. + +And that's the same level of performance you get with **FastAPI**. + +And as you can have parallelism and asynchronicity at the same time, you get higher performance than most of the tested NodeJS frameworks and on par with Go, which is a compiled language closer to C (all thanks to Starlette). + +### Is concurrency better than parallelism? + +Nope! That's not the moral of the story. + +Concurrency is different than parallelism. And it is better on **specific** scenarios that involve a lot of waiting. Because of that, it generally is a lot better than parallelism for web application development. But not for everything. + +So, to balance that out, imagine the following short story: + +> You have to clean a big, dirty house. + +*Yep, that's the whole story*. + +--- + +There's no waiting 🕙 anywhere, just a lot of work to be done, on multiple places of the house. + +You could have turns as in the burgers example, first the living room, then the kitchen, but as you are not waiting 🕙 for anything, just cleaning and cleaning, the turns wouldn't affect anything. + +It would take the same amount of time to finish with or without turns (concurrency) and you would have done the same amount of work. + +But in this case, if you could bring the 8 ex-cashier/cooks/now-cleaners, and each one of them (plus you) could take a zone of the house to clean it, you could do all the work in **parallel**, with the extra help, and finish much sooner. + +In this scenario, each one of the cleaners (including you) would be a processor, doing their part of the job. + +And as most of the execution time is taken by actual work (instead of waiting), and the work in a computer is done by a CPU, they call these problems "CPU bound". + +--- + +Common examples of CPU bound operations are things that require complex math processing. + +For example: + +* **Audio** or **image processing**. +* **Computer vision**: an image is composed of millions of pixels, each pixel has 3 values / colors, processing that normally requires computing something on those pixels, all at the same time. +* **Machine Learning**: it normally requires lots of "matrix" and "vector" multiplications. Think of a huge spreadsheet with numbers and multiplying all of them together at the same time. +* **Deep Learning**: this is a sub-field of Machine Learning, so, the same applies. It's just that there is not a single spreadsheet of numbers to multiply, but a huge set of them, and in many cases, you use a special processor to build and / or use those models. + +### Concurrency + Parallelism: Web + Machine Learning + +With **FastAPI** you can take the advantage of concurrency that is very common for web development (the same main attraction of NodeJS). + +But you can also exploit the benefits of parallelism and multiprocessing (having multiple processes running in parallel) for **CPU bound** workloads like those in Machine Learning systems. + +That, plus the simple fact that Python is the main language for **Data Science**, Machine Learning and especially Deep Learning, make FastAPI a very good match for Data Science / Machine Learning web APIs and applications (among many others). + +To see how to achieve this parallelism in production see the section about [Deployment](deployment/index.md){.internal-link target=_blank}. + +## `async` and `await` + +Modern versions of Python have a very intuitive way to define asynchronous code. This makes it look just like normal "sequential" code and do the "awaiting" for you at the right moments. + +When there is an operation that will require waiting before giving the results and has support for these new Python features, you can code it like: + +```Python +burgers = await get_burgers(2) +``` + +The key here is the `await`. It tells Python that it has to wait ⏸ for `get_burgers(2)` to finish doing its thing 🕙 before storing the results in `burgers`. With that, Python will know that it can go and do something else 🔀 ⏯ in the meanwhile (like receiving another request). + +For `await` to work, it has to be inside a function that supports this asynchronicity. To do that, you just declare it with `async def`: + +```Python hl_lines="1" +async def get_burgers(number: int): + # Do some asynchronous stuff to create the burgers + return burgers +``` + +...instead of `def`: + +```Python hl_lines="2" +# This is not asynchronous +def get_sequential_burgers(number: int): + # Do some sequential stuff to create the burgers + return burgers +``` + +With `async def`, Python knows that, inside that function, it has to be aware of `await` expressions, and that it can "pause" ⏸ the execution of that function and go do something else 🔀 before coming back. + +When you want to call an `async def` function, you have to "await" it. So, this won't work: + +```Python +# This won't work, because get_burgers was defined with: async def +burgers = get_burgers(2) +``` + +--- + +So, if you are using a library that tells you that you can call it with `await`, you need to create the *path operation functions* that uses it with `async def`, like in: + +```Python hl_lines="2-3" +@app.get('/burgers') +async def read_burgers(): + burgers = await get_burgers(2) + return burgers +``` + +### More technical details + +You might have noticed that `await` can only be used inside of functions defined with `async def`. + +But at the same time, functions defined with `async def` have to be "awaited". So, functions with `async def` can only be called inside of functions defined with `async def` too. + +So, about the egg and the chicken, how do you call the first `async` function? + +If you are working with **FastAPI** you don't have to worry about that, because that "first" function will be your *path operation function*, and FastAPI will know how to do the right thing. + +But if you want to use `async` / `await` without FastAPI, you can do it as well. + +### Write your own async code + +Starlette (and **FastAPI**) are based on AnyIO, which makes it compatible with both Python's standard library asyncio and Trio. + +In particular, you can directly use AnyIO for your advanced concurrency use cases that require more advanced patterns in your own code. + +And even if you were not using FastAPI, you could also write your own async applications with AnyIO to be highly compatible and get its benefits (e.g. *structured concurrency*). + +### Other forms of asynchronous code + +This style of using `async` and `await` is relatively new in the language. + +But it makes working with asynchronous code a lot easier. + +This same syntax (or almost identical) was also included recently in modern versions of JavaScript (in Browser and NodeJS). + +But before that, handling asynchronous code was quite more complex and difficult. + +In previous versions of Python, you could have used threads or Gevent. But the code is way more complex to understand, debug, and think about. + +In previous versions of NodeJS / Browser JavaScript, you would have used "callbacks". Which leads to callback hell. + +## Coroutines + +**Coroutine** is just the very fancy term for the thing returned by an `async def` function. Python knows that it is something like a function that it can start and that it will end at some point, but that it might be paused ⏸ internally too, whenever there is an `await` inside of it. + +But all this functionality of using asynchronous code with `async` and `await` is many times summarized as using "coroutines". It is comparable to the main key feature of Go, the "Goroutines". + +## Conclusion + +Let's see the same phrase from above: + +> Modern versions of Python have support for **"asynchronous code"** using something called **"coroutines"**, with **`async` and `await`** syntax. + +That should make more sense now. ✨ + +All that is what powers FastAPI (through Starlette) and what makes it have such an impressive performance. + +## Very Technical Details + +!!! warning + You can probably skip this. + + These are very technical details of how **FastAPI** works underneath. + + If you have quite some technical knowledge (co-routines, threads, blocking, etc.) and are curious about how FastAPI handles `async def` vs normal `def`, go ahead. + +### Path operation functions + +When you declare a *path operation function* with normal `def` instead of `async def`, it is run in an external threadpool that is then awaited, instead of being called directly (as it would block the server). + +If you are coming from another async framework that does not work in the way described above and you are used to defining trivial compute-only *path operation functions* with plain `def` for a tiny performance gain (about 100 nanoseconds), please note that in **FastAPI** the effect would be quite opposite. In these cases, it's better to use `async def` unless your *path operation functions* use code that performs blocking I/O. + +Still, in both situations, chances are that **FastAPI** will [still be faster](/#performance){.internal-link target=_blank} than (or at least comparable to) your previous framework. + +### Dependencies + +The same applies for [dependencies](/tutorial/dependencies/index.md){.internal-link target=_blank}. If a dependency is a standard `def` function instead of `async def`, it is run in the external threadpool. + +### Sub-dependencies + +You can have multiple dependencies and [sub-dependencies](/tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank} requiring each other (as parameters of the function definitions), some of them might be created with `async def` and some with normal `def`. It would still work, and the ones created with normal `def` would be called on an external thread (from the threadpool) instead of being "awaited". + +### Other utility functions + +Any other utility function that you call directly can be created with normal `def` or `async def` and FastAPI won't affect the way you call it. + +This is in contrast to the functions that FastAPI calls for you: *path operation functions* and dependencies. + +If your utility function is a normal function with `def`, it will be called directly (as you write it in your code), not in a threadpool, if the function is created with `async def` then you should `await` for that function when you call it in your code. + +--- + +Again, these are very technical details that would probably be useful if you came searching for them. + +Otherwise, you should be good with the guidelines from the section above: In a hurry?. From ca07105e0ac1ef832fe5b5364e7e26d08e166fa0 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:03 +0800 Subject: [PATCH 041/163] New translations benchmarks.md (Chinese Simplified) --- docs/zh/docs/benchmarks.md | 44 +++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/zh/docs/benchmarks.md b/docs/zh/docs/benchmarks.md index 71e8d483822ac..e05fec8406621 100644 --- a/docs/zh/docs/benchmarks.md +++ b/docs/zh/docs/benchmarks.md @@ -1,34 +1,34 @@ -# 基准测试 +# Benchmarks -第三方机构 TechEmpower 的基准测试表明在 Uvicorn 下运行的 **FastAPI** 应用程序是 可用的最快的 Python 框架之一,仅次于 Starlette 和 Uvicorn 本身 (由 FastAPI 内部使用)。(*) +Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as one of the fastest Python frameworks available, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*) -但是在查看基准得分和对比时,请注意以下几点。 +But when checking benchmarks and comparisons you should have the following in mind. -## 基准测试和速度 +## Benchmarks and speed -当你查看基准测试时,几个不同类型的工具被等效地做比较是很常见的情况。 +When you check the benchmarks, it is common to see several tools of different types compared as equivalent. -具体来说,是将 Uvicorn,Starlette 和 FastAPI 一起比较(在许多其它工具中)。 +Specifically, to see Uvicorn, Starlette and FastAPI compared together (among many other tools). -该工具解决的问题最简单,它将获得更好的性能。而且大多数基准测试并未测试该工具提供的其他功能。 +The simpler the problem solved by the tool, the better performance it will get. And most of the benchmarks don't test the additional features provided by the tool. -层次结构如下: +The hierarchy is like: -* **Uvicorn**:ASGI服务器 - * **Starlette**:(使用 Uvicorn)网络微框架 - * **FastAPI**:(使用 Starlette) 具有多个附加功能的API微框架,用于构建API,进行数据验证等。 +* **Uvicorn**: an ASGI server + * **Starlette**: (uses Uvicorn) a web microframework + * **FastAPI**: (uses Starlette) an API microframework with several additional features for building APIs, with data validation, etc. * **Uvicorn**: - * 具有最佳性能,因为除了服务器本身外,它没有太多额外的代码。 - * 您不会直接在 Uvicorn 中编写应用程序。这意味着您的代码至少必须包含 Starlette(或 **FastAPI**)提供的代码。如果您这样做了(即直接在 Uvicorn 中编写应用程序),最终的应用程序会和使用了框架并且最小化了应用代码和 bug 的情况具有相同的性能损耗。 - * 如果要对比与 Uvicorn 对标的服务器,请将其与 Daphne,Hypercorn,uWSGI等应用服务器进行比较。 + * Will have the best performance, as it doesn't have much extra code apart from the server itself. + * You wouldn't write an application in Uvicorn directly. That would mean that your code would have to include more or less, at least, all the code provided by Starlette (or **FastAPI**). And if you did that, your final application would have the same overhead as having used a framework and minimizing your app code and bugs. + * If you are comparing Uvicorn, compare it against Daphne, Hypercorn, uWSGI, etc. Application servers. * **Starlette**: - * 在 Uvicorn 后使用 Starlette,性能会略有下降。实际上,Starlette 使用 Uvicorn运行。因此,由于必须执行更多的代码,它只会比 Uvicorn 更慢。 - * 但它为您提供了构建简单的网络程序的工具,并具有基于路径的路由等功能。 - * 如果想对比与 Starlette 对标的开发框架,请将其与 Sanic,Flask,Django 等网络框架(或微框架)进行比较。 + * Will have the next best performance, after Uvicorn. In fact, Starlette uses Uvicorn to run. So, it probably can only get "slower" than Uvicorn by having to execute more code. + * But it provides you the tools to build simple web applications, with routing based on paths, etc. + * If you are comparing Starlette, compare it against Sanic, Flask, Django, etc. Web frameworks (or microframeworks). * **FastAPI**: - * 与 Starlette 使用 Uvicorn 一样,由于 **FastAPI** 使用 Starlette,因此 FastAPI 不能比 Starlette 更快。 - * FastAPI 在 Starlette 基础上提供了更多功能。例如在开发 API 时,所需的数据验证和序列化功能。FastAPI 可以帮助您自动生成 API文档,(文档在应用程序启动时自动生成,所以不会增加应用程序运行时的开销)。 - * 如果您不使用 FastAPI 而直接使用 Starlette(或诸如 Sanic,Flask,Responder 等其它工具),您则要自己实现所有的数据验证和序列化。那么最终您的应用程序会和使用 FastAPI 构建的程序有相同的开销。一般这种数据验证和序列化的操作在您应用程序的代码中会占很大比重。 - * 因此,通过使用 FastAPI 意味着您可以节省开发时间,减少编码错误,用更少的编码实现其功能,并且相比不使用 FastAPI 您很大可能会获得相同或更好的性能(因为那样您必须在代码中实现所有相同的功能)。 - * 如果您想对比与 FastAPI 对标的开发框架,请与能够提供数据验证,序列化和带有自动文档生成的网络应用程序框架(或工具集)进行对比,例如具有集成自动数据验证,序列化和自动化文档的 Flask-apispec,NestJS,Molten 等。 + * The same way that Starlette uses Uvicorn and cannot be faster than it, **FastAPI** uses Starlette, so it cannot be faster than it. + * FastAPI provides more features on top of Starlette. Features that you almost always need when building APIs, like data validation and serialization. And by using it, you get automatic documentation for free (the automatic documentation doesn't even add overhead to running applications, it is generated on startup). + * If you didn't use FastAPI and used Starlette directly (or another tool, like Sanic, Flask, Responder, etc) you would have to implement all the data validation and serialization yourself. So, your final application would still have the same overhead as if it was built using FastAPI. And in many cases, this data validation and serialization is the biggest amount of code written in applications. + * So, by using FastAPI you are saving development time, bugs, lines of code, and you would probably get the same performance (or better) you would if you didn't use it (as you would have to implement it all in your code). + * If you are comparing FastAPI, compare it against a web application framework (or set of tools) that provides data validation, serialization and documentation, like Flask-apispec, NestJS, Molten, etc. Frameworks with integrated automatic data validation, serialization and documentation. From 3f444141a1f4f11cfe1806044df00621a6cf5394 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:04 +0800 Subject: [PATCH 042/163] New translations contributing.md (Chinese Simplified) --- docs/zh/docs/contributing.md | 333 ++++++++++++++++------------------- 1 file changed, 152 insertions(+), 181 deletions(-) diff --git a/docs/zh/docs/contributing.md b/docs/zh/docs/contributing.md index 4ebd673150b25..f9276b28e9e49 100644 --- a/docs/zh/docs/contributing.md +++ b/docs/zh/docs/contributing.md @@ -1,14 +1,14 @@ -# 开发 - 贡献 +# Development - Contributing -首先,你最好先了解 [帮助 FastAPI 及获取帮助](help-fastapi.md){.internal-link target=_blank}的基本方式。 +First, you might want to see the basic ways to [help FastAPI and get help](help-fastapi.md){.internal-link target=_blank}. -## 开发 +## Developing -如果你已经克隆了源码仓库,并且需要深入研究代码,下面是设置开发环境的指南。 +If you already cloned the repository and you know that you need to deep dive in the code, here are some guidelines to set up your environment. -### 通过 `venv` 管理虚拟环境 +### Virtual environment with `venv` -你可以使用 Python 的 `venv` 模块在一个目录中创建虚拟环境: +You can create a virtual environment in a directory using Python's `venv` module:
@@ -18,11 +18,11 @@ $ python -m venv env
-这将使用 Python 程序创建一个 `./env/` 目录,然后你将能够为这个隔离的环境安装软件包。 +That will create a directory `./env/` with the Python binaries and then you will be able to install packages for that isolated environment. -### 激活虚拟环境 +### Activate the environment -使用以下方法激活新环境: +Activate the new environment with: === "Linux, macOS" @@ -32,6 +32,7 @@ $ python -m venv env $ source ./env/bin/activate ``` + === "Windows PowerShell" @@ -42,21 +43,23 @@ $ python -m venv env $ .\env\Scripts\Activate.ps1 ``` + === "Windows Bash" Or if you use Bash for Windows (e.g. Git Bash): - +
```console $ source ./env/Scripts/activate ``` +
-要检查操作是否成功,运行: +To check it worked, use: === "Linux, macOS, Windows Bash" @@ -68,6 +71,7 @@ $ python -m venv env some/directory/fastapi/env/bin/pip ``` + === "Windows PowerShell" @@ -80,19 +84,31 @@ $ python -m venv env some/directory/fastapi/env/bin/pip ``` + -如果显示 `pip` 程序文件位于 `env/bin/pip` 则说明激活成功。 🎉 +If it shows the `pip` binary at `env/bin/pip` then it worked. 🎉 + +Make sure you have the latest pip version on your virtual environment to avoid errors on the next steps: + +
+ +```console +$ python -m pip install --upgrade pip +---> 100% +``` + +
!!! tip - 每一次你在该环境下使用 `pip` 安装了新软件包时,请再次激活该环境。 + Every time you install a new package with `pip` under that environment, activate the environment again. - 这样可以确保你在使用由该软件包安装的终端程序时使用的是当前虚拟环境中的程序,而不是其他的可能是全局安装的程序。 + This makes sure that if you use a terminal program installed by that package, you use the one from your local environment and not any other that could be installed globally. ### pip -如上所述激活环境后: +After activating the environment as described above:
@@ -104,78 +120,69 @@ $ pip install -r requirements.txt
-这将在虚拟环境中安装所有依赖和本地版本的 FastAPI。 +It will install all the dependencies and your local FastAPI in your local environment. -#### 使用本地 FastAPI +#### Using your local FastAPI -如果你创建一个导入并使用 FastAPI 的 Python 文件,然后使用虚拟环境中的 Python 运行它,它将使用你本地的 FastAPI 源码。 +If you create a Python file that imports and uses FastAPI, and run it with the Python from your local environment, it will use your local FastAPI source code. -并且如果你更改该本地 FastAPI 的源码,由于它是通过 `-e` 安装的,当你再次运行那个 Python 文件,它将使用你刚刚编辑过的最新版本的 FastAPI。 +And if you update that local FastAPI source code when you run that Python file again, it will use the fresh version of FastAPI you just edited. -这样,你不必再去重新"安装"你的本地版本即可测试所有更改。 +That way, you don't have to "install" your local version to be able to test every change. -### 格式化 +!!! note "Technical Details" + This only happens when you install using this included `requiements.txt` instead of installing `pip install fastapi` directly. -你可以运行下面的脚本来格式化和清理所有代码: + That is because inside of the `requirements.txt` file, the local version of FastAPI is marked to be installed in "editable" mode, with the `-e` option. -
+### Format -```console -$ bash scripts/format.sh -``` - -
- -它还会自动对所有导入代码进行整理。 - -为了使整理正确进行,你需要在当前环境中安装本地的 FastAPI,即在运行上述段落中的命令时添加 `-e`。 - -### 格式化导入 - -还有另一个脚本可以格式化所有导入,并确保你没有未使用的导入代码: +There is a script that you can run that will format and clean all your code:
```console -$ bash scripts/format-imports.sh +$ bash scripts/format.sh ```
-由于它依次运行了多个命令,并修改和还原了许多文件,所以运行时间会更长一些,因此经常地使用 `scripts/format.sh` 然后仅在提交前执行 `scripts/format-imports.sh` 会更好一些。 +It will also auto-sort all your imports. + +For it to sort them correctly, you need to have FastAPI installed locally in your environment, with the command in the section above using `-e`. -## 文档 +## Docs -首先,请确保按上述步骤设置好环境,这将安装所有需要的依赖。 +First, make sure you set up your environment as described above, that will install all the requirements. -文档使用 MkDocs 生成。 +The documentation uses MkDocs. -并且在 `./scripts/docs.py` 中还有适用的额外工具/脚本来处理翻译。 +And there are extra tools/scripts in place to handle translations in `./scripts/docs.py`. !!! tip - 你不需要去了解 `./scripts/docs.py` 中的代码,只需在命令行中使用它即可。 + You don't need to see the code in `./scripts/docs.py`, you just use it in the command line. -所有文档均在 `./docs/en/` 目录中以 Markdown 文件格式保存。 +All the documentation is in Markdown format in the directory `./docs/en/`. -许多的教程章节里包含有代码块。 +Many of the tutorials have blocks of code. -在大多数情况下,这些代码块是可以直接运行的真实完整的应用程序。 +In most of the cases, these blocks of code are actual complete applications that can be run as is. -实际上,这些代码块不是写在 Markdown 文件内的,它们是位于 `./docs_src/` 目录中的 Python 文件。 +In fact, those blocks of code are not written inside the Markdown, they are Python files in the `./docs_src/` directory. -生成站点时,这些 Python 文件会被包含/注入到文档中。 +And those Python files are included/injected in the documentation when generating the site. -### 用于测试的文档 +### Docs for tests -大多数的测试实际上都是针对文档中的示例源文件运行的。 +Most of the tests actually run against the example source files in the documentation. -这有助于确保: +This helps making sure that: -* 文档始终是最新的。 -* 文档示例可以直接运行。 -* 绝大多数特性既在文档中得以阐述,又通过测试覆盖进行保障。 +* The documentation is up to date. +* The documentation examples can be run as is. +* Most of the features are covered by the documentation, ensured by test coverage. -在本地开发期间,有一个脚本可以实时重载地构建站点并用来检查所做的任何更改: +During local development, there is a script that builds the site and checks for any changes, live-reloading:
@@ -189,17 +196,33 @@ $ python ./scripts/docs.py live
-它将在 `http://127.0.0.1:8008` 提供对文档的访问。 +It will serve the documentation on `http://127.0.0.1:8008`. -这样,你可以编辑文档/源文件并实时查看更改。 +That way, you can edit the documentation/source files and see the changes live. + +!!! tip + Alternatively, you can perform the same steps that scripts does manually. + + Go into the language directory, for the main docs in English it's at `docs/en/`: + + ```console + $ cd docs/en/ + ``` -#### Typer CLI (可选) -本指引向你展示了如何直接用 `python` 程序运行 `./scripts/docs.py` 中的脚本。 + Then run `mkdocs` in that directory: -但你也可以使用 Typer CLI,而且在安装了补全功能后,你将可以在终端中对命令进行自动补全。 + ```console + $ mkdocs serve --dev-addr 8008 + ``` -如果你打算安装 Typer CLI ,可以使用以下命令安装自动补全功能: +#### Typer CLI (optional) + +The instructions here show you how to use the script at `./scripts/docs.py` with the `python` program directly. + +But you can also use Typer CLI, and you will get autocompletion in your terminal for the commands after installing completion. + +If you install Typer CLI, you can install completion with:
@@ -212,9 +235,9 @@ Completion will take effect once you restart the terminal.
-### 应用和文档同时运行 +### Apps and docs at the same time -如果你使用以下方式运行示例程序: +If you run the examples with, e.g.:
@@ -226,47 +249,49 @@ $ uvicorn tutorial001:app --reload
-由于 Uvicorn 默认使用 `8000` 端口 ,因此运行在 `8008` 端口上的文档不会与之冲突。 +as Uvicorn by default will use the port `8000`, the documentation on port `8008` won't clash. -### 翻译 +### Translations -非常感谢你能够参与文档的翻译!这项工作需要社区的帮助才能完成。 🌎 🚀 +Help with translations is VERY MUCH appreciated! And it can't be done without the help from the community. 🌎 🚀 -以下是参与帮助翻译的步骤。 +Here are the steps to help with translations. -#### 建议和指南 +#### Tips and guidelines -* 在当前 已有的 pull requests 中查找你使用的语言,添加要求修改或同意合并的评审意见。 +* Check the currently existing pull requests for your language and add reviews requesting changes or approving them. !!! tip - 你可以为已有的 pull requests 添加包含修改建议的评论。 + You can add comments with change suggestions to existing pull requests. - 详情可查看关于 添加 pull request 评审意见 以同意合并或要求修改的文档。 + Check the docs about adding a pull request review to approve it or request changes. -* 在 issues 中查找是否有对你所用语言所进行的协作翻译。 +* Check if there's a GitHub Discussion to coordinate translations for your language. You can subscribe to it, and when there's a new pull request to review, an automatic comment will be added to the discussion. -* 每翻译一个页面新增一个 pull request。这将使其他人更容易对其进行评审。 +* Add a single pull request per page translated. That will make it much easier for others to review it. -对于我(译注:作者使用西班牙语和英语)不懂的语言,我将在等待其他人评审翻译之后将其合并。 +For the languages I don't speak, I'll wait for several others to review the translation before merging. -* 你还可以查看是否有你所用语言的翻译,并对其进行评审,这将帮助我了解翻译是否正确以及能否将其合并。 +* You can also check if there are translations for your language and add a review to them, that will help me know that the translation is correct and I can merge it. + * You could check in the GitHub Discussions for your language. + * Or you can filter the existing PRs by the ones with the label for your language, for example, for Spanish, the label is `lang-es`. -* 使用相同的 Python 示例并且仅翻译文档中的文本。无需进行任何其他更改示例也能正常工作。 +* Use the same Python examples and only translate the text in the docs. You don't have to change anything for this to work. -* 使用相同的图片、文件名以及链接地址。无需进行任何其他调整来让它们兼容。 +* Use the same images, file names, and links. You don't have to change anything for it to work. -* 你可以从 ISO 639-1 代码列表 表中查找你想要翻译语言的两位字母代码。 +* To check the 2-letter code for the language you want to translate you can use the table List of ISO 639-1 codes. -#### 已有的语言 +#### Existing language -假设你想将某个页面翻译成已经翻译了一些页面的语言,例如西班牙语。 +Let's say you want to translate a page for a language that already has translations for some pages, like Spanish. -对于西班牙语来说,它的两位字母代码是 `es`。所以西班牙语翻译的目录位于 `docs/es/`。 +In the case of Spanish, the 2-letter code is `es`. So, the directory for Spanish translations is located at `docs/es/`. !!! tip - 主要("官方")语言是英语,位于 `docs/en/`目录。 + The main ("official") language is English, located at `docs/en/`. -现在为西班牙语文档运行实时服务器: +Now run the live server for the docs in Spanish:
@@ -281,82 +306,56 @@ $ python ./scripts/docs.py live es
-现在你可以访问 http://127.0.0.1:8008 实时查看你所做的更改。 +!!! tip + Alternatively, you can perform the same steps that scripts does manually. -如果你查看 FastAPI 的线上文档网站,会看到每种语言都有所有页面。但是某些页面并未被翻译并且会有一处关于缺少翻译的提示。 + Go into the language directory, for the Spanish translations it's at `docs/es/`: -但是当你像上面这样在本地运行文档时,你只会看到已经翻译的页面。 + ```console + $ cd docs/es/ + ``` -现在假设你要为 [Features](features.md){.internal-link target=_blank} 章节添加翻译。 -* 复制下面的文件: + Then run `mkdocs` in that directory: -``` -docs/en/docs/features.md -``` + ```console + $ mkdocs serve --dev-addr 8008 + ``` -* 粘贴到你想要翻译语言目录的相同位置,比如: +Now you can go to http://127.0.0.1:8008 and see your changes live. -``` -docs/es/docs/features.md -``` +You will see that every language has all the pages. But some pages are not translated and have a notification about the missing translation. -!!! tip - 注意路径和文件名的唯一变化是语言代码,从 `en` 更改为 `es`。 +Now let's say that you want to add a translation for the section [Features](features.md){.internal-link target=_blank}. -* 现在打开位于英语文档目录下的 MkDocs 配置文件: +* Copy the file at: ``` -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 +docs/en/docs/features.md ``` -* 打开你正在编辑的语言目录中的 MkDocs 配置文件,例如: +* Paste it in exactly the same location but for the language you want to translate, e.g.: ``` -docs/es/docs/mkdocs.yml -``` - -* 将其添加到与英语文档完全相同的位置,例如: - -```YAML hl_lines="8" -site_name: FastAPI -# More stuff -nav: -- FastAPI: index.md -- Languages: - - en: / - - es: /es/ -- features.md +docs/es/docs/features.md ``` -如果配置文件中还有其他条目,请确保你所翻译的新条目和它们之间的顺序与英文版本完全相同。 +!!! tip + Notice that the only change in the path and file name is the language code, from `en` to `es`. -打开浏览器,现在你将看到文档展示了你所加入的新章节。 🎉 +If you go to your browser you will see that now the docs show your new section. 🎉 -现在,你可以将它全部翻译完并在保存文件后进行预览。 +Now you can translate it all and see how it looks as you save the file. -#### 新语言 +#### New Language -假设你想要为尚未有任何页面被翻译的语言添加翻译。 +Let's say that you want to add translations for a language that is not yet translated, not even some pages. -假设你想要添加克里奥尔语翻译,而且文档中还没有该语言的翻译。 +Let's say you want to add translations for Creole, and it's not yet there in the docs. -点击上面提到的链接,可以查到"克里奥尔语"的代码为 `ht`。 +Checking the link from above, the code for "Creole" is `ht`. -下一步是运行脚本以生成新的翻译目录: +The next step is to run the script to generate a new translation directory:
@@ -365,57 +364,34 @@ nav: $ python ./scripts/docs.py new-lang ht Successfully initialized: docs/ht -Updating ht -Updating en ```
-现在,你可以在编辑器中查看新创建的目录 `docs/ht/`。 - -!!! tip - 在添加实际的翻译之前,仅以此创建首个 pull request 来设定新语言的配置。 - - 这样当你在翻译第一个页面时,其他人可以帮助翻译其他页面。🚀 - -首先翻译文档主页 `docs/ht/index.md`。 +Now you can check in your code editor the newly created directory `docs/ht/`. -然后,你可以根据上面的"已有语言"的指引继续进行翻译。 - -##### 不支持的新语言 - -如果在运行实时服务器脚本时收到关于不支持该语言的错误,类似于: +That command created a file `docs/ht/mkdocs.yml` with a simple config that inherits everything from the `en` version: +```yaml +INHERIT: ../en/mkdocs.yml ``` - raise TemplateNotFound(template) -jinja2.exceptions.TemplateNotFound: partials/language/xx.html -``` - -这意味着文档的主题不支持该语言(在这种例子中,编造的语言代码是 `xx`)。 -但是别担心,你可以将主题语言设置为英语,然后翻译文档的内容。 - -如果你需要这么做,编辑新语言目录下的 `mkdocs.yml`,它将有类似下面的内容: +!!! tip + You could also simply create that file with those contents manually. -```YAML hl_lines="5" -site_name: FastAPI -# More stuff -theme: - # More stuff - language: xx -``` +That command also created a dummy file `docs/ht/index.md` for the main page, you can start by translating that one. -将其中的 language 项从 `xx`(你的语言代码)更改为 `en`。 +You can continue with the previous instructions for an "Existing Language" for that process. -然后,你就可以再次启动实时服务器了。 +You can make the first pull request with those two files, `docs/ht/mkdocs.yml` and `docs/ht/index.md`. 🎉 -#### 预览结果 +#### Preview the result -当你通过 `live` 命令使用 `./scripts/docs.py` 中的脚本时,该脚本仅展示当前语言已有的文件和翻译。 +You can use the `./scripts/docs.py` with the `live` command to preview the results (or `mkdocs serve`). -但是当你完成翻译后,你可以像在线上展示一样测试所有内容。 +Once you are done, you can also test it all as it would look online, including all the other languages. -为此,首先构建所有文档: +To do that, first build all the docs:
@@ -423,21 +399,16 @@ theme: // 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 ```
-这将在 `./docs_build/` 目录中为每一种语言生成全部的文档。还包括添加所有缺少翻译的文件,并带有一条"此文件还没有翻译"的提醒。但是你不需要对该目录执行任何操作。 - -然后,它针对每种语言构建独立的 MkDocs 站点,将它们组合在一起,并在 `./site/` 目录中生成最终的输出。 +This builds all those independent MkDocs sites for each language, combines them, and generates the final output at `./site/`. -然后你可以使用命令 `serve` 来运行生成的站点: +Then you can serve that with the command `serve`:
@@ -453,9 +424,9 @@ Serving at: http://127.0.0.1:8008
-## 测试 +## Tests -你可以在本地运行下面的脚本来测试所有代码并生成 HTML 格式的覆盖率报告: +There is a script that you can run locally to test all the code and generate coverage reports in HTML:
@@ -465,4 +436,4 @@ $ bash scripts/test-cov-html.sh
-该命令生成了一个 `./htmlcov/` 目录,如果你在浏览器中打开 `./htmlcov/index.html` 文件,你可以交互式地浏览被测试所覆盖的代码区块,并注意是否缺少了任何区块。 +This command generates a directory `./htmlcov/`, if you open the file `./htmlcov/index.html` in your browser, you can explore interactively the regions of code that are covered by the tests, and notice if there is any region missing. From 9f27d8dd4d4714f504ab7d64e3bb7d102bb77a59 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:05 +0800 Subject: [PATCH 043/163] New translations concepts.md (Chinese Simplified) --- docs/zh/docs/deployment/concepts.md | 311 ++++++++++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 docs/zh/docs/deployment/concepts.md diff --git a/docs/zh/docs/deployment/concepts.md b/docs/zh/docs/deployment/concepts.md new file mode 100644 index 0000000000000..72f55860c3093 --- /dev/null +++ b/docs/zh/docs/deployment/concepts.md @@ -0,0 +1,311 @@ +# Deployments Concepts + +When deploying a **FastAPI** application, or actually, any type of web API, there are several concepts that you probably care about, and using them you can find the **most appropriate** way to **deploy your application**. + +Some of the important concepts are: + +* Security - HTTPS +* Running on startup +* Restarts +* Replication (the number of processes running) +* Memory +* Previous steps before starting + +We'll see how they would affect **deployments**. + +In the end, the ultimate objective is to be able to **serve your API clients** in a way that is **secure**, to **avoid disruptions**, and to use the **compute resources** (for example remote servers/virtual machines) as efficiently as possible. 🚀 + +I'll tell you a bit more about these **concepts** here, and that would hopefully give you the **intuition** you would need to decide how to deploy your API in very different environments, possibly even in **future** ones that don't exist yet. + +By considering these concepts, you will be able to **evaluate and design** the best way to deploy **your own APIs**. + +In the next chapters, I'll give you more **concrete recipes** to deploy FastAPI applications. + +But for now, let's check these important **conceptual ideas**. These concepts also apply to any other type of web API. 💡 + +## Security - HTTPS + +In the [previous chapter about HTTPS](./https.md){.internal-link target=_blank} we learned about how HTTPS provides encryption for your API. + +We also saw that HTTPS is normally provided by a component **external** to your application server, a **TLS Termination Proxy**. + +And there has to be something in charge of **renewing the HTTPS certificates**, it could be the same component or it could be something different. + +### Example Tools for HTTPS + +Some of the tools you could use as a TLS Termination Proxy are: + +* Traefik + * Automatically handles certificates renewals ✨ +* Caddy + * Automatically handles certificates renewals ✨ +* Nginx + * With an external component like Certbot for certificate renewals +* HAProxy + * With an external component like Certbot for certificate renewals +* Kubernetes with an Ingress Controller like Nginx + * With an external component like cert-manager for certificate renewals +* Handled internally by a cloud provider as part of their services (read below 👇) + +Another option is that you could use a **cloud service** that does more of the work including setting up HTTPS. It could have some restrictions or charge you more, etc. But in that case, you wouldn't have to set up a TLS Termination Proxy yourself. + +I'll show you some concrete examples in the next chapters. + +--- + +Then the next concepts to consider are all about the program running your actual API (e.g. Uvicorn). + +## Program and Process + +We will talk a lot about the running "**process**", so it's useful to have clarity about what it means, and what's the difference with the word "**program**". + +### What is a Program + +The word **program** is commonly used to describe many things: + +* The **code** that you write, the **Python files**. +* The **file** that can be **executed** by the operating system, for example: `python`, `python.exe` or `uvicorn`. +* A particular program while it is **running** on the operating system, using the CPU, and storing things on memory. This is also called a **process**. + +### What is a Process + +The word **process** is normally used in a more specific way, only referring to the thing that is running in the operating system (like in the last point above): + +* A particular program while it is **running** on the operating system. + * This doesn't refer to the file, nor to the code, it refers **specifically** to the thing that is being **executed** and managed by the operating system. +* Any program, any code, **can only do things** when it is being **executed**. So, when there's a **process running**. +* The process can be **terminated** (or "killed") by you, or by the operating system. At that point, it stops running/being executed, and it can **no longer do things**. +* Each application that you have running on your computer has some process behind it, each running program, each window, etc. And there are normally many processes running **at the same time** while a computer is on. +* There can be **multiple processes** of the **same program** running at the same time. + +If you check out the "task manager" or "system monitor" (or similar tools) in your operating system, you will be able to see many of those processes running. + +And, for example, you will probably see that there are multiple processes running the same browser program (Firefox, Chrome, Edge, etc). They normally run one process per tab, plus some other extra processes. + + + +--- + +Now that we know the difference between the terms **process** and **program**, let's continue talking about deployments. + +## Running on Startup + +In most cases, when you create a web API, you want it to be **always running**, uninterrupted, so that your clients can always access it. This is of course, unless you have a specific reason why you want it to run only in certain situations, but most of the time you want it constantly running and **available**. + +### In a Remote Server + +When you set up a remote server (a cloud server, a virtual machine, etc.) the simplest thing you can do is to run Uvicorn (or similar) manually, the same way you do when developing locally. + +And it will work and will be useful **during development**. + +But if your connection to the server is lost, the **running process** will probably die. + +And if the server is restarted (for example after updates, or migrations from the cloud provider) you probably **won't notice it**. And because of that, you won't even know that you have to restart the process manually. So, your API will just stay dead. 😱 + +### Run Automatically on Startup + +In general, you will probably want the server program (e.g. Uvicorn) to be started automatically on server startup, and without needing any **human intervention**, to have a process always running with your API (e.g. Uvicorn running your FastAPI app). + +### Separate Program + +To achieve this, you will normally have a **separate program** that would make sure your application is run on startup. And in many cases, it would also make sure other components or applications are also run, for example, a database. + +### Example Tools to Run at Startup + +Some examples of the tools that can do this job are: + +* Docker +* Kubernetes +* Docker Compose +* Docker in Swarm Mode +* Systemd +* Supervisor +* Handled internally by a cloud provider as part of their services +* Others... + +I'll give you more concrete examples in the next chapters. + +## Restarts + +Similar to making sure your application is run on startup, you probably also want to make sure it is **restarted** after failures. + +### We Make Mistakes + +We, as humans, make **mistakes**, all the time. Software almost *always* has **bugs** hidden in different places. 🐛 + +And we as developers keep improving the code as we find those bugs and as we implement new features (possibly adding new bugs too 😅). + +### Small Errors Automatically Handled + +When building web APIs with FastAPI, if there's an error in our code, FastAPI will normally contain it to the single request that triggered the error. 🛡 + +The client will get a **500 Internal Server Error** for that request, but the application will continue working for the next requests instead of just crashing completely. + +### Bigger Errors - Crashes + +Nevertheless, there might be cases where we write some code that **crashes the entire application** making Uvicorn and Python crash. 💥 + +And still, you would probably not want the application to stay dead because there was an error in one place, you probably want it to **continue running** at least for the *path operations* that are not broken. + +### Restart After Crash + +But in those cases with really bad errors that crash the running **process**, you would want an external component that is in charge of **restarting** the process, at least a couple of times... + +!!! tip + ...Although if the whole application is just **crashing immediately** it probably doesn't make sense to keep restarting it forever. But in those cases, you will probably notice it during development, or at least right after deployment. + + So let's focus on the main cases, where it could crash entirely in some particular cases **in the future**, and it still makes sense to restart it. + +You would probably want to have the thing in charge of restarting your application as an **external component**, because by that point, the same application with Uvicorn and Python already crashed, so there's nothing in the same code of the same app that could do anything about it. + +### Example Tools to Restart Automatically + +In most cases, the same tool that is used to **run the program on startup** is also used to handle automatic **restarts**. + +For example, this could be handled by: + +* Docker +* Kubernetes +* Docker Compose +* Docker in Swarm Mode +* Systemd +* Supervisor +* Handled internally by a cloud provider as part of their services +* Others... + +## Replication - Processes and Memory + +With a FastAPI application, using a server program like Uvicorn, running it once in **one process** can serve multiple clients concurrently. + +But in many cases, you will want to run several worker processes at the same time. + +### Multiple Processes - Workers + +If you have more clients than what a single process can handle (for example if the virtual machine is not too big) and you have **multiple cores** in the server's CPU, then you could have **multiple processes** running with the same application at the same time, and distribute all the requests among them. + +When you run **multiple processes** of the same API program, they are commonly called **workers**. + +### Worker Processes and Ports + +Remember from the docs [About HTTPS](./https.md){.internal-link target=_blank} that only one process can be listening on one combination of port and IP address in a server? + +This is still true. + +So, to be able to have **multiple processes** at the same time, there has to be a **single process listening on a port** that then transmits the communication to each worker process in some way. + +### Memory per Process + +Now, when the program loads things in memory, for example, a machine learning model in a variable, or the contents of a large file in a variable, all that **consumes a bit of the memory (RAM)** of the server. + +And multiple processes normally **don't share any memory**. This means that each running process has its own things, variables, and memory. And if you are consuming a large amount of memory in your code, **each process** will consume an equivalent amount of memory. + +### Server Memory + +For example, if your code loads a Machine Learning model with **1 GB in size**, when you run one process with your API, it will consume at least 1 GB of RAM. And if you start **4 processes** (4 workers), each will consume 1 GB of RAM. So in total, your API will consume **4 GB of RAM**. + +And if your remote server or virtual machine only has 3 GB of RAM, trying to load more than 4 GB of RAM will cause problems. 🚨 + +### Multiple Processes - An Example + +In this example, there's a **Manager Process** that starts and controls two **Worker Processes**. + +This Manager Process would probably be the one listening on the **port** in the IP. And it would transmit all the communication to the worker processes. + +Those worker processes would be the ones running your application, they would perform the main computations to receive a **request** and return a **response**, and they would load anything you put in variables in RAM. + + + +And of course, the same machine would probably have **other processes** running as well, apart from your application. + +An interesting detail is that the percentage of the **CPU used** by each process can **vary** a lot over time, but the **memory (RAM)** normally stays more or less **stable**. + +If you have an API that does a comparable amount of computations every time and you have a lot of clients, then the **CPU utilization** will probably *also be stable* (instead of constantly going up and down quickly). + +### Examples of Replication Tools and Strategies + +There can be several approaches to achieve this, and I'll tell you more about specific strategies in the next chapters, for example when talking about Docker and containers. + +The main constraint to consider is that there has to be a **single** component handling the **port** in the **public IP**. And then it has to have a way to **transmit** the communication to the replicated **processes/workers**. + +Here are some possible combinations and strategies: + +* **Gunicorn** managing **Uvicorn workers** + * Gunicorn would be the **process manager** listening on the **IP** and **port**, the replication would be by having **multiple Uvicorn worker processes** +* **Uvicorn** managing **Uvicorn workers** + * One Uvicorn **process manager** would listen on the **IP** and **port**, and it would start **multiple Uvicorn worker processes** +* **Kubernetes** and other distributed **container systems** + * Something in the **Kubernetes** layer would listen on the **IP** and **port**. The replication would be by having **multiple containers**, each with **one Uvicorn process** running +* **Cloud services** that handle this for you + * The cloud service will probably **handle replication for you**. It would possibly let you define **a process to run**, or a **container image** to use, in any case, it would most probably be **a single Uvicorn process**, and the cloud service would be in charge of replicating it. + +!!! tip + Don't worry if some of these items about **containers**, Docker, or Kubernetes don't make a lot of sense yet. + + I'll tell you more about container images, Docker, Kubernetes, etc. in a future chapter: [FastAPI in Containers - Docker](./docker.md){.internal-link target=_blank}. + +## Previous Steps Before Starting + +There are many cases where you want to perform some steps **before starting** your application. + +For example, you might want to run **database migrations**. + +But in most cases, you will want to perform these steps only **once**. + +So, you will want to have a **single process** to perform those **previous steps**, before starting the application. + +And you will have to make sure that it's a single process running those previous steps *even* if afterwards, you start **multiple processes** (multiple workers) for the application itself. If those steps were run by **multiple processes**, they would **duplicate** the work by running it on **parallel**, and if the steps were something delicate like a database migration, they could cause conflicts with each other. + +Of course, there are some cases where there's no problem in running the previous steps multiple times, in that case, it's a lot easier to handle. + +!!! tip + Also, have in mind that depending on your setup, in some cases you **might not even need any previous steps** before starting your application. + + In that case, you wouldn't have to worry about any of this. 🤷 + +### Examples of Previous Steps Strategies + +This will **depend heavily** on the way you **deploy your system**, and it would probably be connected to the way you start programs, handling restarts, etc. + +Here are some possible ideas: + +* An "Init Container" in Kubernetes that runs before your app container +* A bash script that runs the previous steps and then starts your application + * You would still need a way to start/restart *that* bash script, detect errors, etc. + +!!! tip + I'll give you more concrete examples for doing this with containers in a future chapter: [FastAPI in Containers - Docker](./docker.md){.internal-link target=_blank}. + +## Resource Utilization + +Your server(s) is (are) a **resource**, you can consume or **utilize**, with your programs, the computation time on the CPUs, and the RAM memory available. + +How much of the system resources do you want to be consuming/utilizing? It might be easy to think "not much", but in reality, you will probably want to consume **as much as possible without crashing**. + +If you are paying for 3 servers but you are using only a little bit of their RAM and CPU, you are probably **wasting money** 💸, and probably **wasting server electric power** 🌎, etc. + +In that case, it could be better to have only 2 servers and use a higher percentage of their resources (CPU, memory, disk, network bandwidth, etc). + +On the other hand, if you have 2 servers and you are using **100% of their CPU and RAM**, at some point one process will ask for more memory, and the server will have to use the disk as "memory" (which can be thousands of times slower), or even **crash**. Or one process might need to do some computation and would have to wait until the CPU is free again. + +In this case, it would be better to get **one extra server** and run some processes on it so that they all have **enough RAM and CPU time**. + +There's also the chance that for some reason you have a **spike** of usage of your API. Maybe it went viral, or maybe some other services or bots start using it. And you might want to have extra resources to be safe in those cases. + +You could put an **arbitrary number** to target, for example, something **between 50% to 90%** of resource utilization. The point is that those are probably the main things you will want to measure and use to tweak your deployments. + +You can use simple tools like `htop` to see the CPU and RAM used in your server or the amount used by each process. Or you can use more complex monitoring tools, which may be distributed across servers, etc. + +## Recap + +You have been reading here some of the main concepts that you would probably need to have in mind when deciding how to deploy your application: + +* Security - HTTPS +* Running on startup +* Restarts +* Replication (the number of processes running) +* Memory +* Previous steps before starting + +Understanding these ideas and how to apply them should give you the intuition necessary to take any decisions when configuring and tweaking your deployments. 🤓 + +In the next sections, I'll give you more concrete examples of possible strategies you can follow. 🚀 From dcb1e51047fea0b9ab916561642fece2110a05f3 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:06 +0800 Subject: [PATCH 044/163] New translations deta.md (Chinese Simplified) --- docs/zh/docs/deployment/deta.md | 393 ++++++++++++++++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 docs/zh/docs/deployment/deta.md diff --git a/docs/zh/docs/deployment/deta.md b/docs/zh/docs/deployment/deta.md new file mode 100644 index 0000000000000..6b1b4690ecc8d --- /dev/null +++ b/docs/zh/docs/deployment/deta.md @@ -0,0 +1,393 @@ +# Deploy FastAPI on Deta Space + +In this section you will learn how to easily deploy a **FastAPI** application on Deta Space, for free. 🎁 + +It will take you about **10 minutes** to deploy an API that you can use. After that, you can optionally release it to anyone. + +Let's dive in. + +!!! info + Deta is a **FastAPI** sponsor. 🎉 + +## A simple **FastAPI** app + +* To start, create an empty directory with the name of your app, for example `./fastapi-deta/`, and then navigate into it. + +```console +$ mkdir fastapi-deta +$ cd fastapi-deta +``` + +### FastAPI code + +* Create a `main.py` file 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): + return {"item_id": item_id} +``` + +### Requirements + +Now, in the same directory create a file `requirements.txt` with: + +```text +fastapi +uvicorn[standard] +``` + +### Directory structure + +You will now have a directory `./fastapi-deta/` with two files: + +``` +. +└── main.py +└── requirements.txt +``` + +## Create a free **Deta Space** account + +Next, create a free account on Deta Space, you just need an email and password. + +You don't even need a credit card, but make sure **Developer Mode** is enabled when you sign up. + + +## Install the CLI + +Once you have your account, install the Deta Space CLI: + +=== "Linux, macOS" + +
+ + ```console + $ curl -fsSL https://get.deta.dev/space-cli.sh | sh + ``` + + +
+ +=== "Windows PowerShell" + +
+ + ```console + $ iwr https://get.deta.dev/space-cli.ps1 -useb | iex + ``` + + +
+ +After installing it, open a new terminal so that the installed CLI is detected. + +In a new terminal, confirm that it was correctly installed with: + +
+ +```console +$ space --help + +Deta command line interface for managing deta micros. +Complete documentation available at https://deta.space/docs + +Usage: + space [flags] + space [command] + +Available Commands: + help Help about any command + link link code to project + login login to space + new create new project + push push code for project + release create release for a project + validate validate spacefile in dir + version Space CLI version +... +``` + +
+ +!!! tip + If you have problems installing the CLI, check the official Deta Space Documentation. + +## Login with the CLI + +In order to authenticate your CLI with Deta Space, you will need an access token. + +To obtain this token, open your Deta Space Canvas, open the **Teletype** (command bar at the bottom of the Canvas), and then click on **Settings**. From there, select **Generate Token** and copy the resulting token. + + + +Now run `space login` from the Space CLI. Upon pasting the token into the CLI prompt and pressing enter, you should see a confirmation message. + +
+ +```console +$ space login + +To authenticate the Space CLI with your Space account, generate a new access token in your Space settings and paste it below: + +# Enter access token (41 chars) >$ ***************************************** + +👍 Login Successful! +``` + +
+ +## Create a new project in Space + +Now that you've authenticated with the Space CLI, use it to create a new Space Project: + +```console +$ space new + +# What is your project's name? >$ fastapi-deta +``` + +The Space CLI will ask you to name the project, we will call ours `fastapi-deta`. + +Then, it will try to automatically detect which framework or language you are using, showing you what it finds. In our case it will identify the Python app with the following message, prompting you to confirm: + +```console +⚙️ No Spacefile found, trying to auto-detect configuration ... +👇 Deta detected the following configuration: + +Micros: +name: fastapi-deta + L src: . + L engine: python3.9 + +# Do you want to bootstrap "fastapi-deta" with this configuration? (y/n)$ y +``` + +After you confirm, your project will be created in Deta Space inside a special app called Builder. Builder is a toolbox that helps you to create and manage your apps in Deta Space. + +The CLI will also create a `Spacefile` locally in the `fastapi-deta` directory. The Spacefile is a configuration file which tells Deta Space how to run your app. The `Spacefile` for your app will be as follows: + +```yaml +v: 0 +micros: + - name: fastapi-deta + src: . + engine: python3.9 +``` + +It is a `yaml` file, and you can use it to add features like scheduled tasks or modify how your app functions, which we'll do later. To learn more, read the `Spacefile` documentation. + +!!! tip + The Space CLI will also create a hidden `.space` folder in your local directory to link your local environment with Deta Space. This folder should not be included in your version control and will automatically be added to your `.gitignore` file, if you have initialized a Git repository. + +## Define the run command in the Spacefile + +The `run` command in the Spacefile tells Space what command should be executed to start your app. In this case it would be `uvicorn main:app`. + +```diff +v: 0 +micros: + - name: fastapi-deta + src: . + engine: python3.9 ++ run: uvicorn main:app +``` + +## Deploy to Deta Space + +To get your FastAPI live in the cloud, use one more CLI command: + +
+ +```console +$ space push + +---> 100% + +build complete... created revision: satyr-jvjk + +✔ Successfully pushed your code and created a new Revision! +ℹ Updating your development instance with the latest Revision, it will be available on your Canvas shortly. +``` +
+ +This command will package your code, upload all the necessary files to Deta Space, and run a remote build of your app, resulting in a **revision**. Whenever you run `space push` successfully, a live instance of your API is automatically updated with the latest revision. + +!!! tip + You can manage your revisions by opening your project in the Builder app. The live copy of your API will be visible under the **Develop** tab in Builder. + +## Check it + +The live instance of your API will also be added automatically to your Canvas (the dashboard) on Deta Space. + + + +Click on the new app called `fastapi-deta`, and it will open your API in a new browser tab on a URL like `https://fastapi-deta-gj7ka8.deta.app/`. + +You will get a JSON response from your FastAPI app: + +```JSON +{ + "Hello": "World" +} +``` + +And now you can head over to the `/docs` of your API. For this example, it would be `https://fastapi-deta-gj7ka8.deta.app/docs`. + + + +## Enable public access + +Deta will handle authentication for your account using cookies. By default, every app or API that you `push` or install to your Space is personal - it's only accessible to you. + +But you can also make your API public using the `Spacefile` from earlier. + +With a `public_routes` parameter, you can specify which paths of your API should be available to the public. + +Set your `public_routes` to `"*"` to open every route of your API to the public: + +```yaml +v: 0 +micros: + - name: fastapi-deta + src: . + engine: python3.9 + public_routes: + - "/*" +``` + +Then run `space push` again to update your live API on Deta Space. + +Once it deploys, you can share your URL with anyone and they will be able to access your API. 🚀 + +## HTTPS + +Congrats! You deployed your FastAPI app to Deta Space! 🎉 🍰 + +Also, notice that Deta Space correctly handles HTTPS for you, so you don't have to take care of that and can be sure that your users will have a secure encrypted connection. ✅ 🔒 + +## Create a release + +Space also allows you to publish your API. When you publish it, anyone else can install their own copy of your API, in their own Deta Space cloud. + +To do so, run `space release` in the Space CLI to create an **unlisted release**: + +
+ +```console +$ space release + +# Do you want to use the latest revision (buzzard-hczt)? (y/n)$ y + +~ Creating a Release with the latest Revision + +---> 100% + +creating release... +publishing release in edge locations.. +completed... +released: fastapi-deta-exp-msbu +https://deta.space/discovery/r/5kjhgyxewkdmtotx + + Lift off -- successfully created a new Release! + Your Release is available globally on 5 Deta Edges + Anyone can install their own copy of your app. +``` +
+ +This command publishes your revision as a release and gives you a link. Anyone you give this link to can install your API. + + +You can also make your app publicly discoverable by creating a **listed release** with `space release --listed` in the Space CLI: + +
+ +```console +$ space release --listed + +# Do you want to use the latest revision (buzzard-hczt)? (y/n)$ y + +~ Creating a listed Release with the latest Revision ... + +creating release... +publishing release in edge locations.. +completed... +released: fastapi-deta-exp-msbu +https://deta.space/discovery/@user/fastapi-deta + + Lift off -- successfully created a new Release! + Your Release is available globally on 5 Deta Edges + Anyone can install their own copy of your app. + Listed on Discovery for others to find! +``` +
+ +This will allow anyone to find and install your app via Deta Discovery. Read more about releasing your app in the docs. + +## Check runtime logs + +Deta Space also lets you inspect the logs of every app you build or install. + +Add some logging functionality to your app by adding a `print` statement to your `main.py` file. + +```py +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): + print(item_id) + return {"item_id": item_id} +``` + +The code within the `read_item` function includes a print statement that will output the `item_id` that is included in the URL. Send a request to your _path operation_ `/items/{item_id}` from the docs UI (which will have a URL like `https://fastapi-deta-gj7ka8.deta.app/docs`), using an ID like `5` as an example. + +Now go to your Space's Canvas. Click on the context menu (`...`) of your live app instance, and then click on **View Logs**. Here you can view your app's logs, sorted by time. + + + +## Learn more + +At some point, you will probably want to store some data for your app in a way that persists through time. For that you can use Deta Base and Deta Drive, both of which have a generous **free tier**. + +You can also read more in the Deta Space Documentation. + +!!! tip + If you have any Deta related questions, comments, or feedback, head to the Deta Discord server. + + +## Deployment Concepts + +Coming back to the concepts we discussed in [Deployments Concepts](./concepts.md){.internal-link target=_blank}, here's how each of them would be handled with Deta Space: + +- **HTTPS**: Handled by Deta Space, they will give you a subdomain and handle HTTPS automatically. +- **Running on startup**: Handled by Deta Space, as part of their service. +- **Restarts**: Handled by Deta Space, as part of their service. +- **Replication**: Handled by Deta Space, as part of their service. +- **Authentication**: Handled by Deta Space, as part of their service. +- **Memory**: Limit predefined by Deta Space, you could contact them to increase it. +- **Previous steps before starting**: Can be configured using the `Spacefile`. + +!!! note + Deta Space is designed to make it easy and free to build cloud applications for yourself. Then you can optionally share them with anyone. + + It can simplify several use cases, but at the same time, it doesn't support others, like using external databases (apart from Deta's own NoSQL database system), custom virtual machines, etc. + + You can read more details in the Deta Space Documentation to see if it's the right choice for you. From a85f630a855b891070a777b66abcf3ead7935bc4 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:07 +0800 Subject: [PATCH 045/163] New translations docker.md (Chinese Simplified) --- docs/zh/docs/deployment/docker.md | 698 ++++++++++++++++++++++++++++++ 1 file changed, 698 insertions(+) create mode 100644 docs/zh/docs/deployment/docker.md diff --git a/docs/zh/docs/deployment/docker.md b/docs/zh/docs/deployment/docker.md new file mode 100644 index 0000000000000..fac4650a2626e --- /dev/null +++ b/docs/zh/docs/deployment/docker.md @@ -0,0 +1,698 @@ +# FastAPI in Containers - Docker + +When deploying FastAPI applications a common approach is to build a **Linux container image**. It's normally done using **Docker**. You can then deploy that container image in one of a few possible ways. + +Using Linux containers has several advantages including **security**, **replicability**, **simplicity**, and others. + +!!! tip + In a hurry and already know this stuff? Jump to the [`Dockerfile` below 👇](#build-a-docker-image-for-fastapi). + +
+Dockerfile Preview 👀 + +```Dockerfile +FROM python:3.9 + +WORKDIR /code + +COPY ./requirements.txt /code/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +COPY ./app /code/app + +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] + +# If running behind a proxy like Nginx or Traefik add --proxy-headers +# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--proxy-headers"] +``` + +
+ +## What is a Container + +Containers (mainly Linux containers) are a very **lightweight** way to package applications including all their dependencies and necessary files while keeping them isolated from other containers (other applications or components) in the same system. + +Linux containers run using the same Linux kernel of the host (machine, virtual machine, cloud server, etc). This just means that they are very lightweight (compared to full virtual machines emulating an entire operating system). + +This way, containers consume **little resources**, an amount comparable to running the processes directly (a virtual machine would consume much more). + +Containers also have their own **isolated** running processes (commonly just one process), file system, and network, simplifying deployment, security, development, etc. + +## What is a Container Image + +A **container** is run from a **container image**. + +A container image is a **static** version of all the files, environment variables, and the default command/program that should be present in a container. **Static** here means that the container **image** is not running, it's not being executed, it's only the packaged files and metadata. + +In contrast to a "**container image**" that is the stored static contents, a "**container**" normally refers to the running instance, the thing that is being **executed**. + +When the **container** is started and running (started from a **container image**) it could create or change files, environment variables, etc. Those changes will exist only in that container, but would not persist in the underlying container image (would not be saved to disk). + +A container image is comparable to the **program** file and contents, e.g. `python` and some file `main.py`. + +And the **container** itself (in contrast to the **container image**) is the actual running instance of the image, comparable to a **process**. In fact, a container is running only when it has a **process running** (and normally it's only a single process). The container stops when there's no process running in it. + +## Container Images + +Docker has been one of the main tools to create and manage **container images** and **containers**. + +And there's a public Docker Hub with pre-made **official container images** for many tools, environments, databases, and applications. + +For example, there's an official Python Image. + +And there are many other images for different things like databases, for example for: + +* PostgreSQL +* MySQL +* MongoDB +* Redis, etc. + +By using a pre-made container image it's very easy to **combine** and use different tools. For example, to try out a new database. In most cases, you can use the **official images**, and just configure them with environment variables. + +That way, in many cases you can learn about containers and Docker and re-use that knowledge with many different tools and components. + +So, you would run **multiple containers** with different things, like a database, a Python application, a web server with a React frontend application, and connect them together via their internal network. + +All the container management systems (like Docker or Kubernetes) have these networking features integrated into them. + +## Containers and Processes + +A **container image** normally includes in its metadata the default program or command that should be run when the **container** is started and the parameters to be passed to that program. Very similar to what would be if it was in the command line. + +When a **container** is started, it will run that command/program (although you can override it and make it run a different command/program). + +A container is running as long as the **main process** (command or program) is running. + +A container normally has a **single process**, but it's also possible to start subprocesses from the main process, and that way you will have **multiple processes** in the same container. + +But it's not possible to have a running container without **at least one running process**. If the main process stops, the container stops. + +## Build a Docker Image for FastAPI + +Okay, let's build something now! 🚀 + +I'll show you how to build a **Docker image** for FastAPI **from scratch**, based on the **official Python** image. + +This is what you would want to do in **most cases**, for example: + +* Using **Kubernetes** or similar tools +* When running on a **Raspberry Pi** +* Using a cloud service that would run a container image for you, etc. + +### Package Requirements + +You would normally have the **package requirements** for your application in some file. + +It would depend mainly on the tool you use to **install** those requirements. + +The most common way to do it is to have a file `requirements.txt` with the package names and their versions, one per line. + +You would of course use the same ideas you read in [About FastAPI versions](./versions.md){.internal-link target=_blank} to set the ranges of versions. + +For example, your `requirements.txt` could look like: + +``` +fastapi>=0.68.0,<0.69.0 +pydantic>=1.8.0,<2.0.0 +uvicorn>=0.15.0,<0.16.0 +``` + +And you would normally install those package dependencies with `pip`, for example: + +
+ +```console +$ pip install -r requirements.txt +---> 100% +Successfully installed fastapi pydantic uvicorn +``` + +
+ +!!! info + There are other formats and tools to define and install package dependencies. + + I'll show you an example using Poetry later in a section below. 👇 + +### Create the **FastAPI** Code + +* Create an `app` directory and enter it. +* Create an empty file `__init__.py`. +* Create a `main.py` file with: + +```Python +from typing import Union + +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: Union[str, None] = None): + return {"item_id": item_id, "q": q} +``` + +### Dockerfile + +Now in the same project directory create a file `Dockerfile` with: + +```{ .dockerfile .annotate } +# (1) +FROM python:3.9 + +# (2) +WORKDIR /code + +# (3) +COPY ./requirements.txt /code/requirements.txt + +# (4) +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +# (5) +COPY ./app /code/app + +# (6) +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] +``` + +1. Start from the official Python base image. + +2. Set the current working directory to `/code`. + + This is where we'll put the `requirements.txt` file and the `app` directory. + +3. Copy the file with the requirements to the `/code` directory. + + Copy **only** the file with the requirements first, not the rest of the code. + + As this file **doesn't change often**, Docker will detect it and use the **cache** for this step, enabling the cache for the next step too. + +4. Install the package dependencies in the requirements file. + + The `--no-cache-dir` option tells `pip` to not save the downloaded packages locally, as that is only if `pip` was going to be run again to install the same packages, but that's not the case when working with containers. + + !!! note + The `--no-cache-dir` is only related to `pip`, it has nothing to do with Docker or containers. + + The `--upgrade` option tells `pip` to upgrade the packages if they are already installed. + + Because the previous step copying the file could be detected by the **Docker cache**, this step will also **use the Docker cache** when available. + + Using the cache in this step will **save** you a lot of **time** when building the image again and again during development, instead of **downloading and installing** all the dependencies **every time**. + +5. Copy the `./app` directory inside the `/code` directory. + + As this has all the code which is what **changes most frequently** the Docker **cache** won't be used for this or any **following steps** easily. + + So, it's important to put this **near the end** of the `Dockerfile`, to optimize the container image build times. + +6. Set the **command** to run the `uvicorn` server. + + `CMD` takes a list of strings, each of these strings is what you would type in the command line separated by spaces. + + This command will be run from the **current working directory**, the same `/code` directory you set above with `WORKDIR /code`. + + Because the program will be started at `/code` and inside of it is the directory `./app` with your code, **Uvicorn** will be able to see and **import** `app` from `app.main`. + +!!! tip + Review what each line does by clicking each number bubble in the code. 👆 + +You should now have a directory structure like: + +``` +. +├── app +│   ├── __init__.py +│ └── main.py +├── Dockerfile +└── requirements.txt +``` + +#### Behind a TLS Termination Proxy + +If you are running your container behind a TLS Termination Proxy (load balancer) like Nginx or Traefik, add the option `--proxy-headers`, this will tell Uvicorn to trust the headers sent by that proxy telling it that the application is running behind HTTPS, etc. + +```Dockerfile +CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"] +``` + +#### Docker Cache + +There's an important trick in this `Dockerfile`, we first copy the **file with the dependencies alone**, not the rest of the code. Let me tell you why is that. + +```Dockerfile +COPY ./requirements.txt /code/requirements.txt +``` + +Docker and other tools **build** these container images **incrementally**, adding **one layer on top of the other**, starting from the top of the `Dockerfile` and adding any files created by each of the instructions of the `Dockerfile`. + +Docker and similar tools also use an **internal cache** when building the image, if a file hasn't changed since the last time building the container image, then it will **re-use the same layer** created the last time, instead of copying the file again and creating a new layer from scratch. + +Just avoiding the copy of files doesn't necessarily improve things too much, but because it used the cache for that step, it can **use the cache for the next step**. For example, it could use the cache for the instruction that installs dependencies with: + +```Dockerfile +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt +``` + +The file with the package requirements **won't change frequently**. So, by copying only that file, Docker will be able to **use the cache** for that step. + +And then, Docker will be able to **use the cache for the next step** that downloads and install those dependencies. And here's where we **save a lot of time**. ✨ ...and avoid boredom waiting. 😪😆 + +Downloading and installing the package dependencies **could take minutes**, but using the **cache** would **take seconds** at most. + +And as you would be building the container image again and again during development to check that your code changes are working, there's a lot of accumulated time this would save. + +Then, near the end of the `Dockerfile`, we copy all the code. As this is what **changes most frequently**, we put it near the end, because almost always, anything after this step will not be able to use the cache. + +```Dockerfile +COPY ./app /code/app +``` + +### Build the Docker Image + +Now that all the files are in place, let's build the container image. + +* Go to the project directory (in where your `Dockerfile` is, containing your `app` directory). +* Build your FastAPI image: + +
+ +```console +$ docker build -t myimage . + +---> 100% +``` + +
+ +!!! tip + Notice the `.` at the end, it's equivalent to `./`, it tells Docker the directory to use to build the container image. + + In this case, it's the same current directory (`.`). + +### Start the Docker Container + +* Run a container based on your image: + +
+ +```console +$ docker run -d --name mycontainer -p 80:80 myimage +``` + +
+ +## Check it + +You should be able to check it in your Docker container's URL, for example: http://192.168.99.100/items/5?q=somequery or http://127.0.0.1/items/5?q=somequery (or equivalent, using your Docker host). + +You will see something like: + +```JSON +{"item_id": 5, "q": "somequery"} +``` + +## Interactive API docs + +Now you can go to http://192.168.99.100/docs or http://127.0.0.1/docs (or equivalent, using your Docker host). + +You will see the automatic interactive API documentation (provided by Swagger UI): + +![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) + +## Alternative API docs + +And you can also go to http://192.168.99.100/redoc or http://127.0.0.1/redoc (or equivalent, using your Docker host). + +You will see the alternative automatic documentation (provided by ReDoc): + +![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) + +## Build a Docker Image with a Single-File FastAPI + +If your FastAPI is a single file, for example, `main.py` without an `./app` directory, your file structure could look like this: + +``` +. +├── Dockerfile +├── main.py +└── requirements.txt +``` + +Then you would just have to change the corresponding paths to copy the file inside the `Dockerfile`: + +```{ .dockerfile .annotate hl_lines="10 13" } +FROM python:3.9 + +WORKDIR /code + +COPY ./requirements.txt /code/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +# (1) +COPY ./main.py /code/ + +# (2) +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"] +``` + +1. Copy the `main.py` file to the `/code` directory directly (without any `./app` directory). + +2. Run Uvicorn and tell it to import the `app` object from `main` (instead of importing from `app.main`). + +Then adjust the Uvicorn command to use the new module `main` instead of `app.main` to import the FastAPI object `app`. + +## Deployment Concepts + +Let's talk again about some of the same [Deployment Concepts](./concepts.md){.internal-link target=_blank} in terms of containers. + +Containers are mainly a tool to simplify the process of **building and deploying** an application, but they don't enforce a particular approach to handle these **deployment concepts**, and there are several possible strategies. + +The **good news** is that with each different strategy there's a way to cover all of the deployment concepts. 🎉 + +Let's review these **deployment concepts** in terms of containers: + +* HTTPS +* Running on startup +* Restarts +* Replication (the number of processes running) +* Memory +* Previous steps before starting + +## HTTPS + +If we focus just on the **container image** for a FastAPI application (and later the running **container**), HTTPS normally would be handled **externally** by another tool. + +It could be another container, for example with Traefik, handling **HTTPS** and **automatic** acquisition of **certificates**. + +!!! tip + Traefik has integrations with Docker, Kubernetes, and others, so it's very easy to set up and configure HTTPS for your containers with it. + +Alternatively, HTTPS could be handled by a cloud provider as one of their services (while still running the application in a container). + +## Running on Startup and Restarts + +There is normally another tool in charge of **starting and running** your container. + +It could be **Docker** directly, **Docker Compose**, **Kubernetes**, a **cloud service**, etc. + +In most (or all) cases, there's a simple option to enable running the container on startup and enabling restarts on failures. For example, in Docker, it's the command line option `--restart`. + +Without using containers, making applications run on startup and with restarts can be cumbersome and difficult. But when **working with containers** in most cases that functionality is included by default. ✨ + +## Replication - Number of Processes + +If you have a cluster of machines with **Kubernetes**, Docker Swarm Mode, Nomad, or another similar complex system to manage distributed containers on multiple machines, then you will probably want to **handle replication** at the **cluster level** instead of using a **process manager** (like Gunicorn with workers) in each container. + +One of those distributed container management systems like Kubernetes normally has some integrated way of handling **replication of containers** while still supporting **load balancing** for the incoming requests. All at the **cluster level**. + +In those cases, you would probably want to build a **Docker image from scratch** as [explained above](#dockerfile), installing your dependencies, and running **a single Uvicorn process** instead of running something like Gunicorn with Uvicorn workers. + +### Load Balancer + +When using containers, you would normally have some component **listening on the main port**. It could possibly be another container that is also a **TLS Termination Proxy** to handle **HTTPS** or some similar tool. + +As this component would take the **load** of requests and distribute that among the workers in a (hopefully) **balanced** way, it is also commonly called a **Load Balancer**. + +!!! tip + The same **TLS Termination Proxy** component used for HTTPS would probably also be a **Load Balancer**. + +And when working with containers, the same system you use to start and manage them would already have internal tools to transmit the **network communication** (e.g. HTTP requests) from that **load balancer** (that could also be a **TLS Termination Proxy**) to the container(s) with your app. + +### One Load Balancer - Multiple Worker Containers + +When working with **Kubernetes** or similar distributed container management systems, using their internal networking mechanisms would allow the single **load balancer** that is listening on the main **port** to transmit communication (requests) to possibly **multiple containers** running your app. + +Each of these containers running your app would normally have **just one process** (e.g. a Uvicorn process running your FastAPI application). They would all be **identical containers**, running the same thing, but each with its own process, memory, etc. That way you would take advantage of **parallelization** in **different cores** of the CPU, or even in **different machines**. + +And the distributed container system with the **load balancer** would **distribute the requests** to each one of the containers with your app **in turns**. So, each request could be handled by one of the multiple **replicated containers** running your app. + +And normally this **load balancer** would be able to handle requests that go to *other* apps in your cluster (e.g. to a different domain, or under a different URL path prefix), and would transmit that communication to the right containers for *that other* application running in your cluster. + +### One Process per Container + +In this type of scenario, you probably would want to have **a single (Uvicorn) process per container**, as you would already be handling replication at the cluster level. + +So, in this case, you **would not** want to have a process manager like Gunicorn with Uvicorn workers, or Uvicorn using its own Uvicorn workers. You would want to have just a **single Uvicorn process** per container (but probably multiple containers). + +Having another process manager inside the container (as would be with Gunicorn or Uvicorn managing Uvicorn workers) would only add **unnecessary complexity** that you are most probably already taking care of with your cluster system. + +### Containers with Multiple Processes and Special Cases + +Of course, there are **special cases** where you could want to have **a container** with a **Gunicorn process manager** starting several **Uvicorn worker processes** inside. + +In those cases, you can use the **official Docker image** that includes **Gunicorn** as a process manager running multiple **Uvicorn worker processes**, and some default settings to adjust the number of workers based on the current CPU cores automatically. I'll tell you more about it below in [Official Docker Image with Gunicorn - Uvicorn](#official-docker-image-with-gunicorn-uvicorn). + +Here are some examples of when that could make sense: + +#### A Simple App + +You could want a process manager in the container if your application is **simple enough** that you don't need (at least not yet) to fine-tune the number of processes too much, and you can just use an automated default (with the official Docker image), and you are running it on a **single server**, not a cluster. + +#### Docker Compose + +You could be deploying to a **single server** (not a cluster) with **Docker Compose**, so you wouldn't have an easy way to manage replication of containers (with Docker Compose) while preserving the shared network and **load balancing**. + +Then you could want to have **a single container** with a **process manager** starting **several worker processes** inside. + +#### Prometheus and Other Reasons + +You could also have **other reasons** that would make it easier to have a **single container** with **multiple processes** instead of having **multiple containers** with **a single process** in each of them. + +For example (depending on your setup) you could have some tool like a Prometheus exporter in the same container that should have access to **each of the requests** that come. + +In this case, if you had **multiple containers**, by default, when Prometheus came to **read the metrics**, it would get the ones for **a single container each time** (for the container that handled that particular request), instead of getting the **accumulated metrics** for all the replicated containers. + +Then, in that case, it could be simpler to have **one container** with **multiple processes**, and a local tool (e.g. a Prometheus exporter) on the same container collecting Prometheus metrics for all the internal processes and exposing those metrics on that single container. + +--- + +The main point is, **none** of these are **rules written in stone** that you have to blindly follow. You can use these ideas to **evaluate your own use case** and decide what is the best approach for your system, checking out how to manage the concepts of: + +* Security - HTTPS +* Running on startup +* Restarts +* Replication (the number of processes running) +* Memory +* Previous steps before starting + +## Memory + +If you run **a single process per container** you will have a more or less well-defined, stable, and limited amount of memory consumed by each of those containers (more than one if they are replicated). + +And then you can set those same memory limits and requirements in your configurations for your container management system (for example in **Kubernetes**). That way it will be able to **replicate the containers** in the **available machines** taking into account the amount of memory needed by them, and the amount available in the machines in the cluster. + +If your application is **simple**, this will probably **not be a problem**, and you might not need to specify hard memory limits. But if you are **using a lot of memory** (for example with **machine learning** models), you should check how much memory you are consuming and adjust the **number of containers** that runs in **each machine** (and maybe add more machines to your cluster). + +If you run **multiple processes per container** (for example with the official Docker image) you will have to make sure that the number of processes started doesn't **consume more memory** than what is available. + +## Previous Steps Before Starting and Containers + +If you are using containers (e.g. Docker, Kubernetes), then there are two main approaches you can use. + +### Multiple Containers + +If you have **multiple containers**, probably each one running a **single process** (for example, in a **Kubernetes** cluster), then you would probably want to have a **separate container** doing the work of the **previous steps** in a single container, running a single process, **before** running the replicated worker containers. + +!!! info + If you are using Kubernetes, this would probably be an Init Container. + +If in your use case there's no problem in running those previous steps **multiple times in parallel** (for example if you are not running database migrations, but just checking if the database is ready yet), then you could also just put them in each container right before starting the main process. + +### Single Container + +If you have a simple setup, with a **single container** that then starts multiple **worker processes** (or also just one process), then you could run those previous steps in the same container, right before starting the process with the app. The official Docker image supports this internally. + +## Official Docker Image with Gunicorn - Uvicorn + +There is an official Docker image that includes Gunicorn running with Uvicorn workers, as detailed in a previous chapter: [Server Workers - Gunicorn with Uvicorn](./server-workers.md){.internal-link target=_blank}. + +This image would be useful mainly in the situations described above in: [Containers with Multiple Processes and Special Cases](#containers-with-multiple-processes-and-special-cases). + +* tiangolo/uvicorn-gunicorn-fastapi. + +!!! warning + There's a high chance that you **don't** need this base image or any other similar one, and would be better off by building the image from scratch as [described above in: Build a Docker Image for FastAPI](#build-a-docker-image-for-fastapi). + +This image has an **auto-tuning** mechanism included to set the **number of worker processes** based on the CPU cores available. + +It has **sensible defaults**, but you can still change and update all the configurations with **environment variables** or configuration files. + +It also supports running **previous steps before starting** with a script. + +!!! tip + To see all the configurations and options, go to the Docker image page: tiangolo/uvicorn-gunicorn-fastapi. + +### Number of Processes on the Official Docker Image + +The **number of processes** on this image is **computed automatically** from the CPU **cores** available. + +This means that it will try to **squeeze** as much **performance** from the CPU as possible. + +You can also adjust it with the configurations using **environment variables**, etc. + +But it also means that as the number of processes depends on the CPU the container is running, the **amount of memory consumed** will also depend on that. + +So, if your application consumes a lot of memory (for example with machine learning models), and your server has a lot of CPU cores **but little memory**, then your container could end up trying to use more memory than what is available, and degrading performance a lot (or even crashing). 🚨 + +### Create a `Dockerfile` + +Here's how you would create a `Dockerfile` based on this image: + +```Dockerfile +FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9 + +COPY ./requirements.txt /app/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt + +COPY ./app /app +``` + +### Bigger Applications + +If you followed the section about creating [Bigger Applications with Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}, your `Dockerfile` might instead look like: + +```Dockerfile hl_lines="7" +FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9 + +COPY ./requirements.txt /app/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt + +COPY ./app /app/app +``` + +### When to Use + +You should probably **not** use this official base image (or any other similar one) if you are using **Kubernetes** (or others) and you are already setting **replication** at the cluster level, with multiple **containers**. In those cases, you are better off **building an image from scratch** as described above: [Build a Docker Image for FastAPI](#build-a-docker-image-for-fastapi). + +This image would be useful mainly in the special cases described above in [Containers with Multiple Processes and Special Cases](#containers-with-multiple-processes-and-special-cases). For example, if your application is **simple enough** that setting a default number of processes based on the CPU works well, you don't want to bother with manually configuring the replication at the cluster level, and you are not running more than one container with your app. Or if you are deploying with **Docker Compose**, running on a single server, etc. + +## Deploy the Container Image + +After having a Container (Docker) Image there are several ways to deploy it. + +For example: + +* With **Docker Compose** in a single server +* With a **Kubernetes** cluster +* With a Docker Swarm Mode cluster +* With another tool like Nomad +* With a cloud service that takes your container image and deploys it + +## Docker Image with Poetry + +If you use Poetry to manage your project's dependencies, you could use Docker multi-stage building: + +```{ .dockerfile .annotate } +# (1) +FROM python:3.9 as requirements-stage + +# (2) +WORKDIR /tmp + +# (3) +RUN pip install poetry + +# (4) +COPY ./pyproject.toml ./poetry.lock* /tmp/ + +# (5) +RUN poetry export -f requirements.txt --output requirements.txt --without-hashes + +# (6) +FROM python:3.9 + +# (7) +WORKDIR /code + +# (8) +COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt + +# (9) +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +# (10) +COPY ./app /code/app + +# (11) +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] +``` + +1. This is the first stage, it is named `requirements-stage`. + +2. Set `/tmp` as the current working directory. + + Here's where we will generate the file `requirements.txt` + +3. Install Poetry in this Docker stage. + +4. Copy the `pyproject.toml` and `poetry.lock` files to the `/tmp` directory. + + Because it uses `./poetry.lock*` (ending with a `*`), it won't crash if that file is not available yet. + +5. Generate the `requirements.txt` file. + +6. This is the final stage, anything here will be preserved in the final container image. + +7. Set the current working directory to `/code`. + +8. Copy the `requirements.txt` file to the `/code` directory. + + This file only lives in the previous Docker stage, that's why we use `--from-requirements-stage` to copy it. + +9. Install the package dependencies in the generated `requirements.txt` file. + +10. Copy the `app` directory to the `/code` directory. + +11. Run the `uvicorn` command, telling it to use the `app` object imported from `app.main`. + +!!! tip + Click the bubble numbers to see what each line does. + +A **Docker stage** is a part of a `Dockerfile` that works as a **temporary container image** that is only used to generate some files to be used later. + +The first stage will only be used to **install Poetry** and to **generate the `requirements.txt`** with your project dependencies from Poetry's `pyproject.toml` file. + +This `requirements.txt` file will be used with `pip` later in the **next stage**. + +In the final container image **only the final stage** is preserved. The previous stage(s) will be discarded. + +When using Poetry, it would make sense to use **Docker multi-stage builds** because you don't really need to have Poetry and its dependencies installed in the final container image, you **only need** to have the generated `requirements.txt` file to install your project dependencies. + +Then in the next (and final) stage you would build the image more or less in the same way as described before. + +### Behind a TLS Termination Proxy - Poetry + +Again, if you are running your container behind a TLS Termination Proxy (load balancer) like Nginx or Traefik, add the option `--proxy-headers` to the command: + +```Dockerfile +CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"] +``` + +## Recap + +Using container systems (e.g. with **Docker** and **Kubernetes**) it becomes fairly straightforward to handle all the **deployment concepts**: + +* HTTPS +* Running on startup +* Restarts +* Replication (the number of processes running) +* Memory +* Previous steps before starting + +In most cases, you probably won't want to use any base image, and instead **build a container image from scratch** one based on the official Python Docker image. + +Taking care of the **order** of instructions in the `Dockerfile` and the **Docker cache** you can **minimize build times**, to maximize your productivity (and avoid boredom). 😎 + +In certain special cases, you might want to use the official Docker image for FastAPI. 🤓 From fce41ecfcdcacb7a63525ae4f697bf1d56cffcf1 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:08 +0800 Subject: [PATCH 046/163] New translations https.md (Chinese Simplified) --- docs/zh/docs/deployment/https.md | 190 +++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 docs/zh/docs/deployment/https.md diff --git a/docs/zh/docs/deployment/https.md b/docs/zh/docs/deployment/https.md new file mode 100644 index 0000000000000..75b7d85132831 --- /dev/null +++ b/docs/zh/docs/deployment/https.md @@ -0,0 +1,190 @@ +# About HTTPS + +It is easy to assume that HTTPS is something that is just "enabled" or not. + +But it is way more complex than that. + +!!! tip + If you are in a hurry or don't care, continue with the next sections for step by step instructions to set everything up with different techniques. + +To **learn the basics of HTTPS**, from a consumer perspective, check https://howhttps.works/. + +Now, from a **developer's perspective**, here are several things to have in mind while thinking about HTTPS: + +* For HTTPS, **the server** needs to **have "certificates"** generated by a **third party**. + * Those certificates are actually **acquired** from the third party, not "generated". +* Certificates have a **lifetime**. + * They **expire**. + * And then they need to be **renewed**, **acquired again** from the third party. +* The encryption of the connection happens at the **TCP level**. + * That's one layer **below HTTP**. + * So, the **certificate and encryption** handling is done **before HTTP**. +* **TCP doesn't know about "domains"**. Only about IP addresses. + * The information about the **specific domain** requested goes in the **HTTP data**. +* The **HTTPS certificates** "certify" a **certain domain**, but the protocol and encryption happen at the TCP level, **before knowing** which domain is being dealt with. +* **By default**, that would mean that you can only have **one HTTPS certificate per IP address**. + * No matter how big your server is or how small each application you have on it might be. + * There is a **solution** to this, however. +* There's an **extension** to the **TLS** protocol (the one handling the encryption at the TCP level, before HTTP) called **SNI**. + * This SNI extension allows one single server (with a **single IP address**) to have **several HTTPS certificates** and serve **multiple HTTPS domains/applications**. + * For this to work, a **single** component (program) running on the server, listening on the **public IP address**, must have **all the HTTPS certificates** in the server. +* **After** obtaining a secure connection, the communication protocol is **still HTTP**. + * The contents are **encrypted**, even though they are being sent with the **HTTP protocol**. + +It is a common practice to have **one program/HTTP server** running on the server (the machine, host, etc.) and **managing all the HTTPS parts**: receiving the **encrypted HTTPS requests**, sending the **decrypted HTTP requests** to the actual HTTP application running in the same server (the **FastAPI** application, in this case), take the **HTTP response** from the application, **encrypt it** using the appropriate **HTTPS certificate** and sending it back to the client using **HTTPS**. This server is often called a **TLS Termination Proxy**. + +Some of the options you could use as a TLS Termination Proxy are: + +* Traefik (that can also handle certificate renewals) +* Caddy (that can also handle certificate renewals) +* Nginx +* HAProxy + +## Let's Encrypt + +Before Let's Encrypt, these **HTTPS certificates** were sold by trusted third parties. + +The process to acquire one of these certificates used to be cumbersome, require quite some paperwork and the certificates were quite expensive. + +But then **Let's Encrypt** was created. + +It is a project from the Linux Foundation. It provides **HTTPS certificates for free**, in an automated way. These certificates use all the standard cryptographic security, and are short-lived (about 3 months), so the **security is actually better** because of their reduced lifespan. + +The domains are securely verified and the certificates are generated automatically. This also allows automating the renewal of these certificates. + +The idea is to automate the acquisition and renewal of these certificates so that you can have **secure HTTPS, for free, forever**. + +## HTTPS for Developers + +Here's an example of how an HTTPS API could look like, step by step, paying attention mainly to the ideas important for developers. + +### Domain Name + +It would probably all start by you **acquiring** some **domain name**. Then, you would configure it in a DNS server (possibly your same cloud provider). + +You would probably get a cloud server (a virtual machine) or something similar, and it would have a fixed **public IP address**. + +In the DNS server(s) you would configure a record (an "`A record`") to point **your domain** to the public **IP address of your server**. + +You would probably do this just once, the first time, when setting everything up. + +!!! tip + This Domain Name part is way before HTTPS, but as everything depends on the domain and the IP address, it's worth mentioning it here. + +### DNS + +Now let's focus on all the actual HTTPS parts. + +First, the browser would check with the **DNS servers** what is the **IP for the domain**, in this case, `someapp.example.com`. + +The DNS servers would tell the browser to use some specific **IP address**. That would be the public IP address used by your server, that you configured in the DNS servers. + + + +### TLS Handshake Start + +The browser would then communicate with that IP address on **port 443** (the HTTPS port). + +The first part of the communication is just to establish the connection between the client and the server and to decide the cryptographic keys they will use, etc. + + + +This interaction between the client and the server to establish the TLS connection is called the **TLS handshake**. + +### TLS with SNI Extension + +**Only one process** in the server can be listening on a specific **port** in a specific **IP address**. There could be other processes listening on other ports in the same IP address, but only one for each combination of IP address and port. + +TLS (HTTPS) uses the specific port `443` by default. So that's the port we would need. + +As only one process can be listening on this port, the process that would do it would be the **TLS Termination Proxy**. + +The TLS Termination Proxy would have access to one or more **TLS certificates** (HTTPS certificates). + +Using the **SNI extension** discussed above, the TLS Termination Proxy would check which of the TLS (HTTPS) certificates available it should use for this connection, using the one that matches the domain expected by the client. + +In this case, it would use the certificate for `someapp.example.com`. + + + +The client already **trusts** the entity that generated that TLS certificate (in this case Let's Encrypt, but we'll see about that later), so it can **verify** that the certificate is valid. + +Then, using the certificate, the client and the TLS Termination Proxy **decide how to encrypt** the rest of the **TCP communication**. This completes the **TLS Handshake** part. + +After this, the client and the server have an **encrypted TCP connection**, this is what TLS provides. And then they can use that connection to start the actual **HTTP communication**. + +And that's what **HTTPS** is, it's just plain **HTTP** inside a **secure TLS connection** instead of a pure (unencrypted) TCP connection. + +!!! tip + Notice that the encryption of the communication happens at the **TCP level**, not at the HTTP level. + +### HTTPS Request + +Now that the client and server (specifically the browser and the TLS Termination Proxy) have an **encrypted TCP connection**, they can start the **HTTP communication**. + +So, the client sends an **HTTPS request**. This is just an HTTP request through an encrypted TLS connection. + + + +### Decrypt the Request + +The TLS Termination Proxy would use the encryption agreed to **decrypt the request**, and would transmit the **plain (decrypted) HTTP request** to the process running the application (for example a process with Uvicorn running the FastAPI application). + + + +### HTTP Response + +The application would process the request and send a **plain (unencrypted) HTTP response** to the TLS Termination Proxy. + + + +### HTTPS Response + +The TLS Termination Proxy would then **encrypt the response** using the cryptography agreed before (that started with the certificate for `someapp.example.com`), and send it back to the browser. + +Next, the browser would verify that the response is valid and encrypted with the right cryptographic key, etc. It would then **decrypt the response** and process it. + + + +The client (browser) will know that the response comes from the correct server because it is using the cryptography they agreed using the **HTTPS certificate** before. + +### Multiple Applications + +In the same server (or servers), there could be **multiple applications**, for example, other API programs or a database. + +Only one process can be handling the specific IP and port (the TLS Termination Proxy in our example) but the other applications/processes can be running on the server(s) too, as long as they don't try to use the same **combination of public IP and port**. + + + +That way, the TLS Termination Proxy could handle HTTPS and certificates for **multiple domains**, for multiple applications, and then transmit the requests to the right application in each case. + +### Certificate Renewal + +At some point in the future, each certificate would **expire** (about 3 months after acquiring it). + +And then, there would be another program (in some cases it's another program, in some cases it could be the same TLS Termination Proxy) that would talk to Let's Encrypt, and renew the certificate(s). + + + +The **TLS certificates** are **associated with a domain name**, not with an IP address. + +So, to renew the certificates, the renewal program needs to **prove** to the authority (Let's Encrypt) that it indeed **"owns" and controls that domain**. + +To do that, and to accommodate different application needs, there are several ways it can do it. Some popular ways are: + +* **Modify some DNS records**. + * For this, the renewal program needs to support the APIs of the DNS provider, so, depending on the DNS provider you are using, this might or might not be an option. +* **Run as a server** (at least during the certificate acquisition process) on the public IP address associated with the domain. + * As we said above, only one process can be listening on a specific IP and port. + * This is one of the reasons why it's very useful when the same TLS Termination Proxy also takes care of the certificate renewal process. + * Otherwise, you might have to stop the TLS Termination Proxy momentarily, start the renewal program to acquire the certificates, then configure them with the TLS Termination Proxy, and then restart the TLS Termination Proxy. This is not ideal, as your app(s) will not be available during the time that the TLS Termination Proxy is off. + +All this renewal process, while still serving the app, is one of the main reasons why you would want to have a **separate system to handle HTTPS** with a TLS Termination Proxy instead of just using the TLS certificates with the application server directly (e.g. Uvicorn). + +## Recap + +Having **HTTPS** is very important, and quite **critical** in most cases. Most of the effort you as a developer have to put around HTTPS is just about **understanding these concepts** and how they work. + +But once you know the basic information of **HTTPS for developers** you can easily combine and configure different tools to help you manage everything in a simple way. + +In some of the next chapters, I'll show you several concrete examples of how to set up **HTTPS** for **FastAPI** applications. 🔒 From c5b3e42450c48938807ff95000471497059ecad9 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:09 +0800 Subject: [PATCH 047/163] New translations index.md (Chinese Simplified) --- docs/zh/docs/deployment/index.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 docs/zh/docs/deployment/index.md diff --git a/docs/zh/docs/deployment/index.md b/docs/zh/docs/deployment/index.md new file mode 100644 index 0000000000000..6c43d8abbe4db --- /dev/null +++ b/docs/zh/docs/deployment/index.md @@ -0,0 +1,21 @@ +# Deployment + +Deploying a **FastAPI** application is relatively easy. + +## What Does Deployment Mean + +To **deploy** an application means to perform the necessary steps to make it **available to the users**. + +For a **web API**, it normally involves putting it in a **remote machine**, with a **server program** that provides good performance, stability, etc, so that your **users** can **access** the application efficiently and without interruptions or problems. + +This is in contrast to the **development** stages, where you are constantly changing the code, breaking it and fixing it, stopping and restarting the development server, etc. + +## Deployment Strategies + +There are several ways to do it depending on your specific use case and the tools that you use. + +You could **deploy a server** yourself using a combination of tools, you could use a **cloud service** that does part of the work for you, or other possible options. + +I will show you some of the main concepts you should probably have in mind when deploying a **FastAPI** application (although most of it applies to any other type of web application). + +You will see more details to have in mind and some of the techniques to do it in the next sections. ✨ From 0b088ba6e01390f5cd629d99ffacf16e49242ea6 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:10 +0800 Subject: [PATCH 048/163] New translations manually.md (Chinese Simplified) --- docs/zh/docs/deployment/manually.md | 141 ++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 docs/zh/docs/deployment/manually.md diff --git a/docs/zh/docs/deployment/manually.md b/docs/zh/docs/deployment/manually.md new file mode 100644 index 0000000000000..8238fa607ab30 --- /dev/null +++ b/docs/zh/docs/deployment/manually.md @@ -0,0 +1,141 @@ +# Run a Server Manually - Uvicorn + +The main thing you need to run a **FastAPI** application in a remote server machine is an ASGI server program like **Uvicorn**. + +There are 3 main alternatives: + +* Uvicorn: a high performance ASGI server. +* Hypercorn: an ASGI server compatible with HTTP/2 and Trio among other features. +* Daphne: the ASGI server built for Django Channels. + +## Server Machine and Server Program + +There's a small detail about names to have in mind. 💡 + +The word "**server**" is commonly used to refer to both the remote/cloud computer (the physical or virtual machine) and also the program that is running on that machine (e.g. Uvicorn). + +Just have that in mind when you read "server" in general, it could refer to one of those two things. + +When referring to the remote machine, it's common to call it **server**, but also **machine**, **VM** (virtual machine), **node**. Those all refer to some type of remote machine, normally running Linux, where you run programs. + +## Install the Server Program + +You can install an ASGI compatible server with: + +=== "Uvicorn" + + * Uvicorn, a lightning-fast ASGI server, built on uvloop and httptools. + +
+B1B + + + +
+ + !!! tip + By adding the `standard`, Uvicorn will install and use some recommended extra dependencies. + + That including `uvloop`, the high-performance drop-in replacement for `asyncio`, that provides the big concurrency performance boost. + +=== "Hypercorn" + + * Hypercorn, an ASGI server also compatible with HTTP/2. + +
+B2B + + + +
+ + ...or any other ASGI server. + +## Run the Server Program + +You can then run your application the same way you have done in the tutorials, but without the `--reload` option, e.g.: + +=== "Uvicorn" + +
+ + ```console + $ uvicorn main:app --host 0.0.0.0 --port 80 + + INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit) + ``` + + +
+ +=== "Hypercorn" + +
+ + ```console + $ hypercorn main:app --bind 0.0.0.0:80 + + Running on 0.0.0.0:8080 over http (CTRL + C to quit) + ``` + + +
+ +!!! warning + Remember to remove the `--reload` option if you were using it. + + The `--reload` option consumes much more resources, is more unstable, etc. + + It helps a lot during **development**, but you **shouldn't** use it in **production**. + +## Hypercorn with Trio + +Starlette and **FastAPI** are based on AnyIO, which makes them compatible with both Python's standard library asyncio and Trio. + +Nevertheless, Uvicorn is currently only compatible with asyncio, and it normally uses `uvloop`, the high-performance drop-in replacement for `asyncio`. + +But if you want to directly use **Trio**, then you can use **Hypercorn** as it supports it. ✨ + +### Install Hypercorn with Trio + +First you need to install Hypercorn with Trio support: + +
+ +```console +$ pip install "hypercorn[trio]" +---> 100% +``` + +
+ +### Run with Trio + +Then you can pass the command line option `--worker-class` with the value `trio`: + +
+ +```console +$ hypercorn main:app --worker-class trio +``` + +
+ +And that will start Hypercorn with your app using Trio as the backend. + +Now you can use Trio internally in your app. Or even better, you can use AnyIO, to keep your code compatible with both Trio and asyncio. 🎉 + +## Deployment Concepts + +These examples run the server program (e.g Uvicorn), starting **a single process**, listening on all the IPs (`0.0.0.0`) on a predefined port (e.g. `80`). + +This is the basic idea. But you will probably want to take care of some additional things, like: + +* Security - HTTPS +* Running on startup +* Restarts +* Replication (the number of processes running) +* Memory +* Previous steps before starting + +I'll tell you more about each of these concepts, how to think about them, and some concrete examples with strategies to handle them in the next chapters. 🚀 From f8e81c87ff1bd93a9498516449cf0fa14169225d Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:11 +0800 Subject: [PATCH 049/163] New translations server-workers.md (Chinese Simplified) --- docs/zh/docs/deployment/server-workers.md | 178 ++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 docs/zh/docs/deployment/server-workers.md diff --git a/docs/zh/docs/deployment/server-workers.md b/docs/zh/docs/deployment/server-workers.md new file mode 100644 index 0000000000000..4ccd9d9f69a8b --- /dev/null +++ b/docs/zh/docs/deployment/server-workers.md @@ -0,0 +1,178 @@ +# Server Workers - Gunicorn with Uvicorn + +Let's check back those deployment concepts from before: + +* Security - HTTPS +* Running on startup +* Restarts +* **Replication (the number of processes running)** +* Memory +* Previous steps before starting + +Up to this point, with all the tutorials in the docs, you have probably been running a **server program** like Uvicorn, running a **single process**. + +When deploying applications you will probably want to have some **replication of processes** to take advantage of **multiple cores** and to be able to handle more requests. + +As you saw in the previous chapter about [Deployment Concepts](./concepts.md){.internal-link target=_blank}, there are multiple strategies you can use. + +Here I'll show you how to use **Gunicorn** with **Uvicorn worker processes**. + +!!! info + If you are using containers, for example with Docker or Kubernetes, I'll tell you more about that in the next chapter: [FastAPI in Containers - Docker](./docker.md){.internal-link target=_blank}. + + In particular, when running on **Kubernetes** you will probably **not** want to use Gunicorn and instead run **a single Uvicorn process per container**, but I'll tell you about it later in that chapter. + +## Gunicorn with Uvicorn Workers + +**Gunicorn** is mainly an application server using the **WSGI standard**. That means that Gunicorn can serve applications like Flask and Django. Gunicorn by itself is not compatible with **FastAPI**, as FastAPI uses the newest **ASGI standard**. + +But Gunicorn supports working as a **process manager** and allowing users to tell it which specific **worker process class** to use. Then Gunicorn would start one or more **worker processes** using that class. + +And **Uvicorn** has a **Gunicorn-compatible worker class**. + +Using that combination, Gunicorn would act as a **process manager**, listening on the **port** and the **IP**. And it would **transmit** the communication to the worker processes running the **Uvicorn class**. + +And then the Gunicorn-compatible **Uvicorn worker** class would be in charge of converting the data sent by Gunicorn to the ASGI standard for FastAPI to use it. + +## Install Gunicorn and Uvicorn + +
+ +```console +$ pip install "uvicorn[standard]" gunicorn + +---> 100% +``` + +
+ +That will install both Uvicorn with the `standard` extra packages (to get high performance) and Gunicorn. + +## Run Gunicorn with Uvicorn Workers + +Then you can run Gunicorn with: + +
+ +```console +$ gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:80 + +[19499] [INFO] Starting gunicorn 20.1.0 +[19499] [INFO] Listening at: http://0.0.0.0:80 (19499) +[19499] [INFO] Using worker: uvicorn.workers.UvicornWorker +[19511] [INFO] Booting worker with pid: 19511 +[19513] [INFO] Booting worker with pid: 19513 +[19514] [INFO] Booting worker with pid: 19514 +[19515] [INFO] Booting worker with pid: 19515 +[19511] [INFO] Started server process [19511] +[19511] [INFO] Waiting for application startup. +[19511] [INFO] Application startup complete. +[19513] [INFO] Started server process [19513] +[19513] [INFO] Waiting for application startup. +[19513] [INFO] Application startup complete. +[19514] [INFO] Started server process [19514] +[19514] [INFO] Waiting for application startup. +[19514] [INFO] Application startup complete. +[19515] [INFO] Started server process [19515] +[19515] [INFO] Waiting for application startup. +[19515] [INFO] Application startup complete. +``` + +
+ +Let's see what each of those options mean: + +* `main:app`: This is the same syntax used by Uvicorn, `main` means the Python module named "`main`", so, a file `main.py`. And `app` is the name of the variable that is the **FastAPI** application. + * You can imagine that `main:app` is equivalent to a Python `import` statement like: + + ```Python + from main import app + ``` + + * So, the colon in `main:app` would be equivalent to the Python `import` part in `from main import app`. +* `--workers`: The number of worker processes to use, each will run a Uvicorn worker, in this case, 4 workers. +* `--worker-class`: The Gunicorn-compatible worker class to use in the worker processes. + * Here we pass the class that Gunicorn can import and use with: + + ```Python + import uvicorn.workers.UvicornWorker + ``` + +* `--bind`: This tells Gunicorn the IP and the port to listen to, using a colon (`:`) to separate the IP and the port. + * If you were running Uvicorn directly, instead of `--bind 0.0.0.0:80` (the Gunicorn option) you would use `--host 0.0.0.0` and `--port 80`. + +In the output, you can see that it shows the **PID** (process ID) of each process (it's just a number). + +You can see that: + +* The Gunicorn **process manager** starts with PID `19499` (in your case it will be a different number). +* Then it starts `Listening at: http://0.0.0.0:80`. +* Then it detects that it has to use the worker class at `uvicorn.workers.UvicornWorker`. +* And then it starts **4 workers**, each with its own PID: `19511`, `19513`, `19514`, and `19515`. + +Gunicorn would also take care of managing **dead processes** and **restarting** new ones if needed to keep the number of workers. So that helps in part with the **restart** concept from the list above. + +Nevertheless, you would probably also want to have something outside making sure to **restart Gunicorn** if necessary, and also to **run it on startup**, etc. + +## Uvicorn with Workers + +Uvicorn also has an option to start and run several **worker processes**. + +Nevertheless, as of now, Uvicorn's capabilities for handling worker processes are more limited than Gunicorn's. So, if you want to have a process manager at this level (at the Python level), then it might be better to try with Gunicorn as the process manager. + +In any case, you would run it like this: + +
+ +```console +$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4 +INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit) +INFO: Started parent process [27365] +INFO: Started server process [27368] +INFO: Waiting for application startup. +INFO: Application startup complete. +INFO: Started server process [27369] +INFO: Waiting for application startup. +INFO: Application startup complete. +INFO: Started server process [27370] +INFO: Waiting for application startup. +INFO: Application startup complete. +INFO: Started server process [27367] +INFO: Waiting for application startup. +INFO: Application startup complete. +``` + +
+ +The only new option here is `--workers` telling Uvicorn to start 4 worker processes. + +You can also see that it shows the **PID** of each process, `27365` for the parent process (this is the **process manager**) and one for each worker process: `27368`, `27369`, `27370`, and `27367`. + +## Deployment Concepts + +Here you saw how to use **Gunicorn** (or Uvicorn) managing **Uvicorn worker processes** to **parallelize** the execution of the application, take advantage of **multiple cores** in the CPU, and be able to serve **more requests**. + +From the list of deployment concepts from above, using workers would mainly help with the **replication** part, and a little bit with the **restarts**, but you still need to take care of the others: + +* **Security - HTTPS** +* **Running on startup** +* ***Restarts*** +* Replication (the number of processes running) +* **Memory** +* **Previous steps before starting** + +## Containers and Docker + +In the next chapter about [FastAPI in Containers - Docker](./docker.md){.internal-link target=_blank} I'll tell some strategies you could use to handle the other **deployment concepts**. + +I'll also show you the **official Docker image** that includes **Gunicorn with Uvicorn workers** and some default configurations that can be useful for simple cases. + +There I'll also show you how to **build your own image from scratch** to run a single Uvicorn process (without Gunicorn). It is a simple process and is probably what you would want to do when using a distributed container management system like **Kubernetes**. + +## Recap + +You can use **Gunicorn** (or also Uvicorn) as a process manager with Uvicorn workers to take advantage of **multi-core CPUs**, to run **multiple processes in parallel**. + +You could use these tools and ideas if you are setting up **your own deployment system** while taking care of the other deployment concepts yourself. + +Check out the next chapter to learn about **FastAPI** with containers (e.g. Docker and Kubernetes). You will see that those tools have simple ways to solve the other **deployment concepts** as well. ✨ From 875dd91d42e175f4bb9f41db6a1767a5b97d14b3 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:12 +0800 Subject: [PATCH 050/163] New translations versions.md (Chinese Simplified) --- docs/zh/docs/deployment/versions.md | 87 +++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 docs/zh/docs/deployment/versions.md diff --git a/docs/zh/docs/deployment/versions.md b/docs/zh/docs/deployment/versions.md new file mode 100644 index 0000000000000..4be9385ddf530 --- /dev/null +++ b/docs/zh/docs/deployment/versions.md @@ -0,0 +1,87 @@ +# About FastAPI versions + +**FastAPI** is already being used in production in many applications and systems. And the test coverage is kept at 100%. But its development is still moving quickly. + +New features are added frequently, bugs are fixed regularly, and the code is still continuously improving. + +That's why the current versions are still `0.x.x`, this reflects that each version could potentially have breaking changes. This follows the Semantic Versioning conventions. + +You can create production applications with **FastAPI** right now (and you have probably been doing it for some time), you just have to make sure that you use a version that works correctly with the rest of your code. + +## Pin your `fastapi` version + +The first thing you should do is to "pin" the version of **FastAPI** you are using to the specific latest version that you know works correctly for your application. + +For example, let's say you are using version `0.45.0` in your app. + +If you use a `requirements.txt` file you could specify the version with: + +```txt +fastapi==0.45.0 +``` + +that would mean that you would use exactly the version `0.45.0`. + +Or you could also pin it with: + +```txt +fastapi>=0.45.0,<0.46.0 +``` + +that would mean that you would use the versions `0.45.0` or above, but less than `0.46.0`, for example, a version `0.45.2` would still be accepted. + +If you use any other tool to manage your installations, like Poetry, Pipenv, or others, they all have a way that you can use to define specific versions for your packages. + +## Available versions + +You can see the available versions (e.g. to check what is the current latest) in the [Release Notes](../release-notes.md){.internal-link target=_blank}. + +## About versions + +Following the Semantic Versioning conventions, any version below `1.0.0` could potentially add breaking changes. + +FastAPI also follows the convention that any "PATCH" version change is for bug fixes and non-breaking changes. + +!!! tip + The "PATCH" is the last number, for example, in `0.2.3`, the PATCH version is `3`. + +So, you should be able to pin to a version like: + +```txt +fastapi>=0.45.0,<0.46.0 +``` + +Breaking changes and new features are added in "MINOR" versions. + +!!! tip + The "MINOR" is the number in the middle, for example, in `0.2.3`, the MINOR version is `2`. + +## Upgrading the FastAPI versions + +You should add tests for your app. + +With **FastAPI** it's very easy (thanks to Starlette), check the docs: [Testing](../tutorial/testing.md){.internal-link target=_blank} + +After you have tests, then you can upgrade the **FastAPI** version to a more recent one, and make sure that all your code is working correctly by running your tests. + +If everything is working, or after you make the necessary changes, and all your tests are passing, then you can pin your `fastapi` to that new recent version. + +## About Starlette + +You shouldn't pin the version of `starlette`. + +Different versions of **FastAPI** will use a specific newer version of Starlette. + +So, you can just let **FastAPI** use the correct Starlette version. + +## About Pydantic + +Pydantic includes the tests for **FastAPI** with its own tests, so new versions of Pydantic (above `1.0.0`) are always compatible with FastAPI. + +You can pin Pydantic to any version above `1.0.0` that works for you and below `2.0.0`. + +For example: + +```txt +pydantic>=1.2.0,<2.0.0 +``` From 82b459d272e38f9f0ab9239a995ddcd2ec39ec1d Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:13 +0800 Subject: [PATCH 051/163] New translations external-links.md (Chinese Simplified) --- docs/zh/docs/external-links.md | 91 ++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 docs/zh/docs/external-links.md diff --git a/docs/zh/docs/external-links.md b/docs/zh/docs/external-links.md new file mode 100644 index 0000000000000..0c91470bc0a03 --- /dev/null +++ b/docs/zh/docs/external-links.md @@ -0,0 +1,91 @@ +# External Links and Articles + +**FastAPI** has a great community constantly growing. + +There are many posts, articles, tools, and projects, related to **FastAPI**. + +Here's an incomplete list of some of them. + +!!! tip + If you have an article, project, tool, or anything related to **FastAPI** that is not yet listed here, create a Pull Request adding it. + +## Articles + +### English + +{% if external_links %} +{% for article in external_links.articles.english %} + +* {{ article.title }} by {{ article.author }}. +{% endfor %} +{% endif %} + +### Japanese + +{% if external_links %} +{% for article in external_links.articles.japanese %} + +* {{ article.title }} by {{ article.author }}. +{% endfor %} +{% endif %} + +### Vietnamese + +{% if external_links %} +{% for article in external_links.articles.vietnamese %} + +* {{ article.title }} by {{ article.author }}. +{% endfor %} +{% endif %} + +### Russian + +{% if external_links %} +{% for article in external_links.articles.russian %} + +* {{ article.title }} by {{ article.author }}. +{% endfor %} +{% endif %} + +### German + +{% if external_links %} +{% for article in external_links.articles.german %} + +* {{ article.title }} by {{ article.author }}. +{% endfor %} +{% endif %} + +### Taiwanese + +{% if external_links %} +{% for article in external_links.articles.taiwanese %} + +* {{ article.title }} by {{ article.author }}. +{% endfor %} +{% endif %} + +## Podcasts + +{% if external_links %} +{% for article in external_links.podcasts.english %} + +* {{ article.title }} by {{ article.author }}. +{% endfor %} +{% endif %} + +## Talks + +{% if external_links %} +{% for article in external_links.talks.english %} + +* {{ article.title }} by {{ article.author }}. +{% endfor %} +{% endif %} + +## Projects + +Latest GitHub projects with the topic `fastapi`: + +
+
From 8ffb26f9fe5d546058e509c4c2616f9bbef7c473 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:14 +0800 Subject: [PATCH 052/163] New translations fastapi-people.md (Chinese Simplified) --- docs/zh/docs/fastapi-people.md | 88 +++++++++++++++++----------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/docs/zh/docs/fastapi-people.md b/docs/zh/docs/fastapi-people.md index 5d7b0923f33c4..20caaa1ee8be1 100644 --- a/docs/zh/docs/fastapi-people.md +++ b/docs/zh/docs/fastapi-people.md @@ -1,12 +1,12 @@ -# FastAPI 社区 +# FastAPI People -FastAPI 有一个非常棒的社区,它欢迎来自各个领域和背景的朋友。 +FastAPI has an amazing community that welcomes people from all backgrounds. -## 创建者 & 维护者 +## Creator - Maintainer -嘿! 👋 +Hey! 👋 -这就是我: +This is me: {% if people %}
@@ -18,61 +18,61 @@ FastAPI 有一个非常棒的社区,它欢迎来自各个领域和背景的朋
{% endif %} -我是 **FastAPI** 的创建者和维护者. 你能在 [帮助 FastAPI - 获取帮助 - 与作者联系](help-fastapi.md#connect-with-the-author){.internal-link target=_blank} 阅读有关此内容的更多信息。 +I'm the creator and maintainer of **FastAPI**. You can read more about that in [Help FastAPI - Get Help - Connect with the author](help-fastapi.md#connect-with-the-author){.internal-link target=_blank}. -...但是在这里我想向您展示社区。 +...But here I want to show you the community. --- -**FastAPI** 得到了社区的大力支持。因此我想突出他们的贡献。 +**FastAPI** receives a lot of support from the community. And I want to highlight their contributions. -这些人: +These are the people that: -* [帮助他人解决 GitHub 的 issues](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank}。 -* [创建 Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}。 -* 审核 Pull Requests, 对于 [翻译](contributing.md#translations){.internal-link target=_blank} 尤为重要。 +* [Help others with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank}. +* [Create Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}. +* Review Pull Requests, [especially important for translations](contributing.md#translations){.internal-link target=_blank}. -向他们致以掌声。 👏 🙇 +A round of applause to them. 👏 🙇 -## 上个月最活跃的用户 +## Most active users last month -上个月这些用户致力于 [帮助他人解决 GitHub 的 issues](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank}。 +These are the users that have been [helping others the most with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank} during the last month. ☕ {% if people %}
{% for user in people.last_month_active %} -
@{{ user.login }}
Issues replied: {{ user.count }}
+
@{{ user.login }}
Questions replied: {{ user.count }}
{% endfor %}
{% endif %} -## 专家组 +## Experts -以下是 **FastAPI 专家**。 🤓 +Here are the **FastAPI Experts**. 🤓 -这些用户一直以来致力于 [帮助他人解决 GitHub 的 issues](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank}。 +These are the users that have [helped others the most with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank} through *all time*. -他们通过帮助许多人而被证明是专家。✨ +They have proven to be experts by helping many others. ✨ {% if people %}
{% for user in people.experts %} -
@{{ user.login }}
Issues replied: {{ user.count }}
+
@{{ user.login }}
Questions replied: {{ user.count }}
{% endfor %}
{% endif %} -## 杰出贡献者 +## Top Contributors -以下是 **杰出的贡献者**。 👷 +Here are the **Top Contributors**. 👷 -这些用户 [创建了最多已被合并的 Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}。 +These users have [created the most Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank} that have been *merged*. -他们贡献了源代码,文档,翻译等。 📦 +They have contributed source code, documentation, translations, etc. 📦 {% if people %}
@@ -84,19 +84,19 @@ FastAPI 有一个非常棒的社区,它欢迎来自各个领域和背景的朋
{% endif %} -还有很多其他贡献者(超过100个),你可以在 FastAPI GitHub 贡献者页面 中看到他们。👷 +There are many other contributors (more than a hundred), you can see them all in the FastAPI GitHub Contributors page. 👷 -## 杰出审核者 +## Top Reviewers -以下用户是「杰出的评审者」。 🕵️ +These users are the **Top Reviewers**. 🕵️ -### 翻译审核 +### Reviews for Translations -我只会说少数几种语言(而且还不是很流利 😅)。所以,具备[能力去批准文档翻译](contributing.md#translations){.internal-link target=_blank} 是这些评审者们。如果没有它们,就不会有多语言文档。 +I only speak a few languages (and not very well 😅). So, the reviewers are the ones that have the [**power to approve translations**](contributing.md#translations){.internal-link target=_blank} of the documentation. Without them, there wouldn't be documentation in several other languages. --- -**杰出的评审者** 🕵️ 评审了最多来自他人的 Pull Requests,他们保证了代码、文档尤其是 **翻译** 的质量。 +The **Top Reviewers** 🕵️ have reviewed the most Pull Requests from others, ensuring the quality of the code, documentation, and especially, the **translations**. {% if people %}
@@ -108,17 +108,17 @@ FastAPI 有一个非常棒的社区,它欢迎来自各个领域和背景的朋
{% endif %} -## 赞助商 +## Sponsors -以下是 **赞助商** 。😎 +These are the **Sponsors**. 😎 -他们主要通过GitHub Sponsors支持我在 **FastAPI** (和其他项目)的工作。 +They are supporting my work with **FastAPI** (and others), mainly through GitHub Sponsors. {% if sponsors %} {% if sponsors.gold %} -### 金牌赞助商 +### Gold Sponsors {% for sponsor in sponsors.gold -%} @@ -127,7 +127,7 @@ FastAPI 有一个非常棒的社区,它欢迎来自各个领域和背景的朋 {% if sponsors.silver %} -### 银牌赞助商 +### Silver Sponsors {% for sponsor in sponsors.silver -%} @@ -136,7 +136,7 @@ FastAPI 有一个非常棒的社区,它欢迎来自各个领域和背景的朋 {% if sponsors.bronze %} -### 铜牌赞助商 +### Bronze Sponsors {% for sponsor in sponsors.bronze -%} @@ -145,7 +145,7 @@ FastAPI 有一个非常棒的社区,它欢迎来自各个领域和背景的朋 {% endif %} -### 个人赞助 +### Individual Sponsors {% if github_sponsors %} {% for group in github_sponsors.sponsors %} @@ -165,14 +165,14 @@ FastAPI 有一个非常棒的社区,它欢迎来自各个领域和背景的朋 {% endfor %} {% endif %} -## 关于数据 - 技术细节 +## About the data - technical details -该页面的目的是突出社区为帮助他人而付出的努力。 +The main intention of this page is to highlight the effort of the community to help others. -尤其是那些不引人注目且涉及更困难的任务,例如帮助他人解决问题或者评审翻译 Pull Requests。 +Especially including efforts that are normally less visible, and in many cases more arduous, like helping others with questions and reviewing Pull Requests with translations. -该数据每月计算一次,您可以阅读 源代码。 +The data is calculated each month, you can read the source code here. -这里也强调了赞助商的贡献。 +Here I'm also highlighting contributions from sponsors. -我也保留更新算法,栏目,统计阈值等的权利(以防万一🤷)。 +I also reserve the right to update the algorithm, sections, thresholds, etc (just in case 🤷). From 23a7cd661f0048c0a076894a93d6e7e035721dd2 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:15 +0800 Subject: [PATCH 053/163] New translations features.md (Chinese Simplified) --- docs/zh/docs/features.md | 203 +++++++++++++++++++-------------------- 1 file changed, 98 insertions(+), 105 deletions(-) diff --git a/docs/zh/docs/features.md b/docs/zh/docs/features.md index 2db7f852a9e83..98f37b5344580 100644 --- a/docs/zh/docs/features.md +++ b/docs/zh/docs/features.md @@ -1,36 +1,35 @@ -# 特性 +# Features -## FastAPI 特性 +## FastAPI features -**FastAPI** 提供了以下内容: +**FastAPI** gives you the following: -### 基于开放标准 +### Based on open standards +* OpenAPI for API creation, including declarations of path operations, parameters, body requests, security, etc. +* Automatic data model documentation with JSON Schema (as OpenAPI itself is based on JSON Schema). +* Designed around these standards, after a meticulous study. Instead of an afterthought layer on top. +* This also allows using automatic **client code generation** in many languages. -* 用于创建 API 的 OpenAPI 包含了路径操作,请求参数,请求体,安全性等的声明。 -* 使用 JSON Schema (因为 OpenAPI 本身就是基于 JSON Schema 的)自动生成数据模型文档。 -* 经过了缜密的研究后围绕这些标准而设计。并非狗尾续貂。 -* 这也允许了在很多语言中自动**生成客户端代码**。 +### Automatic docs -### 自动生成文档 +Interactive API documentation and exploration web user interfaces. As the framework is based on OpenAPI, there are multiple options, 2 included by default. -交互式 API 文档以及具探索性 web 界面。因为该框架是基于 OpenAPI,所以有很多可选项,FastAPI 默认自带两个交互式 API 文档。 - -* Swagger UI,可交互式操作,能在浏览器中直接调用和测试你的 API 。 +* Swagger UI, with interactive exploration, call and test your API directly from the browser. ![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png) -* 另外的 API 文档:ReDoc +* Alternative API documentation with ReDoc. ![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png) -### 更主流的 Python +### Just Modern Python -全部都基于标准的 **Python 3.6 类型**声明(感谢 Pydantic )。没有新的语法需要学习。只需要标准的 Python 。 +It's all based on standard **Python 3.6 type** declarations (thanks to Pydantic). No new syntax to learn. Just standard modern Python. -如果你需要2分钟来学习如何使用 Python 类型(即使你不使用 FastAPI ),看看这个简短的教程:[Python Types](python-types.md){.internal-link target=_blank}。 +If you need a 2 minute refresher of how to use Python types (even if you don't use FastAPI), check the short tutorial: [Python Types](python-types.md){.internal-link target=_blank}. -编写带有类型标注的标准 Python: +You write standard Python with types: ```Python from datetime import date @@ -50,7 +49,7 @@ class User(BaseModel): joined: date ``` -可以像这样来使用: +That can then be used like: ```Python my_user: User = User(id=3, name="John Doe", joined="2018-07-19") @@ -64,142 +63,136 @@ second_user_data = { my_second_user: User = User(**second_user_data) ``` - !!! info - `**second_user_data` 意思是: + `**second_user_data` means: - 直接将`second_user_data`字典的键和值直接作为key-value参数传递,等同于:`User(id=4, name="Mary", joined="2018-11-30")` + Pass the keys and values of the `second_user_data` dict directly as key-value arguments, equivalent to: `User(id=4, name="Mary", joined="2018-11-30")` -### 编辑器支持 +### Editor support -整个框架都被设计得易于使用且直观,所有的决定都在开发之前就在多个编辑器上进行了测试,来确保最佳的开发体验。 +All the framework was designed to be easy and intuitive to use, all the decisions were tested on multiple editors even before starting development, to ensure the best development experience. -在最近的 Python 开发者调查中,我们能看到 被使用最多的功能是"自动补全"。 +In the last Python developer survey it was clear that the most used feature is "autocompletion". -整个 **FastAPI** 框架就是基于这一点的。任何地方都可以进行自动补全。 +The whole **FastAPI** framework is based to satisfy that. Autocompletion works everywhere. -你几乎不需要经常回来看文档。 +You will rarely need to come back to the docs. -在这里,你的编辑器可能会这样帮助你: +Here's how your editor might help you: -* Visual Studio Code 中: +* in Visual Studio Code: ![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png) -* PyCharm 中: +* in PyCharm: ![editor support](https://fastapi.tiangolo.com/img/pycharm-completion.png) -你将能进行代码补全,这是在之前你可能曾认为不可能的事。例如,在来自请求 JSON 体(可能是嵌套的)中的键 `price`。 - -不会再输错键名,来回翻看文档,或者来回滚动寻找你最后使用的 `username` 或者 `user_name` 。 +You will get completion in code you might even consider impossible before. As for example, the `price` key inside a JSON body (that could have been nested) that comes from a request. +No more typing the wrong key names, coming back and forth between docs, or scrolling up and down to find if you finally used `username` or `user_name`. +### Short -### 简洁 +It has sensible **defaults** for everything, with optional configurations everywhere. All the parameters can be fine-tuned to do what you need and to define the API you need. -任何类型都有合理的**默认值**,任何和地方都有可选配置。所有的参数被微调,来满足你的需求,定义成你需要的 API。 +But by default, it all **"just works"**. -但是默认情况下,一切都能**“顺利工作”**。 +### Validation -### 验证 +* Validation for most (or all?) Python **data types**, including: + * JSON objects (`dict`). + * JSON array (`list`) defining item types. + * String (`str`) fields, defining min and max lengths. + * Numbers (`int`, `float`) with min and max values, etc. -* 校验大部分(甚至所有?)的 Python **数据类型**,包括: - * JSON 对象 (`dict`). - * JSON 数组 (`list`) 定义成员类型。 - * 字符串 (`str`) 字段, 定义最小或最大长度。 - * 数字 (`int`, `float`) 有最大值和最小值, 等等。 - -* 校验外来类型, 比如: +* Validation for more exotic types, like: * URL. * Email. * UUID. - * ...及其他. - -所有的校验都由完善且强大的 **Pydantic** 处理。 + * ...and others. -### 安全性及身份验证 +All the validation is handled by the well-established and robust **Pydantic**. -集成了安全性和身份认证。杜绝数据库或者数据模型的渗透风险。 +### Security and authentication -OpenAPI 中定义的安全模式,包括: +Security and authentication integrated. Without any compromise with databases or data models. -* HTTP 基本认证。 -* **OAuth2** (也使用 **JWT tokens**)。在 [OAuth2 with JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}查看教程。 -* API 密钥,在: - * 请求头。 - * 查询参数。 - * Cookies, 等等。 +All the security schemes defined in OpenAPI, including: -加上来自 Starlette(包括 **session cookie**)的所有安全特性。 +* HTTP Basic. +* **OAuth2** (also with **JWT tokens**). Check the tutorial on [OAuth2 with JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. +* API keys in: + * Headers. + * Query parameters. + * Cookies, etc. -所有的这些都是可复用的工具和组件,可以轻松与你的系统,数据仓库,关系型以及 NoSQL 数据库等等集成。 +Plus all the security features from Starlette (including **session cookies**). +All built as reusable tools and components that are easy to integrate with your systems, data stores, relational and NoSQL databases, etc. +### Dependency Injection -### 依赖注入 +FastAPI includes an extremely easy to use, but extremely powerful Dependency Injection system. -FastAPI 有一个使用非常简单,但是非常强大的依赖注入系统。 +* Even dependencies can have dependencies, creating a hierarchy or **"graph" of dependencies**. +* All **automatically handled** by the framework. +* All the dependencies can require data from requests and **augment the path operation** constraints and automatic documentation. +* **Automatic validation** even for *path operation* parameters defined in dependencies. +* Support for complex user authentication systems, **database connections**, etc. +* **No compromise** with databases, frontends, etc. But easy integration with all of them. -* 甚至依赖也可以有依赖,创建一个层级或者**“图”依赖**。 -* 所有**自动化处理**都由框架完成。 -* 所有的依赖关系都可以从请求中获取数据,并且**增加了路径操作**约束和自动文档生成。 -* 即使在依赖项中被定义的*路径操作* 也会**自动验证**。 -* 支持复杂的用户身份认证系统,**数据库连接**等等。 -* **不依赖**数据库,前端等。 但是和它们集成很简单。 +### Unlimited "plug-ins" -### 无限制"插件" +Or in other way, no need for them, import and use the code you need. -或者说,导入并使用你需要的代码,而不需要它们。 +Any integration is designed to be so simple to use (with dependencies) that you can create a "plug-in" for your application in 2 lines of code using the same structure and syntax used for your *path operations*. -任何集成都被设计得被易于使用(用依赖关系),你可以用和*路径操作*相同的结构和语法,在两行代码中为你的应用创建一个“插件”。 +### Tested -### 测试 +* 100% test coverage. +* 100% type annotated code base. +* Used in production applications. -* 100% 测试覆盖。 -* 代码库100% 类型注释。 -* 用于生产应用。 +## Starlette features -## Starlette 特性 +**FastAPI** is fully compatible with (and based on) Starlette. So, any additional Starlette code you have, will also work. -**FastAPI** 和 Starlette 完全兼容(并基于)。所以,你有的其他的 Starlette 代码也能正常工作。`FastAPI` 实际上是 `Starlette`的一个子类。所以,如果你已经知道或者使用 Starlette,大部分的功能会以相同的方式工作。 +`FastAPI` is actually a sub-class of `Starlette`. So, if you already know or use Starlette, most of the functionality will work the same way. -通过 **FastAPI** 你可以获得所有 **Starlette** 的特性 ( FastAPI 就像加强版的 Starlette ): +With **FastAPI** you get all of **Starlette**'s features (as FastAPI is just Starlette on steroids): -* 令人惊叹的性能。它是 Python 可用的最快的框架之一,和 **NodeJS** 及 **Go** 相当。 -* **支持 WebSocket** 。 -* **支持 GraphQL** 。 -* 后台任务处理。 -* Startup 和 shutdown 事件。 -* 测试客户端基于 HTTPX。 -* **CORS**, GZip, 静态文件, 流响应。 -* 支持 **Session 和 Cookie** 。 -* 100% 测试覆盖率。 -* 代码库 100% 类型注释。 +* Seriously impressive performance. It is one of the fastest Python frameworks available, on par with **NodeJS** and **Go**. +* **WebSocket** support. +* In-process background tasks. +* Startup and shutdown events. +* Test client built on HTTPX. +* **CORS**, GZip, Static Files, Streaming responses. +* **Session and Cookie** support. +* 100% test coverage. +* 100% type annotated codebase. -## Pydantic 特性 +## Pydantic features -**FastAPI** 和 Pydantic 完全兼容(并基于)。所以,你有的其他的 Pydantic 代码也能正常工作。 +**FastAPI** is fully compatible with (and based on) Pydantic. So, any additional Pydantic code you have, will also work. -兼容包括基于 Pydantic 的外部库, 例如用与数据库的 ORMs, ODMs。 +Including external libraries also based on Pydantic, as ORMs, ODMs for databases. -这也意味着在很多情况下,你可以将从请求中获得的相同对象**直接传到数据库**,因为所有的验证都是自动的。 +This also means that in many cases you can pass the same object you get from a request **directly to the database**, as everything is validated automatically. -反之亦然,在很多情况下,你也可以将从数据库中获取的对象**直接传到客户端**。 +The same applies the other way around, in many cases you can just pass the object you get from the database **directly to the client**. -通过 **FastAPI** 你可以获得所有 **Pydantic** (FastAPI 基于 Pydantic 做了所有的数据处理): +With **FastAPI** you get all of **Pydantic**'s features (as FastAPI is based on Pydantic for all the data handling): -* **更简单**: - * 没有新的模式定义 micro-language 需要学习。 - * 如果你知道 Python types,你就知道如何使用 Pydantic。 -* 和你 **IDE/linter/brain** 适配: - * 因为 pydantic 数据结构仅仅是你定义的类的实例;自动补全,linting,mypy 以及你的直觉应该可以和你验证的数据一起正常工作。 -* **更快**: - * 在 基准测试 中,Pydantic 比其他被测试的库都要快。 -* 验证**复杂结构**: - * 使用分层的 Pydantic 模型, Python `typing`的 `List` 和 `Dict` 等等。 - * 验证器使我们能够简单清楚的将复杂的数据模式定义、检查并记录为 JSON Schema。 - * 你可以拥有深度**嵌套的 JSON** 对象并对它们进行验证和注释。 -* **可扩展**: - * Pydantic 允许定义自定义数据类型或者你可以用验证器装饰器对被装饰的模型上的方法扩展验证。 -* 100% 测试覆盖率。 +* **No brainfuck**: + * No new schema definition micro-language to learn. + * If you know Python types you know how to use Pydantic. +* Plays nicely with your **IDE/linter/brain**: + * Because pydantic data structures are just instances of classes you define; auto-completion, linting, mypy and your intuition should all work properly with your validated data. +* Validate **complex structures**: + * Use of hierarchical Pydantic models, Python `typing`’s `List` and `Dict`, etc. + * And validators allow complex data schemas to be clearly and easily defined, checked and documented as JSON Schema. + * You can have deeply **nested JSON** objects and have them all validated and annotated. +* **Extensible**: + * Pydantic allows custom data types to be defined or you can extend validation with methods on a model decorated with the validator decorator. +* 100% test coverage. From b4b804498d43339206bf40d6891edd08e3bf85f2 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:16 +0800 Subject: [PATCH 054/163] New translations help-fastapi.md (Chinese Simplified) --- docs/zh/docs/help-fastapi.md | 287 ++++++++++++++++++++++++----------- 1 file changed, 202 insertions(+), 85 deletions(-) diff --git a/docs/zh/docs/help-fastapi.md b/docs/zh/docs/help-fastapi.md index 2a99950e31ea6..ee1c4d38fe334 100644 --- a/docs/zh/docs/help-fastapi.md +++ b/docs/zh/docs/help-fastapi.md @@ -1,148 +1,265 @@ -# 帮助 FastAPI 与求助 +# Help FastAPI - Get Help -您喜欢 **FastAPI** 吗? +Do you like **FastAPI**? -想帮助 FastAPI?其它用户?还有项目作者? +Would you like to help FastAPI, other users, and the author? -或要求助怎么使用 **FastAPI**? +Or would you like to get help with **FastAPI**? -以下几种帮助的方式都非常简单(有些只需要点击一两下鼠标)。 +There are very simple ways to help (several involve just one or two clicks). -求助的渠道也很多。 +And there are several ways to get help too. -## 订阅新闻邮件 +## Subscribe to the newsletter -您可以订阅 [**FastAPI 和它的小伙伴** 新闻邮件](/newsletter/){.internal-link target=_blank}(不会经常收到) +You can subscribe to the (infrequent) [**FastAPI and friends** newsletter](/newsletter/){.internal-link target=_blank} to stay updated about: -* FastAPI 及其小伙伴的新闻 🚀 -* 指南 📝 -* 功能 ✨ -* 破坏性更改 🚨 -* 开发技巧 ✅ +* News about FastAPI and friends 🚀 +* Guides 📝 +* Features ✨ +* Breaking changes 🚨 +* Tips and tricks ✅ -## 在推特上关注 FastAPI +## Follow FastAPI on Twitter -在 **Twitter** 上关注 @fastapi 获取 **FastAPI** 的最新消息。🐦 +Follow @fastapi on **Twitter** to get the latest news about **FastAPI**. 🐦 -## 在 GitHub 上为 **FastAPI** 加星 +## Star **FastAPI** in GitHub -您可以在 GitHub 上 **Star** FastAPI(只要点击右上角的星星就可以了): https://github.com/tiangolo/fastapi。⭐️ +You can "star" FastAPI in GitHub (clicking the star button at the top right): https://github.com/tiangolo/fastapi. ⭐️ -**Star** 以后,其它用户就能更容易找到 FastAPI,并了解到已经有其他用户在使用它了。 +By adding a star, other users will be able to find it more easily and see that it has been already useful for others. -## 关注 GitHub 资源库的版本发布 +## Watch the GitHub repository for releases -您还可以在 GitHub 上 **Watch** FastAPI,(点击右上角的 **Watch** 按钮)https://github.com/tiangolo/fastapi。👀 +You can "watch" FastAPI in GitHub (clicking the "watch" button at the top right): https://github.com/tiangolo/fastapi. 👀 -您可以选择只关注发布(**Releases only**)。 +There you can select "Releases only". -这样,您就可以(在电子邮件里)接收到 **FastAPI** 新版发布的通知,及时了解 bug 修复与新功能。 +By doing it, you will receive notifications (in your email) whenever there's a new release (a new version) of **FastAPI** with bug fixes and new features. -## 联系作者 +## Connect with the author -您可以联系项目作者,就是我(Sebastián Ramírez / `tiangolo`)。 +You can connect with me (Sebastián Ramírez / `tiangolo`), the author. -您可以: +You can: -* 在 **GitHub** 上关注我 - * 了解其它我创建的开源项目,或许对您会有帮助 - * 关注我什么时候创建新的开源项目 -* 在 **Twitter** 上关注我 - * 告诉我您使用 FastAPI(我非常乐意听到这种消息) - * 接收我发布公告或新工具的消息 - * 您还可以关注@fastapi on Twitter,这是个独立的账号 -* 在**领英**上联系我 - * 接收我发布公告或新工具的消息(虽然我用 Twitter 比较多) -* 阅读我在 **Dev.to****Medium** 上的文章,或关注我 - * 阅读我的其它想法、文章,了解我创建的工具 - * 关注我,这样就可以随时看到我发布的新文章 +* Follow me on **GitHub**. + * See other Open Source projects I have created that could help you. + * Follow me to see when I create a new Open Source project. +* Follow me on **Twitter** or Mastodon. + * Tell me how you use FastAPI (I love to hear that). + * Hear when I make announcements or release new tools. + * You can also follow @fastapi on Twitter (a separate account). +* Connect with me on **Linkedin**. + * Hear when I make announcements or release new tools (although I use Twitter more often 🤷‍♂). +* Read what I write (or follow me) on **Dev.to** or **Medium**. + * Read other ideas, articles, and read about tools I have created. + * Follow me to read when I publish something new. ## Tweet about **FastAPI** -Tweet about **FastAPI** 让我和大家知道您为什么喜欢 FastAPI。🎉 +Tweet about **FastAPI** and let me and others know why you like it. 🎉 -知道有人使用 **FastAPI**,我会很开心,我也想知道您为什么喜欢 FastAPI,以及您在什么项目/哪些公司使用 FastAPI,等等。 +I love to hear about how **FastAPI** is being used, what you have liked in it, in which project/company are you using it, etc. -## 为 FastAPI 投票 +## Vote for FastAPI -* 在 Slant 上为 **FastAPI** 投票 -* 在 AlternativeTo 上为 **FastAPI** 投票 +* Vote for **FastAPI** in Slant. +* Vote for **FastAPI** in AlternativeTo. +* Say you use **FastAPI** on StackShare. -## 在 GitHub 上帮助其他人解决问题 +## Help others with questions in GitHub -您可以查看现有 issues,并尝试帮助其他人解决问题,说不定您能解决这些问题呢。🤓 +You can try and help others with their questions in: -如果帮助很多人解决了问题,您就有可能成为 [FastAPI 的官方专家](fastapi-people.md#experts){.internal-link target=_blank}。🎉 +* GitHub Discussions +* GitHub Issues -## 监听 GitHub 资源库 +In many cases you might already know the answer for those questions. 🤓 -您可以在 GitHub 上「监听」FastAPI(点击右上角的 "watch" 按钮): https://github.com/tiangolo/fastapi. 👀 +If you are helping a lot of people with their questions, you will become an official [FastAPI Expert](fastapi-people.md#experts){.internal-link target=_blank}. 🎉 -如果您选择 "Watching" 而不是 "Releases only",有人创建新 Issue 时,您会接收到通知。 +Just remember, the most important point is: try to be kind. People come with their frustrations and in many cases don't ask in the best way, but try as best as you can to be kind. 🤗 -然后您就可以尝试并帮助他们解决问题。 +The idea is for the **FastAPI** community to be kind and welcoming. At the same time, don't accept bullying or disrespectful behavior towards others. We have to take care of each other. -## 创建 Issue +--- + +Here's how to help others with questions (in discussions or issues): + +### Understand the question + +* Check if you can understand what is the **purpose** and use case of the person asking. + +* Then check if the question (the vast majority are questions) is **clear**. + +* In many cases the question asked is about an imaginary solution from the user, but there might be a **better** one. If you can understand the problem and use case better, you might be able to suggest a better **alternative solution**. + +* If you can't understand the question, ask for more **details**. + +### Reproduce the problem + +For most of the cases and most of the questions there's something related to the person's **original code**. + +In many cases they will only copy a fragment of the code, but that's not enough to **reproduce the problem**. + +* You can ask them to provide a minimal, reproducible, example, that you can **copy-paste** and run locally to see the same error or behavior they are seeing, or to understand their use case better. + +* If you are feeling too generous, you can try to **create an example** like that yourself, just based on the description of the problem. Just have in mind that this might take a lot of time and it might be better to ask them to clarify the problem first. + +### Suggest solutions + +* After being able to understand the question, you can give them a possible **answer**. + +* In many cases, it's better to understand their **underlying problem or use case**, because there might be a better way to solve it than what they are trying to do. + +### Ask to close + +If they reply, there's a high chance you would have solved their problem, congrats, **you're a hero**! 🦸 + +* Now, if that solved their problem, you can ask them to: + + * In GitHub Discussions: mark the comment as the **answer**. + * In GitHub Issues: **close** the issue. + +## Watch the GitHub repository + +You can "watch" FastAPI in GitHub (clicking the "watch" button at the top right): https://github.com/tiangolo/fastapi. 👀 + +If you select "Watching" instead of "Releases only" you will receive notifications when someone creates a new issue or question. You can also specify that you only want to be notified about new issues, or discussions, or PRs, etc. + +Then you can try and help them solve those questions. + +## Ask Questions + +You can create a new question in the GitHub repository, for example to: + +* Ask a **question** or ask about a **problem**. +* Suggest a new **feature**. + +**Note**: if you do it, then I'm going to ask you to also help others. 😉 + +## Review Pull Requests + +You can help me review pull requests from others. + +Again, please try your best to be kind. 🤗 + +--- + +Here's what to have in mind and how to review a pull request: + +### Understand the problem + +* First, make sure you **understand the problem** that the pull request is trying to solve. It might have a longer discussion in a GitHub Discussion or issue. + +* There's also a good chance that the pull request is not actually needed because the problem can be solved in a **different way**. Then you can suggest or ask about that. + +### Don't worry about style + +* Don't worry too much about things like commit message styles, I will squash and merge customizing the commit manually. + +* Also don't worry about style rules, there are already automatized tools checking that. + +And if there's any other style or consistency need, I'll ask directly for that, or I'll add commits on top with the needed changes. + +### Check the code + +* Check and read the code, see if it makes sense, **run it locally** and see if it actually solves the problem. + +* Then **comment** saying that you did that, that's how I will know you really checked it. + +!!! info + Unfortunately, I can't simply trust PRs that just have several approvals. + + Several times it has happened that there are PRs with 3, 5 or more approvals, probably because the description is appealing, but when I check the PRs, they are actually broken, have a bug, or don't solve the problem they claim to solve. 😅 + + So, it's really important that you actually read and run the code, and let me know in the comments that you did. 🤓 + +* If the PR can be simplified in a way, you can ask for that, but there's no need to be too picky, there might be a lot of subjective points of view (and I will have my own as well 🙈), so it's better if you can focus on the fundamental things. + +### Tests + +* Help me check that the PR has **tests**. + +* Check that the tests **fail** before the PR. 🚨 + +* Then check that the tests **pass** after the PR. ✅ + +* Many PRs don't have tests, you can **remind** them to add tests, or you can even **suggest** some tests yourself. That's one of the things that consume most time and you can help a lot with that. + +* Then also comment what you tried, that way I'll know that you checked it. 🤓 + +## Create a Pull Request + +You can [contribute](contributing.md){.internal-link target=_blank} to the source code with Pull Requests, for example: + +* To fix a typo you found on the documentation. +* To share an article, video, or podcast you created or found about FastAPI by editing this file. + * Make sure you add your link to the start of the corresponding section. +* To help [translate the documentation](contributing.md#translations){.internal-link target=_blank} to your language. + * You can also help to review the translations created by others. +* To propose new documentation sections. +* To fix an existing issue/bug. + * Make sure to add tests. +* To add a new feature. + * Make sure to add tests. + * Make sure to add documentation if it's relevant. -您可以在 GitHub 资源库中创建 Issue,例如: +## Help Maintain FastAPI -* 提出**问题**或**意见** -* 提出新**特性**建议 +Help me maintain **FastAPI**! 🤓 -**注意**:如果您创建 Issue,我会要求您也要帮助别的用户。😉 +There's a lot of work to do, and for most of it, **YOU** can do it. -## 创建 PR +The main tasks that you can do right now are: -您可以创建 PR 为源代码做[贡献](contributing.md){.internal-link target=_blank},例如: +* [Help others with questions in GitHub](#help-others-with-questions-in-github){.internal-link target=_blank} (see the section above). +* [Review Pull Requests](#review-pull-requests){.internal-link target=_blank} (see the section above). -* 修改文档错别字 -* 编辑这个文件,分享 FastAPI 的文章、视频、博客,不论是您自己的,还是您看到的都成 - * 注意,添加的链接要放在对应区块的开头 -* [翻译文档](contributing.md#translations){.internal-link target=_blank} - * 审阅别人翻译的文档 -* 添加新的文档内容 -* 修复现有问题/Bug -* 添加新功能 +Those two tasks are what **consume time the most**. That's the main work of maintaining FastAPI. -## 加入聊天 +If you can help me with that, **you are helping me maintain FastAPI** and making sure it keeps **advancing faster and better**. 🚀 -快加入 👥 Discord 聊天服务器 👥 和 FastAPI 社区里的小伙伴一起哈皮吧。 +## Join the chat -!!! tip "提示" +Join the 👥 Discord chat server 👥 and hang out with others in the FastAPI community. - 如有问题,请在 GitHub Issues 里提问,在这里更容易得到 [FastAPI 专家](fastapi-people.md#experts){.internal-link target=_blank}的帮助。 +!!! tip + For questions, ask them in GitHub Discussions, there's a much better chance you will receive help by the [FastAPI Experts](fastapi-people.md#experts){.internal-link target=_blank}. - 聊天室仅供闲聊。 + Use the chat only for other general conversations. -我们之前还使用过 Gitter chat,但它不支持频道等高级功能,聊天也比较麻烦,所以现在推荐使用 Discord。 +There is also the previous Gitter chat, but as it doesn't have channels and advanced features, conversations are more difficult, so Discord is now the recommended system. -### 别在聊天室里提问 +### Don't use the chat for questions -注意,聊天室更倾向于“闲聊”,经常有人会提出一些笼统得让人难以回答的问题,所以在这里提问一般没人回答。 +Have in mind that as chats allow more "free conversation", it's easy to ask questions that are too general and more difficult to answer, so, you might not receive answers. -GitHub Issues 里提供了模板,指引您提出正确的问题,有利于获得优质的回答,甚至可能解决您还没有想到的问题。而且就算答疑解惑要耗费不少时间,我还是会尽量在 GitHub 里回答问题。但在聊天室里,我就没功夫这么做了。😅 +In GitHub, the template will guide you to write the right question so that you can more easily get a good answer, or even solve the problem yourself even before asking. And in GitHub I can make sure I always answer everything, even if it takes some time. I can't personally do that with the chat systems. 😅 -聊天室里的聊天内容也不如 GitHub 里好搜索,聊天里的问答很容易就找不到了。只有在 GitHub Issues 里的问答才能帮助您成为 [FastAPI 专家](fastapi-people.md#experts){.internal-link target=_blank},在 GitHub Issues 中为您带来更多关注。 +Conversations in the chat systems are also not as easily searchable as in GitHub, so questions and answers might get lost in the conversation. And only the ones in GitHub count to become a [FastAPI Expert](fastapi-people.md#experts){.internal-link target=_blank}, so you will most probably receive more attention in GitHub. -另一方面,聊天室里有成千上万的用户,在这里,您有很大可能遇到聊得来的人。😄 +On the other side, there are thousands of users in the chat systems, so there's a high chance you'll find someone to talk to there, almost all the time. 😄 -## 赞助作者 +## Sponsor the author -您还可以通过 GitHub 赞助商资助本项目的作者(就是我)。 +You can also financially support the author (me) through GitHub sponsors. -给我买杯咖啡 ☕️ 以示感谢 😄 +There you could buy me a coffee ☕️ to say thanks. 😄 -当然您也可以成为 FastAPI 的金牌或银牌赞助商。🏅🎉 +And you can also become a Silver or Gold sponsor for FastAPI. 🏅🎉 -## 赞助 FastAPI 使用的工具 +## Sponsor the tools that power FastAPI -如您在本文档中所见,FastAPI 站在巨人的肩膀上,它们分别是 Starlette 和 Pydantic。 +As you have seen in the documentation, FastAPI stands on the shoulders of giants, Starlette and Pydantic. -您还可以赞助: +You can also sponsor: -* Samuel Colvin (Pydantic) -* Encode (Starlette, Uvicorn) +* Samuel Colvin (Pydantic) +* Encode (Starlette, Uvicorn) --- -谢谢!🚀 +Thanks! 🚀 From b549f8f5c042730934aae191df1dfdf6e6e5c7df Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:17 +0800 Subject: [PATCH 055/163] New translations history-design-future.md (Chinese Simplified) --- docs/zh/docs/history-design-future.md | 76 +++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 docs/zh/docs/history-design-future.md diff --git a/docs/zh/docs/history-design-future.md b/docs/zh/docs/history-design-future.md new file mode 100644 index 0000000000000..ef6ea883213c2 --- /dev/null +++ b/docs/zh/docs/history-design-future.md @@ -0,0 +1,76 @@ +# History, Design and Future + +Some time ago, a **FastAPI** user asked: + +> What’s the history of this project? It seems to have come from nowhere to awesome in a few weeks [...] + +Here's a little bit of that history. + +## Alternatives + +I have been creating APIs with complex requirements for several years (Machine Learning, distributed systems, asynchronous jobs, NoSQL databases, etc), leading several teams of developers. + +As part of that, I needed to investigate, test and use many alternatives. + +The history of **FastAPI** is in great part the history of its predecessors. + +As said in the section [Alternatives](alternatives.md){.internal-link target=_blank}: +
+ **FastAPI** wouldn't exist if not for the previous work of others. + + There have been many tools created before that have helped inspire its creation. + + I have been avoiding the creation of a new framework for several years. First I tried to solve all the features covered by **FastAPI** using many different frameworks, plug-ins, and tools. + + But at some point, there was no other option than creating something that provided all these features, taking the best ideas from previous tools, and combining them in the best way possible, using language features that weren't even available before (Python 3.6+ type hints). +
+ +## Investigation + +By using all the previous alternatives I had the chance to learn from all of them, take ideas, and combine them in the best way I could find for myself and the teams of developers I have worked with. + +For example, it was clear that ideally it should be based on standard Python type hints. + +Also, the best approach was to use already existing standards. + +So, before even starting to code **FastAPI**, I spent several months studying the specs for OpenAPI, JSON Schema, OAuth2, etc. Understanding their relationship, overlap, and differences. + +## Design + +Then I spent some time designing the developer "API" I wanted to have as a user (as a developer using FastAPI). + +I tested several ideas in the most popular Python editors: PyCharm, VS Code, Jedi based editors. + +By the last Python Developer Survey, that covers about 80% of the users. + +It means that **FastAPI** was specifically tested with the editors used by 80% of the Python developers. And as most of the other editors tend to work similarly, all its benefits should work for virtually all editors. + +That way I could find the best ways to reduce code duplication as much as possible, to have completion everywhere, type and error checks, etc. + +All in a way that provided the best development experience for all the developers. + +## Requirements + +After testing several alternatives, I decided that I was going to use **Pydantic** for its advantages. + +Then I contributed to it, to make it fully compliant with JSON Schema, to support different ways to define constraint declarations, and to improve editor support (type checks, autocompletion) based on the tests in several editors. + +During the development, I also contributed to **Starlette**, the other key requirement. + +## Development + +By the time I started creating **FastAPI** itself, most of the pieces were already in place, the design was defined, the requirements and tools were ready, and the knowledge about the standards and specifications was clear and fresh. + +## Future + +By this point, it's already clear that **FastAPI** with its ideas is being useful for many people. + +It is being chosen over previous alternatives for suiting many use cases better. + +Many developers and teams already depend on **FastAPI** for their projects (including me and my team). + +But still, there are many improvements and features to come. + +**FastAPI** has a great future ahead. + +And [your help](help-fastapi.md){.internal-link target=_blank} is greatly appreciated. From 9797d5b5715ccd2435b4283cacfcd74da12658c3 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:18 +0800 Subject: [PATCH 056/163] New translations index.md (Chinese Simplified) --- docs/zh/docs/index.md | 345 ++++++++++++++++++++++-------------------- 1 file changed, 177 insertions(+), 168 deletions(-) diff --git a/docs/zh/docs/index.md b/docs/zh/docs/index.md index 1de2a8d36d09a..ebd74bc8f00d2 100644 --- a/docs/zh/docs/index.md +++ b/docs/zh/docs/index.md @@ -2,43 +2,45 @@ FastAPI

- FastAPI 框架,高性能,易于学习,高效编码,生产可用 + FastAPI framework, high performance, easy to learn, fast to code, ready for production

- - Test + + Test - - Coverage + + Coverage Package version + + Supported Python versions +

--- -**文档**: https://fastapi.tiangolo.com +**Documentation**: https://fastapi.tiangolo.com -**源码**: https://github.com/tiangolo/fastapi +**Source Code**: https://github.com/tiangolo/fastapi --- -FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架,使用 Python 3.6+ 并基于标准的 Python 类型提示。 +FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.7+ based on standard Python type hints. -关键特性: +The key features are: -* **快速**:可与 **NodeJS** 和 **Go** 并肩的极高性能(归功于 Starlette 和 Pydantic)。[最快的 Python web 框架之一](#_11)。 +* **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. Completion 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: OpenAPI (previously known as Swagger) and JSON Schema. -* **高效编码**:提高功能开发速度约 200% 至 300%。* -* **更少 bug**:减少约 40% 的人为(开发者)导致错误。* -* **智能**:极佳的编辑器支持。处处皆可自动补全,减少调试时间。 -* **简单**:设计的易于使用和学习,阅读文档的时间更短。 -* **简短**:使代码重复最小化。通过不同的参数声明实现丰富功能。bug 更少。 -* **健壮**:生产可用级别的代码。还有自动生成的交互式文档。 -* **标准化**:基于(并完全兼容)API 的相关开放标准:OpenAPI (以前被称为 Swagger) 和 JSON Schema。 - -* 根据对某个构建线上应用的内部开发团队所进行的测试估算得出。 +* estimation based on tests on an internal development team, building production applications. ## Sponsors @@ -57,64 +59,70 @@ FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框 Other sponsors -## 评价 +## Opinions -「_[...] 最近我一直在使用 **FastAPI**。[...] 实际上我正在计划将其用于我所在的**微软**团队的所有**机器学习服务**。其中一些服务正被集成进核心 **Windows** 产品和一些 **Office** 产品。_」 +"_[...] 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._" -
Kabir Khan - 微软 (ref)
+
Kabir Khan - Microsoft (ref)
--- -「_我们选择了 **FastAPI** 来创建用于获取**预测结果**的 **REST** 服务。[用于 Ludwig]_」 +"_We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]_" -
Piero Molino,Yaroslav Dudin 和 Sai Sumanth Miryala - Uber (ref)
+
Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - Uber (ref)
--- -「_**Netflix** 非常高兴地宣布,正式开源我们的**危机管理**编排框架:**Dispatch**![使用 **FastAPI** 构建]_」 +"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_" -
Kevin Glisson,Marc Vilanova,Forest Monsen - Netflix (ref)
+
Kevin Glisson, Marc Vilanova, Forest Monsen - Netflix (ref)
--- -「_**FastAPI** 让我兴奋的欣喜若狂。它太棒了!_」 +"_I’m over the moon excited about **FastAPI**. It’s so fun!_" -
Brian Okken - Python Bytes 播客主持人 (ref)
+
Brian Okken - Python Bytes podcast host (ref)
--- -「_老实说,你的作品看起来非常可靠和优美。在很多方面,这就是我想让 **Hug** 成为的样子 - 看到有人实现了它真的很鼓舞人心。_」 +"_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._" -
Timothy Crosley - Hug 作者 (ref)
+
Timothy Crosley - Hug creator (ref)
--- -「_如果你正打算学习一个**现代框架**用来构建 REST API,来看下 **FastAPI** [...] 它快速、易用且易于学习 [...]_」 +"_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 [...]_" + +
Ines Montani - Matthew Honnibal - Explosion AI founders - spaCy creators (ref) - (ref)
+ +--- -「_我们已经将 **API** 服务切换到了 **FastAPI** [...] 我认为你会喜欢它的 [...]_」 +"_If anyone is looking to build a production Python API, I would highly recommend **FastAPI**. It is **beautifully designed**, **simple to use** and **highly scalable**, it has become a **key component** in our API first development strategy and is driving many automations and services such as our Virtual TAC Engineer._" -
Ines Montani - Matthew Honnibal - Explosion AI 创始人 - spaCy 作者 (ref) - (ref)
+
Deon Pillsbury - Cisco (ref)
--- -## **Typer**,命令行中的 FastAPI +## **Typer**, the FastAPI of CLIs -如果你正在开发一个在终端中运行的命令行应用而不是 web API,不妨试下 **Typer**。 +If you are building a CLI app to be used in the terminal instead of a web API, check out **Typer**. -**Typer** 是 FastAPI 的小同胞。它想要成为**命令行中的 FastAPI**。 ⌨️ 🚀 +**Typer** is FastAPI's little sibling. And it's intended to be the **FastAPI of CLIs**. ⌨️ 🚀 -## 依赖 +## Requirements -Python 3.6 及更高版本 +Python 3.7+ -FastAPI 站在以下巨人的肩膀之上: +FastAPI stands on the shoulders of giants: -* Starlette 负责 web 部分。 -* Pydantic 负责数据部分。 +* Starlette for the web parts. +* Pydantic for the data parts. -## 安装 +## Installation
@@ -126,7 +134,7 @@ $ pip install fastapi
-你还会需要一个 ASGI 服务器,生产环境可以使用 Uvicorn 或者 Hypercorn。 +You will also need an ASGI server, for production such as Uvicorn or Hypercorn.
@@ -138,11 +146,11 @@ $ pip install "uvicorn[standard]"
-## 示例 +## Example -### 创建 +### Create it -* 创建一个 `main.py` 文件并写入以下内容: +* Create a file `main.py` with: ```Python from typing import Union @@ -163,9 +171,9 @@ def read_item(item_id: int, q: Union[str, None] = None): ```
-或者使用 async def... +Or use async def... -如果你的代码里会出现 `async` / `await`,请使用 `async def`: +If your code uses `async` / `await`, use `async def`: ```Python hl_lines="9 14" from typing import Union @@ -187,13 +195,13 @@ async def read_item(item_id: int, q: Union[str, None] = None): **Note**: -如果你不知道是否会用到,可以查看文档的 _"In a hurry?"_ 章节中 关于 `async` 和 `await` 的部分。 +If you don't know, check the _"In a hurry?"_ section about `async` and `await` in the docs.
-### 运行 +### Run it -通过以下命令运行服务器: +Run the server with:
@@ -210,54 +218,54 @@ INFO: Application startup complete.
-关于 uvicorn main:app --reload 命令...... +About the command uvicorn main:app --reload... - `uvicorn main:app` 命令含义如下: +The command `uvicorn main:app` refers to: -* `main`:`main.py` 文件(一个 Python "模块")。 -* `app`:在 `main.py` 文件中通过 `app = FastAPI()` 创建的对象。 -* `--reload`:让服务器在更新代码后重新启动。仅在开发时使用该选项。 +* `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.
-### 检查 +### Check it -使用浏览器访问 http://127.0.0.1:8000/items/5?q=somequery。 +Open your browser at http://127.0.0.1:8000/items/5?q=somequery. -你将会看到如下 JSON 响应: +You will see the JSON response as: ```JSON {"item_id": 5, "q": "somequery"} ``` -你已经创建了一个具有以下功能的 API: +You already created an API that: -* 通过 _路径_ `/` 和 `/items/{item_id}` 接受 HTTP 请求。 -* 以上 _路径_ 都接受 `GET` 操作(也被称为 HTTP _方法_)。 -* `/items/{item_id}` _路径_ 有一个 _路径参数_ `item_id` 并且应该为 `int` 类型。 -* `/items/{item_id}` _路径_ 有一个可选的 `str` 类型的 _查询参数_ `q`。 +* Receives HTTP requests in the _paths_ `/` and `/items/{item_id}`. +* Both _paths_ take `GET` operations (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`. -### 交互式 API 文档 +### Interactive API docs -现在访问 http://127.0.0.1:8000/docs。 +Now go to http://127.0.0.1:8000/docs. -你会看到自动生成的交互式 API 文档(由 Swagger UI生成): +You will see the automatic interactive API documentation (provided by Swagger UI): ![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) -### 可选的 API 文档 +### Alternative API docs -访问 http://127.0.0.1:8000/redoc。 +And now, go to http://127.0.0.1:8000/redoc. -你会看到另一个自动生成的文档(由 ReDoc 生成): +You will see the alternative automatic documentation (provided by ReDoc): ![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) -## 示例升级 +## Example upgrade -现在修改 `main.py` 文件来从 `PUT` 请求中接收请求体。 +Now modify the file `main.py` to receive a body from a `PUT` request. -我们借助 Pydantic 来使用标准的 Python 类型声明请求体。 +Declare the body using standard Python types, thanks to Pydantic. ```Python hl_lines="4 9-12 25-27" from typing import Union @@ -289,173 +297,174 @@ def update_item(item_id: int, item: Item): return {"item_name": item.name, "item_id": item_id} ``` -服务器将会自动重载(因为在上面的步骤中你向 `uvicorn` 命令添加了 `--reload` 选项)。 +The server should reload automatically (because you added `--reload` to the `uvicorn` command above). -### 交互式 API 文档升级 +### Interactive API docs upgrade -访问 http://127.0.0.1:8000/docs。 +Now go to http://127.0.0.1:8000/docs. -* 交互式 API 文档将会自动更新,并加入新的请求体: +* 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) -* 点击「Try it out」按钮,之后你可以填写参数并直接调用 API: +* 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) -* 然后点击「Execute」按钮,用户界面将会和 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: ![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png) -### 可选文档升级 +### Alternative API docs upgrade -访问 http://127.0.0.1:8000/redoc。 +And now, go to http://127.0.0.1:8000/redoc. -* 可选文档同样会体现新加入的请求参数和请求体: +* 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. -你使用了标准的现代 Python 类型来完成声明。 +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. -只需要使用标准的 **Python 3.6 及更高版本**。 +Just standard **Python 3.7+**. -举个例子,比如声明 `int` 类型: +For example, for an `int`: ```Python item_id: int ``` -或者一个更复杂的 `Item` 模型: +or for a more complex `Item` model: ```Python item: Item ``` -......在进行一次声明之后,你将获得: - -* 编辑器支持,包括: - * 自动补全 - * 类型检查 -* 数据校验: - * 在校验失败时自动生成清晰的错误信息 - * 对多层嵌套的 JSON 对象依然执行校验 -* 转换 来自网络请求的输入数据为 Python 数据类型。包括以下数据: - * JSON - * 路径参数 - * 查询参数 - * Cookies - * 请求头 - * 表单 - * 文件 -* 转换 输出的数据:转换 Python 数据类型为供网络传输的 JSON 数据: - * 转换 Python 基础类型 (`str`、 `int`、 `float`、 `bool`、 `list` 等) - * `datetime` 对象 - * `UUID` 对象 - * 数据库模型 - * ......以及更多其他类型 -* 自动生成的交互式 API 文档,包括两种可选的用户界面: - * Swagger UI - * ReDoc +...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. +* Conversion of input data: coming from the network to Python data and types. Reading from: + * JSON. + * Path parameters. + * Query parameters. + * Cookies. + * Headers. + * Forms. + * Files. +* Conversion 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. --- -回到前面的代码示例,**FastAPI** 将会: - -* 校验 `GET` 和 `PUT` 请求的路径中是否含有 `item_id`。 -* 校验 `GET` 和 `PUT` 请求中的 `item_id` 是否为 `int` 类型。 - * 如果不是,客户端将会收到清晰有用的错误信息。 -* 检查 `GET` 请求中是否有命名为 `q` 的可选查询参数(比如 `http://127.0.0.1:8000/items/foo?q=somequery`)。 - * 因为 `q` 被声明为 `= None`,所以它是可选的。 - * 如果没有 `None` 它将会是必需的 (如 `PUT` 例子中的请求体)。 -* 对于访问 `/items/{item_id}` 的 `PUT` 请求,将请求体读取为 JSON 并: - * 检查是否有必需属性 `name` 并且值为 `str` 类型 。 - * 检查是否有必需属性 `price` 并且值为 `float` 类型。 - * 检查是否有可选属性 `is_offer`, 如果有的话值应该为 `bool` 类型。 - * 以上过程对于多层嵌套的 JSON 对象同样也会执行 -* 自动对 JSON 进行转换或转换成 JSON。 -* 通过 OpenAPI 文档来记录所有内容,可被用于: - * 交互式文档系统 - * 许多编程语言的客户端代码自动生成系统 -* 直接提供 2 种交互式文档 web 界面。 +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 Tutorial - User Guide. -**剧透警告**: 教程 - 用户指南中的内容有: +**Spoiler alert**: the tutorial - user guide includes: -* 对来自不同地方的参数进行声明,如:**请求头**、**cookies**、**form 表单**以及**上传的文件**。 -* 如何设置**校验约束**如 `maximum_length` 或者 `regex`。 -* 一个强大并易于使用的 **依赖注入** 系统。 -* 安全性和身份验证,包括通过 **JWT 令牌**和 **HTTP 基本身份认证**来支持 **OAuth2**。 -* 更进阶(但同样简单)的技巧来声明 **多层嵌套 JSON 模型** (借助 Pydantic)。 -* 许多额外功能(归功于 Starlette)比如: +* 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 **Dependency Injection** 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). +* **GraphQL** integration with Strawberry and other libraries. +* Many extra features (thanks to Starlette) as: * **WebSockets** - * **GraphQL** - * 基于 HTTPX 和 `pytest` 的极其简单的测试 + * extremely easy tests based on HTTPX and `pytest` * **CORS** * **Cookie Sessions** - * ......以及更多 + * ...and more. -## 性能 +## Performance -独立机构 TechEmpower 所作的基准测试结果显示,基于 Uvicorn 运行的 **FastAPI** 程序是 最快的 Python web 框架之一,仅次于 Starlette 和 Uvicorn 本身(FastAPI 内部使用了它们)。(*) +Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as one of the fastest Python frameworks available, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*) -想了解更多,请查阅 基准测试 章节。 +To understand more about it, see the section Benchmarks. -## 可选依赖 +## Optional Dependencies -用于 Pydantic: +Used by Pydantic: -* email_validator - 用于 email 校验。 +* email_validator - for email validation. +* pydantic-settings - for settings management. +* pydantic-extra-types - for extra types to be used with Pydantic. -用于 Starlette: +Used by Starlette: -* httpx - 使用 `TestClient` 时安装。 -* jinja2 - 使用默认模板配置时安装。 -* python-multipart - 需要通过 `request.form()` 对表单进行「解析」时安装。 -* itsdangerous - 需要 `SessionMiddleware` 支持时安装。 -* pyyaml - 使用 Starlette 提供的 `SchemaGenerator` 时安装(有 FastAPI 你可能并不需要它)。 -* graphene - 需要 `GraphQLApp` 支持时安装。 -* ujson - 使用 `UJSONResponse` 时安装。 +* httpx - Required if you want to use the `TestClient`. +* jinja2 - Required if you want to use the default template configuration. +* python-multipart - Required if you want to support form "parsing", with `request.form()`. +* itsdangerous - Required for `SessionMiddleware` support. +* pyyaml - Required for Starlette's `SchemaGenerator` support (you probably don't need it with FastAPI). +* ujson - Required if you want to use `UJSONResponse`. -用于 FastAPI / Starlette: +Used by FastAPI / Starlette: -* uvicorn - 用于加载和运行你的应用程序的服务器。 -* orjson - 使用 `ORJSONResponse` 时安装。 +* uvicorn - for the server that loads and serves your application. +* orjson - Required if you want to use `ORJSONResponse`. -你可以通过 `pip install fastapi[all]` 命令来安装以上所有依赖。 +You can install all of these with `pip install "fastapi[all]"`. -## 许可协议 +## License -该项目遵循 MIT 许可协议。 +This project is licensed under the terms of the MIT license. From 91e9ecbde6b78e1646da95d8e0e337f86032118f Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:18 +0800 Subject: [PATCH 057/163] New translations newsletter.md (Chinese Simplified) --- docs/zh/docs/newsletter.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 docs/zh/docs/newsletter.md diff --git a/docs/zh/docs/newsletter.md b/docs/zh/docs/newsletter.md new file mode 100644 index 0000000000000..782db1353c8d0 --- /dev/null +++ b/docs/zh/docs/newsletter.md @@ -0,0 +1,5 @@ +# FastAPI and friends newsletter + + + + From 7e4cf98c7dc8b295f9c3d222c8078d680206bc12 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:19 +0800 Subject: [PATCH 058/163] New translations project-generation.md (Chinese Simplified) --- docs/zh/docs/project-generation.md | 84 ++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 docs/zh/docs/project-generation.md diff --git a/docs/zh/docs/project-generation.md b/docs/zh/docs/project-generation.md new file mode 100644 index 0000000000000..8ba34fa11200d --- /dev/null +++ b/docs/zh/docs/project-generation.md @@ -0,0 +1,84 @@ +# Project Generation - Template + +You can use a project generator to get started, as it includes a lot of the initial set up, security, database and some API endpoints already done for you. + +A project generator will always have a very opinionated setup that you should update and adapt for your own needs, but it might be a good starting point for your project. + +## Full Stack FastAPI PostgreSQL + +GitHub: https://github.com/tiangolo/full-stack-fastapi-postgresql + +### Full Stack FastAPI PostgreSQL - Features + +* Full **Docker** integration (Docker based). +* Docker Swarm Mode deployment. +* **Docker Compose** integration and optimization for local development. +* **Production ready** Python web server using Uvicorn and Gunicorn. +* Python **FastAPI** backend: + * **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). + * **Intuitive**: Great editor support. Completion 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. + * **Robust**: Get production-ready code. With automatic interactive documentation. + * **Standards-based**: Based on (and fully compatible with) the open standards for APIs: OpenAPI and JSON Schema. + * **Many other features** including automatic validation, serialization, interactive documentation, authentication with OAuth2 JWT tokens, etc. +* **Secure password** hashing by default. +* **JWT token** authentication. +* **SQLAlchemy** models (independent of Flask extensions, so they can be used with Celery workers directly). +* Basic starting models for users (modify and remove as you need). +* **Alembic** migrations. +* **CORS** (Cross Origin Resource Sharing). +* **Celery** worker that can import and use models and code from the rest of the backend selectively. +* REST backend tests based on **Pytest**, integrated with Docker, so you can test the full API interaction, independent on the database. As it runs in Docker, it can build a new data store from scratch each time (so you can use ElasticSearch, MongoDB, CouchDB, or whatever you want, and just test that the API works). +* Easy Python integration with **Jupyter Kernels** for remote or in-Docker development with extensions like Atom Hydrogen or Visual Studio Code Jupyter. +* **Vue** frontend: + * Generated with Vue CLI. + * **JWT Authentication** handling. + * Login view. + * After login, main dashboard view. + * Main dashboard with user creation and edition. + * Self user edition. + * **Vuex**. + * **Vue-router**. + * **Vuetify** for beautiful material design components. + * **TypeScript**. + * Docker server based on **Nginx** (configured to play nicely with Vue-router). + * Docker multi-stage building, so you don't need to save or commit compiled code. + * Frontend tests ran at build time (can be disabled too). + * Made as modular as possible, so it works out of the box, but you can re-generate with Vue CLI or create it as you need, and re-use what you want. +* **PGAdmin** for PostgreSQL database, you can modify it to use PHPMyAdmin and MySQL easily. +* **Flower** for Celery jobs monitoring. +* Load balancing between frontend and backend with **Traefik**, so you can have both under the same domain, separated by path, but served by different containers. +* Traefik integration, including Let's Encrypt **HTTPS** certificates automatic generation. +* GitLab **CI** (continuous integration), including frontend and backend testing. + +## Full Stack FastAPI Couchbase + +GitHub: https://github.com/tiangolo/full-stack-fastapi-couchbase + +⚠️ **WARNING** ⚠️ + +If you are starting a new project from scratch, check the alternatives here. + +For example, the project generator Full Stack FastAPI PostgreSQL might be a better alternative, as it is actively maintained and used. And it includes all the new features and improvements. + +You are still free to use the Couchbase-based generator if you want to, it should probably still work fine, and if you already have a project generated with it that's fine as well (and you probably already updated it to suit your needs). + +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: https://github.com/microsoft/cookiecutter-spacy-fastapi + +### 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. From f015c66f5dab1aa39e2110770c77ff844be329ca Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:21 +0800 Subject: [PATCH 059/163] New translations python-types.md (Chinese Simplified) --- docs/zh/docs/python-types.md | 496 ++++++++++++++++++++++++++--------- 1 file changed, 374 insertions(+), 122 deletions(-) diff --git a/docs/zh/docs/python-types.md b/docs/zh/docs/python-types.md index 6cdb4b58838d7..34350d70befba 100644 --- a/docs/zh/docs/python-types.md +++ b/docs/zh/docs/python-types.md @@ -1,139 +1,139 @@ -# Python 类型提示简介 +# Python Types Intro -**Python 3.6+ 版本**加入了对"类型提示"的支持。 +Python has support for optional "type hints" (also called "type annotations"). -这些**"类型提示"**是一种新的语法(在 Python 3.6 版本加入)用来声明一个变量的类型。 +These **"type hints"** or annotations are a special syntax that allow declaring the type of a variable. -通过声明变量的类型,编辑器和一些工具能给你提供更好的支持。 +By declaring types for your variables, editors and tools can give you better support. -这只是一个关于 Python 类型提示的**快速入门 / 复习**。它仅涵盖与 **FastAPI** 一起使用所需的最少部分...实际上只有很少一点。 +This is just a **quick tutorial / refresher** about Python type hints. It covers only the minimum necessary to use them with **FastAPI**... which is actually very little. -整个 **FastAPI** 都基于这些类型提示构建,它们带来了许多优点和好处。 +**FastAPI** is all based on these type hints, they give it many advantages and benefits. -但即使你不会用到 **FastAPI**,了解一下类型提示也会让你从中受益。 +But even if you never use **FastAPI**, you would benefit from learning a bit about them. !!! note - 如果你已经精通 Python,并且了解关于类型提示的一切知识,直接跳到下一章节吧。 + If you are a Python expert, and you already know everything about type hints, skip to the next chapter. -## 动机 +## Motivation -让我们从一个简单的例子开始: +Let's start with a simple example: ```Python {!../../../docs_src/python_types/tutorial001.py!} ``` -运行这段程序将输出: +Calling this program outputs: ``` John Doe ``` -这个函数做了下面这些事情: +The function does the following: -* 接收 `first_name` 和 `last_name` 参数。 -* 通过 `title()` 将每个参数的第一个字母转换为大写形式。 -* 中间用一个空格来拼接它们。 +* Takes a `first_name` and `last_name`. +* Converts the first letter of each one to upper case with `title()`. +* Concatenates them with a space in the middle. ```Python hl_lines="2" {!../../../docs_src/python_types/tutorial001.py!} ``` -### 修改示例 +### Edit it -这是一个非常简单的程序。 +It's a very simple program. -现在假设你将从头开始编写这段程序。 +But now imagine that you were writing it from scratch. -在某一时刻,你开始定义函数,并且准备好了参数...。 +At some point you would have started the definition of the function, you had the parameters ready... -现在你需要调用一个"将第一个字母转换为大写形式的方法"。 +But then you have to call "that method that converts the first letter to upper case". -等等,那个方法是什么来着?`upper`?还是 `uppercase`?`first_uppercase`?`capitalize`? +Was it `upper`? Was it `uppercase`? `first_uppercase`? `capitalize`? -然后你尝试向程序员老手的朋友——编辑器自动补全寻求帮助。 +Then, you try with the old programmer's friend, editor autocompletion. -输入函数的第一个参数 `first_name`,输入点号(`.`)然后敲下 `Ctrl+Space` 来触发代码补全。 +You type the first parameter of the function, `first_name`, then a dot (`.`) and then hit `Ctrl+Space` to trigger the completion. -但遗憾的是并没有起什么作用: +But, sadly, you get nothing useful: - + -### 添加类型 +### Add types -让我们来修改上面例子的一行代码。 +Let's modify a single line from the previous version. -我们将把下面这段代码中的函数参数从: +We will change exactly this fragment, the parameters of the function, from: ```Python first_name, last_name ``` -改成: +to: ```Python first_name: str, last_name: str ``` -就是这样。 +That's it. -这些就是"类型提示": +Those are the "type hints": ```Python hl_lines="1" {!../../../docs_src/python_types/tutorial002.py!} ``` -这和声明默认值是不同的,例如: +That is not the same as declaring default values like would be with: ```Python first_name="john", last_name="doe" ``` -这两者不一样。 +It's a different thing. -我们用的是冒号(`:`),不是等号(`=`)。 +We are using colons (`:`), not equals (`=`). -而且添加类型提示一般不会改变原来的运行结果。 +And adding type hints normally doesn't change what happens from what would happen without them. -现在假设我们又一次正在创建这个函数,这次添加了类型提示。 +But now, imagine you are again in the middle of creating that function, but with type hints. -在同样的地方,通过 `Ctrl+Space` 触发自动补全,你会发现: +At the same point, you try to trigger the autocomplete with `Ctrl+Space` and you see: - + -这样,你可以滚动查看选项,直到你找到看起来眼熟的那个: +With that, you can scroll, seeing the options, until you find the one that "rings a bell": - + -## 更多动机 +## More motivation -下面是一个已经有类型提示的函数: +Check this function, it already has type hints: ```Python hl_lines="1" {!../../../docs_src/python_types/tutorial003.py!} ``` -因为编辑器已经知道了这些变量的类型,所以不仅能对代码进行补全,还能检查其中的错误: +Because the editor knows the types of the variables, you don't only get completion, you also get error checks: - + -现在你知道了必须先修复这个问题,通过 `str(age)` 把 `age` 转换成字符串: +Now you know that you have to fix it, convert `age` to a string with `str(age)`: ```Python hl_lines="2" {!../../../docs_src/python_types/tutorial004.py!} ``` -## 声明类型 +## Declaring types -你刚刚看到的就是声明类型提示的主要场景。用于函数的参数。 +You just saw the main place to declare type hints. As function parameters. -这也是你将在 **FastAPI** 中使用它们的主要场景。 +This is also the main place you would use them with **FastAPI**. -### 简单类型 +### Simple types -不只是 `str`,你能够声明所有的标准 Python 类型。 +You can declare all the standard Python types, not only `str`. -比如以下类型: +You can use, for example: * `int` * `float` @@ -144,143 +144,395 @@ John Doe {!../../../docs_src/python_types/tutorial005.py!} ``` -### 嵌套类型 +### Generic types with type parameters -有些容器数据结构可以包含其他的值,比如 `dict`、`list`、`set` 和 `tuple`。它们内部的值也会拥有自己的类型。 +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. -你可以使用 Python 的 `typing` 标准库来声明这些类型以及子类型。 +These types that have internal types are called "**generic**" types. And it's possible to declare them, even with their internal types. -它专门用来支持这些类型提示。 +To declare those types and the internal types, you can use the standard Python module `typing`. It exists specifically to support these type hints. -#### 列表 +#### Newer versions of Python -例如,让我们来定义一个由 `str` 组成的 `list` 变量。 +The syntax using `typing` is **compatible** with all versions, from Python 3.6 to the latest ones, including Python 3.9, Python 3.10, etc. -从 `typing` 模块导入 `List`(注意是大写的 `L`): +As Python advances, **newer versions** come with improved support for these type annotations and in many cases you won't even need to import and use the `typing` module to declare the type annotations. -```Python hl_lines="1" -{!../../../docs_src/python_types/tutorial006.py!} -``` +If you can choose a more recent version of Python for your project, you will be able to take advantage of that extra simplicity. -同样以冒号(`:`)来声明这个变量。 +In all the docs there are examples compatible with each version of Python (when there's a difference). -输入 `List` 作为类型。 +For example "**Python 3.6+**" means it's compatible with Python 3.6 or above (including 3.7, 3.8, 3.9, 3.10, etc). And "**Python 3.9+**" means it's compatible with Python 3.9 or above (including 3.10, etc). -由于列表是带有"子类型"的类型,所以我们把子类型放在方括号中: +If you can use the **latest versions of Python**, use the examples for the latest version, those will have the **best and simplest syntax**, for example, "**Python 3.10+**". -```Python hl_lines="4" -{!../../../docs_src/python_types/tutorial006.py!} -``` +#### List + +For example, let's define a variable to be a `list` of `str`. + +=== "Python 3.9+" + + Declare the variable, with the same colon (`:`) syntax. + + As the type, put `list`. + + As the list is a type that contains some internal types, you put them in square brackets: + + ```Python hl_lines="1" + {!> ../../../docs_src/python_types/tutorial006_py39.py!} + ``` + +=== "Python 3.6+" + + From `typing`, import `List` (with a capital `L`): + + ``` Python hl_lines="1" + {!> ../../../docs_src/python_types/tutorial006.py!} + ``` + + + Declare the variable, with the same colon (`:`) syntax. + + As the type, put the `List` that you imported from `typing`. + + 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!} + ``` + +!!! info + Those internal types in the square brackets are called "type parameters". + + In this case, `str` is the type parameter passed to `List` (or `list` in Python 3.9 and above). + +That means: "the variable `items` is a `list`, and each of the items in this list is a `str`". + +!!! tip + If you use Python 3.9 or above, you don't have to import `List` from `typing`, you can use the same regular `list` type instead. + +By doing that, your editor can provide support even while processing items from the list: + + + +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. + +#### Tuple and Set + +You would do the same to declare `tuple`s and `set`s: + +=== "Python 3.9+" + + ```Python hl_lines="1" + {!> ../../../docs_src/python_types/tutorial007_py39.py!} + ``` -这表示:"变量 `items` 是一个 `list`,并且这个列表里的每一个元素都是 `str`"。 +=== "Python 3.6+" -这样,即使在处理列表中的元素时,你的编辑器也可以提供支持。 + ```Python hl_lines="1 4" + {!> ../../../docs_src/python_types/tutorial007.py!} + ``` -没有类型,几乎是不可能实现下面这样: +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`. -注意,变量 `item` 是列表 `items` 中的元素之一。 +#### Dict -而且,编辑器仍然知道它是一个 `str`,并为此提供了支持。 +To define a `dict`, you pass 2 type parameters, separated by commas. -#### 元组和集合 +The first type parameter is for the keys of the `dict`. -声明 `tuple` 和 `set` 的方法也是一样的: +The second type parameter is for the values of the `dict`: + +=== "Python 3.9+" + + ```Python hl_lines="1" + {!> ../../../docs_src/python_types/tutorial008_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="1 4" + {!> ../../../docs_src/python_types/tutorial008.py!} + ``` + +This means: + +* The variable `prices` is a `dict`: + * 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). + +#### Union + +You can declare that a variable can be any of **several types**, for example, an `int` or a `str`. + +In Python 3.6 and above (including Python 3.10) you can use the `Union` type from `typing` and put inside the square brackets the possible types to accept. + +In Python 3.10 there's also a **new syntax** where you can put the possible types separated by a vertical bar (`|`). + +=== "Python 3.10+" + + ```Python hl_lines="1" + {!> ../../../docs_src/python_types/tutorial008b_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="1 4" + {!> ../../../docs_src/python_types/tutorial008b.py!} + ``` + +In both cases this means that `item` could be an `int` or a `str`. + +#### Possibly `None` + +You can declare that a value could have a type, like `str`, but that it could also be `None`. + +In Python 3.6 and above (including Python 3.10) you can declare it by importing and using `Optional` from the `typing` module. ```Python hl_lines="1 4" -{!../../../docs_src/python_types/tutorial007.py!} +{!../../../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. + +`Optional[Something]` is actually a shortcut for `Union[Something, None]`, they are equivalent. + +This also means that in Python 3.10, you can use `Something | None`: + +=== "Python 3.10+" + + ```Python hl_lines="1" + {!> ../../../docs_src/python_types/tutorial009_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="1 4" + {!> ../../../docs_src/python_types/tutorial009.py!} + ``` + +=== "Python 3.6+ alternative" + + ```Python hl_lines="1 4" + {!> ../../../docs_src/python_types/tutorial009b.py!} + ``` + +#### Using `Union` or `Optional` -* 变量 `items_t` 是一个 `tuple`,其中的前两个元素都是 `int` 类型, 最后一个元素是 `str` 类型。 -* 变量 `items_s` 是一个 `set`,其中的每个元素都是 `bytes` 类型。 +If you are using a Python version below 3.10, here's a tip from my very **subjective** point of view: -#### 字典 +* 🚨 Avoid using `Optional[SomeType]` +* Instead ✨ **use `Union[SomeType, None]`** ✨. -定义 `dict` 时,需要传入两个子类型,用逗号进行分隔。 +Both are equivalent and underneath they are the same, but I would recommend `Union` instead of `Optional` because the word "**optional**" would seem to imply that the value is optional, and it actually means "it can be `None`", even if it's not optional and is still required. -第一个子类型声明 `dict` 的所有键。 +I think `Union[SomeType, None]` is more explicit about what it means. -第二个子类型声明 `dict` 的所有值: +It's just about the words and names. But those words can affect how you and your teammates think about the code. + +As an example, let's take this function: + +```Python hl_lines="1 4" +{!../../../docs_src/python_types/tutorial009c.py!} +``` + +The parameter `name` is defined as `Optional[str]`, but it is **not optional**, you cannot call the function without the parameter: + +```Python +say_hi() # Oh, no, this throws an error! 😱 +``` + +The `name` parameter is **still required** (not *optional*) because it doesn't have a default value. Still, `name` accepts `None` as the value: + +```Python +say_hi(name=None) # This works, None is valid 🎉 +``` + +The good news is, once you are on Python 3.10 you won't have to worry about that, as you will be able to simply use `|` to define unions of types: ```Python hl_lines="1 4" -{!../../../docs_src/python_types/tutorial008.py!} +{!../../../docs_src/python_types/tutorial009c_py310.py!} ``` -这表示: +And then you won't have to worry about names like `Optional` and `Union`. 😎 + +#### Generic types + +These types that take type parameters in square brackets are called **Generic types** or **Generics**, for example: + +=== "Python 3.10+" + + You can use the same builtin types as generics (with square brackets and types inside): + + * `list` + * `tuple` + * `set` + * `dict` + + And the same as with Python 3.6, from the `typing` module: + + * `Union` + * `Optional` (the same as with Python 3.6) + * ...and others. + + In Python 3.10, as an alternative to using the generics `Union` and `Optional`, you can use the vertical bar (`|`) to declare unions of types, that's a lot better and simpler. + +=== "Python 3.9+" + + You can use the same builtin types as generics (with square brackets and types inside): + + * `list` + * `tuple` + * `set` + * `dict` + + And the same as with Python 3.6, from the `typing` module: + + * `Union` + * `Optional` + * ...and others. + +=== "Python 3.6+" -* 变量 `prices` 是一个 `dict`: - * 这个 `dict` 的所有键为 `str` 类型(可以看作是字典内每个元素的名称)。 - * 这个 `dict` 的所有值为 `float` 类型(可以看作是字典内每个元素的价格)。 + * `List` + * `Tuple` + * `Set` + * `Dict` + * `Union` + * `Optional` + * ...and others. -### 类作为类型 +### Classes as types -你也可以将类声明为变量的类型。 +You can also declare a class as the type of a variable. -假设你有一个名为 `Person` 的类,拥有 name 属性: +Let's say you have a class `Person`, with a name: ```Python hl_lines="1-3" {!../../../docs_src/python_types/tutorial010.py!} ``` -接下来,你可以将一个变量声明为 `Person` 类型: +Then you can declare a variable to be of type `Person`: ```Python hl_lines="6" {!../../../docs_src/python_types/tutorial010.py!} ``` -然后,你将再次获得所有的编辑器支持: +And then, again, you get all the editor support: - + -## Pydantic 模型 +Notice that this means "`one_person` is an **instance** of the class `Person`". -Pydantic 是一个用来用来执行数据校验的 Python 库。 +It doesn't mean "`one_person` is the **class** called `Person`". -你可以将数据的"结构"声明为具有属性的类。 +## Pydantic models -每个属性都拥有类型。 +Pydantic is a Python library to perform data validation. -接着你用一些值来创建这个类的实例,这些值会被校验,并被转换为适当的类型(在需要的情况下),返回一个包含所有数据的对象。 +You declare the "shape" of the data as classes with attributes. -然后,你将获得这个对象的所有编辑器支持。 +And each attribute has a type. -下面的例子来自 Pydantic 官方文档: +Then you create an instance of that class with some values and it will validate the values, convert them to the appropriate type (if that's the case) and give you an object with all the data. -```Python -{!../../../docs_src/python_types/tutorial010.py!} -``` +And you get all the editor support with that resulting object. + +An example from the official Pydantic docs: + +=== "Python 3.10+" + + ```Python + {!> ../../../docs_src/python_types/tutorial011_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python + {!> ../../../docs_src/python_types/tutorial011_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python + {!> ../../../docs_src/python_types/tutorial011.py!} + ``` !!! info - 想进一步了解 Pydantic,请阅读其文档. + To learn more about Pydantic, check its docs. + +**FastAPI** is all based on Pydantic. + +You will see a lot more of all this in practice in the [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank}. + +!!! tip + Pydantic has a special behavior when you use `Optional` or `Union[Something, None]` without a default value, you can read more about it in the Pydantic docs about Required Optional fields. + +## Type Hints with Metadata Annotations + +Python also has a feature that allows putting **additional metadata** in these type hints using `Annotated`. + +=== "Python 3.9+" + + In Python 3.9, `Annotated` is part of the standard library, so you can import it from `typing`. + + ```Python hl_lines="1 4" + {!> ../../../docs_src/python_types/tutorial013_py39.py!} + ``` + +=== "Python 3.6+" + + In versions below Python 3.9, you import `Annotated` from `typing_extensions`. + + It will already be installed with **FastAPI**. + + ```Python hl_lines="1 4" + {!> ../../../docs_src/python_types/tutorial013.py!} + ``` + +Python itself doesn't do anything with this `Annotated`. And for editors and other tools, the type is still `str`. + +But you can use this space in `Annotated` to provide **FastAPI** with additional metadata about how you want your application to behave. + +The important thing to remember is that ***the first ***type parameter****** you pass to `Annotated` is the **actual type**. The rest, is just metadata for other tools. + +For now, you just need to know that `Annotated` exists, and that it's standard Python. 😎 + +Later you will see how **powerful** it can be. -整个 **FastAPI** 建立在 Pydantic 的基础之上。 +!!! tip + The fact that this is **standard Python** means that you will still get the **best possible developer experience** in your editor, with the tools you use to analyze and refactor your code, etc. ✨ -实际上你将在 [教程 - 用户指南](tutorial/index.md){.internal-link target=_blank} 看到很多这种情况。 + And also that your code will be very compatible with many other Python tools and libraries. 🚀 -## **FastAPI** 中的类型提示 +## Type hints in **FastAPI** -**FastAPI** 利用这些类型提示来做下面几件事。 +**FastAPI** takes advantage of these type hints to do several things. -使用 **FastAPI** 时用类型提示声明参数可以获得: +With **FastAPI** you declare parameters with type hints and you get: -* **编辑器支持**。 -* **类型检查**。 +* **Editor support**. +* **Type checks**. -...并且 **FastAPI** 还会用这些类型声明来: +...and **FastAPI** uses the same declarations to: -* **定义参数要求**:声明对请求路径参数、查询参数、请求头、请求体、依赖等的要求。 -* **转换数据**:将来自请求的数据转换为需要的类型。 -* **校验数据**: 对于每一个请求: - * 当数据校验失败时自动生成**错误信息**返回给客户端。 -* 使用 OpenAPI **记录** API: - * 然后用于自动生成交互式文档的用户界面。 +* **Define requirements**: from request path parameters, query parameters, headers, bodies, dependencies, etc. +* **Convert data**: from the request to the required type. +* **Validate data**: coming from each request: + * Generating **automatic errors** returned to the client when the data is invalid. +* **Document** the API using OpenAPI: + * which is then used by the automatic interactive documentation user interfaces. -听上去有点抽象。不过不用担心。你将在 [教程 - 用户指南](tutorial/index.md){.internal-link target=_blank} 中看到所有的实战。 +This might all sound abstract. Don't worry. You'll see all this in action in the [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank}. -最重要的是,通过使用标准的 Python 类型,只需要在一个地方声明(而不是添加更多的类、装饰器等),**FastAPI** 会为你完成很多的工作。 +The important thing is that by using standard Python types, in a single place (instead of adding more classes, decorators, etc), **FastAPI** will do a lot of the work for you. !!! info - 如果你已经阅读了所有教程,回过头来想了解有关类型的更多信息,来自 `mypy` 的"速查表"是不错的资源。 + If you already went through all the tutorial and came back to see more about types, a good resource is the "cheat sheet" from `mypy`. From 254be13bc5753c47dff19e9525d74a6aec0a97a5 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:21 +0800 Subject: [PATCH 060/163] New translations background-tasks.md (Chinese Simplified) --- docs/zh/docs/tutorial/background-tasks.md | 126 ++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 docs/zh/docs/tutorial/background-tasks.md diff --git a/docs/zh/docs/tutorial/background-tasks.md b/docs/zh/docs/tutorial/background-tasks.md new file mode 100644 index 0000000000000..1782971922ab2 --- /dev/null +++ b/docs/zh/docs/tutorial/background-tasks.md @@ -0,0 +1,126 @@ +# Background Tasks + +You can define background tasks to be run *after* returning a response. + +This is useful for operations that need to happen after a request, but that the client doesn't really have to be waiting for the operation to complete before receiving the response. + +This includes, for example: + +* Email notifications sent after performing an action: + * As connecting to an email server and sending an email tends to be "slow" (several seconds), you can return the response right away and send the email notification in the background. +* Processing data: + * For example, let's say you receive a file that must go through a slow process, you can return a response of "Accepted" (HTTP 202) and process it in the background. + +## Using `BackgroundTasks` + +First, import `BackgroundTasks` and define a parameter in your *path operation function* with a type declaration of `BackgroundTasks`: + +```Python hl_lines="1 13" +{!../../../docs_src/background_tasks/tutorial001.py!} +``` + +**FastAPI** will create the object of type `BackgroundTasks` for you and pass it as that parameter. + +## Create a task function + +Create a function to be run as the background task. + +It is just a standard function that can receive parameters. + +It can be an `async def` or normal `def` function, **FastAPI** will know how to handle it correctly. + +In this case, the task function will write to a file (simulating sending an email). + +And as the write operation doesn't use `async` and `await`, we define the function with normal `def`: + +```Python hl_lines="6-9" +{!../../../docs_src/background_tasks/tutorial001.py!} +``` + +## Add the background task + +Inside of your *path operation function*, pass your task function to the *background tasks* object with the method `.add_task()`: + +```Python hl_lines="14" +{!../../../docs_src/background_tasks/tutorial001.py!} +``` + +`.add_task()` receives as arguments: + +* A task function to be run in the background (`write_notification`). +* Any sequence of arguments that should be passed to the task function in order (`email`). +* Any keyword arguments that should be passed to the task function (`message="some notification"`). + +## Dependency Injection + +Using `BackgroundTasks` also works with the dependency injection system, you can declare a parameter of type `BackgroundTasks` at multiple levels: in a *path operation function*, in a dependency (dependable), in a sub-dependency, etc. + +**FastAPI** knows what to do in each case and how to re-use the same object, so that all the background tasks are merged together and are run in the background afterwards: + +=== "Python 3.10+" + + ```Python hl_lines="13 15 22 25" + {!> ../../../docs_src/background_tasks/tutorial002_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="13 15 22 25" + {!> ../../../docs_src/background_tasks/tutorial002_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="14 16 23 26" + {!> ../../../docs_src/background_tasks/tutorial002_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="11 13 20 23" + {!> ../../../docs_src/background_tasks/tutorial002_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="13 15 22 25" + {!> ../../../docs_src/background_tasks/tutorial002.py!} + ``` + +In this example, the messages will be written to the `log.txt` file *after* the response is sent. + +If there was a query in the request, it will be written to the log in a background task. + +And then another background task generated at the *path operation function* will write a message using the `email` path parameter. + +## Technical Details + +The class `BackgroundTasks` comes directly from `starlette.background`. + +It is imported/included directly into FastAPI so that you can import it from `fastapi` and avoid accidentally importing the alternative `BackgroundTask` (without the `s` at the end) from `starlette.background`. + +By only using `BackgroundTasks` (and not `BackgroundTask`), it's then possible to use it as a *path operation function* parameter and have **FastAPI** handle the rest for you, just like when using the `Request` object directly. + +It's still possible to use `BackgroundTask` alone in FastAPI, but you have to create the object in your code and return a Starlette `Response` including it. + +You can see more details in Starlette's official docs for Background Tasks. + +## Caveat + +If you need to perform heavy background computation and you don't necessarily need it to be run by the same process (for example, you don't need to share memory, variables, etc), you might benefit from using other bigger tools like Celery. + +They tend to require more complex configurations, a message/job queue manager, like RabbitMQ or Redis, but they allow you to run background tasks in multiple processes, and especially, in multiple servers. + +To see an example, check the [Project Generators](../project-generation.md){.internal-link target=_blank}, they all include Celery already configured. + +But if you need to access variables and objects from the same **FastAPI** app, or you need to perform small background tasks (like sending an email notification), you can simply just use `BackgroundTasks`. + +## Recap + +Import and use `BackgroundTasks` with parameters in *path operation functions* and dependencies to add background tasks. From 958cf2f344e204a86a032d45c1805ea890c27c7e Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:22 +0800 Subject: [PATCH 061/163] New translations bigger-applications.md (Chinese Simplified) --- docs/zh/docs/tutorial/bigger-applications.md | 413 ++++++++++--------- 1 file changed, 216 insertions(+), 197 deletions(-) diff --git a/docs/zh/docs/tutorial/bigger-applications.md b/docs/zh/docs/tutorial/bigger-applications.md index 9f0134f683c9e..0f990e3367f4f 100644 --- a/docs/zh/docs/tutorial/bigger-applications.md +++ b/docs/zh/docs/tutorial/bigger-applications.md @@ -1,15 +1,15 @@ -# 更大的应用 - 多个文件 +# Bigger Applications - Multiple Files -如果你正在开发一个应用程序或 Web API,很少会将所有的内容都放在一个文件中。 +If you are building an application or a web API, it's rarely the case that you can put everything on a single file. -**FastAPI** 提供了一个方便的工具,可以在保持所有灵活性的同时构建你的应用程序。 +**FastAPI** provides a convenience tool to structure your application while keeping all the flexibility. !!! info - 如果你来自 Flask,那这将相当于 Flask 的 Blueprints。 + If you come from Flask, this would be the equivalent of Flask's Blueprints. -## 一个文件结构示例 +## An example file structure -假设你的文件结构如下: +Let's say you have a file structure like this: ``` . @@ -27,127 +27,144 @@ ``` !!! tip - 上面有几个 `__init__.py` 文件:每个目录或子目录中都有一个。 + There are several `__init__.py` files: one in each directory or subdirectory. - 这就是能将代码从一个文件导入到另一个文件的原因。 - - 例如,在 `app/main.py` 中,你可以有如下一行: + This is what allows importing code from one file into another. + + For example, in `app/main.py` you could have a line like: ``` from app.routers import items ``` -* `app` 目录包含了所有内容。并且它有一个空文件 `app/__init__.py`,因此它是一个「Python 包」(「Python 模块」的集合):`app`。 -* 它包含一个 `app/main.py` 文件。由于它位于一个 Python 包(一个包含 `__init__.py` 文件的目录)中,因此它是该包的一个「模块」:`app.main`。 -* 还有一个 `app/dependencies.py` 文件,就像 `app/main.py` 一样,它是一个「模块」:`app.dependencies`。 -* 有一个子目录 `app/routers/` 包含另一个 `__init__.py` 文件,因此它是一个「Python 子包」:`app.routers`。 -* 文件 `app/routers/items.py` 位于 `app/routers/` 包中,因此它是一个子模块:`app.routers.items`。 -* 同样适用于 `app/routers/users.py`,它是另一个子模块:`app.routers.users`。 -* 还有一个子目录 `app/internal/` 包含另一个 `__init__.py` 文件,因此它是又一个「Python 子包」:`app.internal`。 -* `app/internal/admin.py` 是另一个子模块:`app.internal.admin`。 +* The `app` directory contains everything. And it has an empty file `app/__init__.py`, so it is a "Python package" (a collection of "Python modules"): `app`. +* It contains an `app/main.py` file. As it is inside a Python package (a directory with a file `__init__.py`), it is a "module" of that package: `app.main`. +* There's also an `app/dependencies.py` file, just like `app/main.py`, it is a "module": `app.dependencies`. +* There's a subdirectory `app/routers/` with another file `__init__.py`, so it's a "Python subpackage": `app.routers`. +* The file `app/routers/items.py` is inside a package, `app/routers/`, so, it's a submodule: `app.routers.items`. +* The same with `app/routers/users.py`, it's another submodule: `app.routers.users`. +* There's also a subdirectory `app/internal/` with another file `__init__.py`, so it's another "Python subpackage": `app.internal`. +* And the file `app/internal/admin.py` is another submodule: `app.internal.admin`. - + -带有注释的同一文件结构: +The same file structure with comments: ``` . -├── app # 「app」是一个 Python 包 -│   ├── __init__.py # 这个文件使「app」成为一个 Python 包 -│   ├── main.py # 「main」模块,例如 import app.main -│   ├── dependencies.py # 「dependencies」模块,例如 import app.dependencies -│   └── routers # 「routers」是一个「Python 子包」 -│   │ ├── __init__.py # 使「routers」成为一个「Python 子包」 -│   │ ├── items.py # 「items」子模块,例如 import app.routers.items -│   │ └── users.py # 「users」子模块,例如 import app.routers.users -│   └── internal # 「internal」是一个「Python 子包」 -│   ├── __init__.py # 使「internal」成为一个「Python 子包」 -│   └── admin.py # 「admin」子模块,例如 import app.internal.admin +├── app # "app" is a Python package +│   ├── __init__.py # this file makes "app" a "Python package" +│   ├── main.py # "main" module, e.g. import app.main +│   ├── dependencies.py # "dependencies" module, e.g. import app.dependencies +│   └── routers # "routers" is a "Python subpackage" +│   │ ├── __init__.py # makes "routers" a "Python subpackage" +│   │ ├── items.py # "items" submodule, e.g. import app.routers.items +│   │ └── users.py # "users" submodule, e.g. import app.routers.users +│   └── internal # "internal" is a "Python subpackage" +│   ├── __init__.py # makes "internal" a "Python subpackage" +│   └── admin.py # "admin" submodule, e.g. import app.internal.admin ``` ## `APIRouter` -假设专门用于处理用户逻辑的文件是位于 `/app/routers/users.py` 的子模块。 +Let's say the file dedicated to handling just users is the submodule at `/app/routers/users.py`. -你希望将与用户相关的*路径操作*与其他代码分开,以使其井井有条。 +You want to have the *path operations* related to your users separated from the rest of the code, to keep it organized. -但它仍然是同一 **FastAPI** 应用程序/web API 的一部分(它是同一「Python 包」的一部分)。 +But it's still part of the same **FastAPI** application/web API (it's part of the same "Python Package"). -你可以使用 `APIRouter` 为该模块创建*路径操作*。 +You can create the *path operations* for that module using `APIRouter`. -### 导入 `APIRouter` +### Import `APIRouter` -你可以导入它并通过与 `FastAPI` 类相同的方式创建一个「实例」: +You import it and create an "instance" the same way you would with the class `FastAPI`: ```Python hl_lines="1 3" {!../../../docs_src/bigger_applications/app/routers/users.py!} ``` -### 使用 `APIRouter` 的*路径操作* +### *Path operations* with `APIRouter` -然后你可以使用它来声明*路径操作*。 +And then you use it to declare your *path operations*. -使用方式与 `FastAPI` 类相同: +Use it the same way you would use the `FastAPI` class: ```Python hl_lines="6 11 16" {!../../../docs_src/bigger_applications/app/routers/users.py!} ``` -你可以将 `APIRouter` 视为一个「迷你 `FastAPI`」类。 +You can think of `APIRouter` as a "mini `FastAPI`" class. -所有相同的选项都得到支持。 +All the same options are supported. -所有相同的 `parameters`、`responses`、`dependencies`、`tags` 等等。 +All the same `parameters`, `responses`, `dependencies`, `tags`, etc. !!! tip - 在此示例中,该变量被命名为 `router`,但你可以根据你的想法自由命名。 + In this example, the variable is called `router`, but you can name it however you want. -我们将在主 `FastAPI` 应用中包含该 `APIRouter`,但首先,让我们来看看依赖项和另一个 `APIRouter`。 +We are going to include this `APIRouter` in the main `FastAPI` app, but first, let's check the dependencies and another `APIRouter`. -## 依赖项 +## Dependencies -我们了解到我们将需要一些在应用程序的好几个地方所使用的依赖项。 +We see that we are going to need some dependencies used in several places of the application. -因此,我们将它们放在它们自己的 `dependencies` 模块(`app/dependencies.py`)中。 +So we put them in their own `dependencies` module (`app/dependencies.py`). -现在我们将使用一个简单的依赖项来读取一个自定义的 `X-Token` 请求首部: +We will now use a simple dependency to read a custom `X-Token` header: -```Python hl_lines="1 4-6" -{!../../../docs_src/bigger_applications/app/dependencies.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="3 6-8" + {!> ../../../docs_src/bigger_applications/app_an_py39/dependencies.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="1 5-7" + {!> ../../../docs_src/bigger_applications/app_an/dependencies.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="1 4-6" + {!> ../../../docs_src/bigger_applications/app/dependencies.py!} + ``` !!! tip - 我们正在使用虚构的请求首部来简化此示例。 + We are using an invented header to simplify this example. - 但在实际情况下,使用集成的[安全性实用工具](./security/index.md){.internal-link target=_blank}会得到更好的效果。 + But in real cases you will get better results using the integrated [Security utilities](./security/index.md){.internal-link target=_blank}. -## 其他使用 `APIRouter` 的模块 +## Another module with `APIRouter` -假设你在位于 `app/routers/items.py` 的模块中还有专门用于处理应用程序中「项目」的端点。 +Let's say you also have the endpoints dedicated to handling "items" from your application in the module at `app/routers/items.py`. -你具有以下*路径操作*: +You have *path operations* for: * `/items/` * `/items/{item_id}` -这和 `app/routers/users.py` 的结构完全相同。 +It's all the same structure as with `app/routers/users.py`. -但是我们想变得更聪明并简化一些代码。 +But we want to be smarter and simplify the code a bit. -我们知道此模块中的所有*路径操作*都有相同的: +We know all the *path operations* in this module have the same: -* 路径 `prefix`:`/items`。 -* `tags`:(仅有一个 `items` 标签)。 -* 额外的 `responses`。 -* `dependencies`:它们都需要我们创建的 `X-Token` 依赖项。 +* Path `prefix`: `/items`. +* `tags`: (just one tag: `items`). +* Extra `responses`. +* `dependencies`: they all need that `X-Token` dependency we created. -因此,我们可以将其添加到 `APIRouter` 中,而不是将其添加到每个路径操作中。 +So, instead of adding all that to each *path operation*, we can add it to the `APIRouter`. ```Python hl_lines="5-10 16 21" {!../../../docs_src/bigger_applications/app/routers/items.py!} ``` -由于每个*路径操作*的路径都必须以 `/` 开头,例如: +As the path of each *path operation* has to start with `/`, like in: ```Python hl_lines="1" @router.get("/{item_id}") @@ -155,303 +172,305 @@ async def read_item(item_id: str): ... ``` -...前缀不能以 `/` 作为结尾。 +...the prefix must not include a final `/`. -因此,本例中的前缀为 `/items`。 +So, the prefix in this case is `/items`. -我们还可以添加一个 `tags` 列表和额外的 `responses` 列表,这些参数将应用于此路由器中包含的所有*路径操作*。 +We can also add a list of `tags` and extra `responses` that will be applied to all the *path operations* included in this router. -我们可以添加一个 `dependencies` 列表,这些依赖项将被添加到路由器中的所有*路径操作*中,并将针对向它们发起的每个请求执行/解决。 +And we can add a list of `dependencies` that will be added to all the *path operations* in the router and will be executed/solved for each request made to them. !!! tip - 请注意,和[*路径操作装饰器*中的依赖项](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}很类似,没有值会被传递给你的*路径操作函数*。 + Note that, much like [dependencies in *path operation decorators*](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, no value will be passed to your *path operation function*. -最终结果是项目相关的路径现在为: +The end result is that the item paths are now: * `/items/` * `/items/{item_id}` -...如我们所愿。 +...as we intended. -* 它们将被标记为仅包含单个字符串 `"items"` 的标签列表。 - * 这些「标签」对于自动化交互式文档系统(使用 OpenAPI)特别有用。 -* 所有的路径操作都将包含预定义的 `responses`。 -* 所有的这些*路径操作*都将在自身之前计算/执行 `dependencies` 列表。 - * 如果你还在一个具体的*路径操作*中声明了依赖项,**它们也会被执行**。 - * 路由器的依赖项最先执行,然后是[装饰器中的 `dependencies`](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank},再然后是普通的参数依赖项。 - * 你还可以添加[具有 `scopes` 的 `Security` 依赖项](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}。 +* They will be marked with a list of tags that contain a single string `"items"`. + * These "tags" are especially useful for the automatic interactive documentation systems (using OpenAPI). +* All of them will include the predefined `responses`. +* All these *path operations* will have the list of `dependencies` evaluated/executed before them. + * If you also declare dependencies in a specific *path operation*, **they will be executed too**. + * The router dependencies are executed first, then the [`dependencies` in the decorator](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, and then the normal parameter dependencies. + * You can also add [`Security` dependencies with `scopes`](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}. !!! tip - 在 `APIRouter`中具有 `dependencies` 可以用来,例如,对一整组的*路径操作*要求身份认证。即使这些依赖项并没有分别添加到每个路径操作中。 + Having `dependencies` in the `APIRouter` can be used, for example, to require authentication for a whole group of *path operations*. Even if the dependencies are not added individually to each one of them. !!! check - `prefix`、`tags`、`responses` 以及 `dependencies` 参数只是(和其他很多情况一样)**FastAPI** 的一个用于帮助你避免代码重复的功能。 + The `prefix`, `tags`, `responses`, and `dependencies` parameters are (as in many other cases) just a feature from **FastAPI** to help you avoid code duplication. -### 导入依赖项 +### Import the dependencies -这些代码位于 `app.routers.items` 模块,`app/routers/items.py` 文件中。 +This code lives in the module `app.routers.items`, the file `app/routers/items.py`. -我们需要从 `app.dependencies` 模块即 `app/dependencies.py` 文件中获取依赖函数。 +And we need to get the dependency function from the module `app.dependencies`, the file `app/dependencies.py`. -因此,我们通过 `..` 对依赖项使用了相对导入: +So we use a relative import with `..` for the dependencies: ```Python hl_lines="3" {!../../../docs_src/bigger_applications/app/routers/items.py!} ``` -#### 相对导入如何工作 +#### How relative imports work !!! tip - 如果你完全了解导入的工作原理,请从下面的下一部分继续。 + If you know perfectly how imports work, continue to the next section below. -一个单点 `.`,例如: +A single dot `.`, like in: ```Python from .dependencies import get_token_header ``` -表示: +would mean: -* 从该模块(`app/routers/items.py` 文件)所在的同一个包(`app/routers/` 目录)开始... -* 找到 `dependencies` 模块(一个位于 `app/routers/dependencies.py` 的虚构文件)... -* 然后从中导入函数 `get_token_header`。 +* Starting in the same package that this module (the file `app/routers/items.py`) lives in (the directory `app/routers/`)... +* find the module `dependencies` (an imaginary file at `app/routers/dependencies.py`)... +* and from it, import the function `get_token_header`. -但是该文件并不存在,我们的依赖项位于 `app/dependencies.py` 文件中。 +But that file doesn't exist, our dependencies are in a file at `app/dependencies.py`. -请记住我们的程序/文件结构是怎样的: +Remember how our app/file structure looks like: - + --- -两个点 `..`,例如: +The two dots `..`, like in: ```Python from ..dependencies import get_token_header ``` -表示: +mean: -* 从该模块(`app/routers/items.py` 文件)所在的同一个包(`app/routers/` 目录)开始... -* 跳转到其父包(`app/` 目录)... -* 在该父包中,找到 `dependencies` 模块(位于 `app/dependencies.py` 的文件)... -* 然后从中导入函数 `get_token_header`。 +* Starting in the same package that this module (the file `app/routers/items.py`) lives in (the directory `app/routers/`)... +* go to the parent package (the directory `app/`)... +* and in there, find the module `dependencies` (the file at `app/dependencies.py`)... +* and from it, import the function `get_token_header`. -正常工作了!🎉 +That works correctly! 🎉 --- -同样,如果我们使用了三个点 `...`,例如: +The same way, if we had used three dots `...`, like in: ```Python from ...dependencies import get_token_header ``` -那将意味着: +that would mean: -* 从该模块(`app/routers/items.py` 文件)所在的同一个包(`app/routers/` 目录)开始... -* 跳转到其父包(`app/` 目录)... -* 然后跳转到该包的父包(该父包并不存在,`app` 已经是最顶层的包 😱)... -* 在该父包中,找到 `dependencies` 模块(位于 `app/` 更上一级目录中的 `dependencies.py` 文件)... -* 然后从中导入函数 `get_token_header`。 +* Starting in the same package that this module (the file `app/routers/items.py`) lives in (the directory `app/routers/`)... +* go to the parent package (the directory `app/`)... +* then go to the parent of that package (there's no parent package, `app` is the top level 😱)... +* and in there, find the module `dependencies` (the file at `app/dependencies.py`)... +* and from it, import the function `get_token_header`. -这将引用 `app/` 的往上一级,带有其自己的 `__init __.py` 等文件的某个包。但是我们并没有这个包。因此,这将在我们的示例中引发错误。🚨 +That would refer to some package above `app/`, with its own file `__init__.py`, etc. But we don't have that. So, that would throw an error in our example. 🚨 -但是现在你知道了它的工作原理,因此无论它们多么复杂,你都可以在自己的应用程序中使用相对导入。🤓 +But now you know how it works, so you can use relative imports in your own apps no matter how complex they are. 🤓 -### 添加一些自定义的 `tags`、`responses` 和 `dependencies` +### Add some custom `tags`, `responses`, and `dependencies` -我们不打算在每个*路径操作*中添加前缀 `/items` 或 `tags =["items"]`,因为我们将它们添加到了 `APIRouter` 中。 +We are not adding the prefix `/items` nor the `tags=["items"]` to each *path operation* because we added them to the `APIRouter`. -但是我们仍然可以添加*更多*将会应用于特定的*路径操作*的 `tags`,以及一些特定于该*路径操作*的额外 `responses`: +But we can still add _more_ `tags` that will be applied to a specific *path operation*, and also some extra `responses` specific to that *path operation*: ```Python hl_lines="30-31" {!../../../docs_src/bigger_applications/app/routers/items.py!} ``` !!! tip - 最后的这个路径操作将包含标签的组合:`["items","custom"]`。 + This last path operation will have the combination of tags: `["items", "custom"]`. - 并且在文档中也会有两个响应,一个用于 `404`,一个用于 `403`。 + And it will also have both responses in the documentation, one for `404` and one for `403`. -## `FastAPI` 主体 +## The main `FastAPI` -现在,让我们来看看位于 `app/main.py` 的模块。 +Now, let's see the module at `app/main.py`. -在这里你导入并使用 `FastAPI` 类。 +Here's where you import and use the class `FastAPI`. -这将是你的应用程序中将所有内容联结在一起的主文件。 +This will be the main file in your application that ties everything together. -并且由于你的大部分逻辑现在都存在于其自己的特定模块中,因此主文件的内容将非常简单。 +And as most of your logic will now live in its own specific module, the main file will be quite simple. -### 导入 `FastAPI` +### Import `FastAPI` -你可以像平常一样导入并创建一个 `FastAPI` 类。 +You import and create a `FastAPI` class as normally. -我们甚至可以声明[全局依赖项](dependencies/global-dependencies.md){.internal-link target=_blank},它会和每个 `APIRouter` 的依赖项组合在一起: +And we can even declare [global dependencies](dependencies/global-dependencies.md){.internal-link target=_blank} that will be combined with the dependencies for each `APIRouter`: ```Python hl_lines="1 3 7" {!../../../docs_src/bigger_applications/app/main.py!} ``` -### 导入 `APIRouter` +### Import the `APIRouter` -现在,我们导入具有 `APIRouter` 的其他子模块: +Now we import the other submodules that have `APIRouter`s: ```Python hl_lines="5" {!../../../docs_src/bigger_applications/app/main.py!} ``` -由于文件 `app/routers/users.py` 和 `app/routers/items.py` 是同一 Python 包 `app` 一个部分的子模块,因此我们可以使用单个点 ` .` 通过「相对导入」来导入它们。 +As the files `app/routers/users.py` and `app/routers/items.py` are submodules that are part of the same Python package `app`, we can use a single dot `.` to import them using "relative imports". -### 导入是如何工作的 +### How the importing works -这段代码: +The section: ```Python from .routers import items, users ``` -表示: +Means: -* 从该模块(`app/main.py` 文件)所在的同一个包(`app/` 目录)开始... -* 寻找 `routers` 子包(位于 `app/routers/` 的目录)... -* 从该包中,导入子模块 `items` (位于 `app/routers/items.py` 的文件) 以及 `users` (位于 `app/routers/users.py` 的文件)... +* Starting in the same package that this module (the file `app/main.py`) lives in (the directory `app/`)... +* look for the subpackage `routers` (the directory at `app/routers/`)... +* and from it, import the submodule `items` (the file at `app/routers/items.py`) and `users` (the file at `app/routers/users.py`)... -`items` 模块将具有一个 `router` 变量(`items.router`)。这与我们在 `app/routers/items.py` 文件中创建的变量相同,它是一个 `APIRouter` 对象。 +The module `items` will have a variable `router` (`items.router`). This is the same one we created in the file `app/routers/items.py`, it's an `APIRouter` object. -然后我们对 `users` 模块进行相同的操作。 +And then we do the same for the module `users`. -我们也可以像这样导入它们: +We could also import them like: ```Python from app.routers import items, users ``` !!! info - 第一个版本是「相对导入」: + The first version is a "relative import": ```Python from .routers import items, users ``` - 第二个版本是「绝对导入」: + + The second version is an "absolute import": ```Python from app.routers import items, users ``` - 要了解有关 Python 包和模块的更多信息,请查阅关于 Modules 的 Python 官方文档。 -### 避免名称冲突 + To learn more about Python Packages and Modules, read the official Python documentation about Modules. + +### Avoid name collisions -我们将直接导入 `items` 子模块,而不是仅导入其 `router` 变量。 +We are importing the submodule `items` directly, instead of importing just its variable `router`. -这是因为我们在 `users` 子模块中也有另一个名为 `router` 的变量。 +This is because we also have another variable named `router` in the submodule `users`. -如果我们一个接一个地导入,例如: +If we had imported one after the other, like: ```Python from .routers.items import router from .routers.users import router ``` -来自 `users` 的 `router` 将覆盖来自 `items` 中的 `router`,我们将无法同时使用它们。 +The `router` from `users` would overwrite the one from `items` and we wouldn't be able to use them at the same time. -因此,为了能够在同一个文件中使用它们,我们直接导入子模块: +So, to be able to use both of them in the same file, we import the submodules directly: ```Python hl_lines="4" {!../../../docs_src/bigger_applications/app/main.py!} ``` -### 包含 `users` 和 `items` 的 `APIRouter` +### Include the `APIRouter`s for `users` and `items` -现在,让我们来包含来自 `users` 和 `items` 子模块的 `router`。 +Now, let's include the `router`s from the submodules `users` and `items`: ```Python hl_lines="10-11" {!../../../docs_src/bigger_applications/app/main.py!} ``` !!! info - `users.router` 包含了 `app/routers/users.py` 文件中的 `APIRouter`。 + `users.router` contains the `APIRouter` inside of the file `app/routers/users.py`. - `items.router` 包含了 `app/routers/items.py` 文件中的 `APIRouter`。 + And `items.router` contains the `APIRouter` inside of the file `app/routers/items.py`. -使用 `app.include_router()`,我们可以将每个 `APIRouter` 添加到主 `FastAPI` 应用程序中。 +With `app.include_router()` we can add each `APIRouter` to the main `FastAPI` application. -它将包含来自该路由器的所有路由作为其一部分。 +It will include all the routes from that router as part of it. -!!! note "技术细节" - 实际上,它将在内部为声明在 `APIRouter` 中的每个*路径操作*创建一个*路径操作*。 +!!! note "Technical Details" + It will actually internally create a *path operation* for each *path operation* that was declared in the `APIRouter`. - 所以,在幕后,它实际上会像所有的东西都是同一个应用程序一样工作。 + So, behind the scenes, it will actually work as if everything was the same single app. !!! check - 包含路由器时,你不必担心性能问题。 + You don't have to worry about performance when including routers. - 这将花费几微秒时间,并且只会在启动时发生。 + This will take microseconds and will only happen at startup. + + So it won't affect performance. ⚡ - 因此,它不会影响性能。⚡ +### Include an `APIRouter` with a custom `prefix`, `tags`, `responses`, and `dependencies` -### 包含一个有自定义 `prefix`、`tags`、`responses` 和 `dependencies` 的 `APIRouter` +Now, let's imagine your organization gave you the `app/internal/admin.py` file. -现在,假设你的组织为你提供了 `app/internal/admin.py` 文件。 +It contains an `APIRouter` with some admin *path operations* that your organization shares between several projects. -它包含一个带有一些由你的组织在多个项目之间共享的管理员*路径操作*的 `APIRouter`。 - -对于此示例,它将非常简单。但是假设由于它是与组织中的其他项目所共享的,因此我们无法对其进行修改,以及直接在 `APIRouter` 中添加 `prefix`、`dependencies`、`tags` 等: +For this example it will be super simple. But let's say that because it is shared with other projects in the organization, we cannot modify it and add a `prefix`, `dependencies`, `tags`, etc. directly to the `APIRouter`: ```Python hl_lines="3" {!../../../docs_src/bigger_applications/app/internal/admin.py!} ``` -但是我们仍然希望在包含 `APIRouter` 时设置一个自定义的 `prefix`,以便其所有*路径操作*以 `/admin` 开头,我们希望使用本项目已经有的 `dependencies` 保护它,并且我们希望它包含自定义的 `tags` 和 `responses`。 +But we still want to set a custom `prefix` when including the `APIRouter` so that all its *path operations* start with `/admin`, we want to secure it with the `dependencies` we already have for this project, and we want to include `tags` and `responses`. -我们可以通过将这些参数传递给 `app.include_router()` 来完成所有的声明,而不必修改原始的 `APIRouter`: +We can declare all that without having to modify the original `APIRouter` by passing those parameters to `app.include_router()`: ```Python hl_lines="14-17" {!../../../docs_src/bigger_applications/app/main.py!} ``` -这样,原始的 `APIRouter` 将保持不变,因此我们仍然可以与组织中的其他项目共享相同的 `app/internal/admin.py` 文件。 +That way, the original `APIRouter` will keep unmodified, so we can still share that same `app/internal/admin.py` file with other projects in the organization. -结果是在我们的应用程序中,来自 `admin` 模块的每个*路径操作*都将具有: +The result is that in our app, each of the *path operations* from the `admin` module will have: -* `/admin` 前缀 。 -* `admin` 标签。 -* `get_token_header` 依赖项。 -* `418` 响应。 🍵 +* The prefix `/admin`. +* The tag `admin`. +* The dependency `get_token_header`. +* The response `418`. 🍵 -但这只会影响我们应用中的 `APIRouter`,而不会影响使用它的任何其他代码。 +But that will only affect that `APIRouter` in our app, not in any other code that uses it. -因此,举例来说,其他项目能够以不同的身份认证方法使用相同的 `APIRouter`。 +So, for example, other projects could use the same `APIRouter` with a different authentication method. -### 包含一个*路径操作* +### Include a *path operation* -我们还可以直接将*路径操作*添加到 `FastAPI` 应用中。 +We can also add *path operations* directly to the `FastAPI` app. -这里我们这样做了...只是为了表明我们可以做到🤷: +Here we do it... just to show that we can 🤷: ```Python hl_lines="21-23" {!../../../docs_src/bigger_applications/app/main.py!} ``` -它将与通过 `app.include_router()` 添加的所有其他*路径操作*一起正常运行。 +and it will work correctly, together with all the other *path operations* added with `app.include_router()`. -!!! info "特别的技术细节" - **注意**:这是一个非常技术性的细节,你也许可以**直接跳过**。 +!!! info "Very Technical Details" + **Note**: this is a very technical detail that you probably can **just skip**. --- + + The `APIRouter`s are not "mounted", they are not isolated from the rest of the application. + + This is because we want to include their *path operations* in the OpenAPI schema and the user interfaces. + + As we cannot just isolate them and "mount" them independently of the rest, the *path operations* are "cloned" (re-created), not included directly. - `APIRouter` 没有被「挂载」,它们与应用程序的其余部分没有隔离。 - - 这是因为我们想要在 OpenAPI 模式和用户界面中包含它们的*路径操作*。 - - 由于我们不能仅仅隔离它们并独立于其余部分来「挂载」它们,因此*路径操作*是被「克隆的」(重新创建),而不是直接包含。 - -## 查看自动化的 API 文档 +## Check the automatic API docs -现在,使用 `app.main` 模块和 `app` 变量运行 `uvicorn`: +Now, run `uvicorn`, using the module `app.main` and the variable `app`:
@@ -463,26 +482,26 @@ $ uvicorn app.main:app --reload
-然后打开位于 http://127.0.0.1:8000/docs 的文档。 +And open the docs at http://127.0.0.1:8000/docs. -你将看到使用了正确路径(和前缀)和正确标签的自动化 API 文档,包括了来自所有子模块的路径: +You will see the automatic API docs, including the paths from all the submodules, using the correct paths (and prefixes) and the correct tags: - + -## 多次使用不同的 `prefix` 包含同一个路由器 +## Include the same router multiple times with different `prefix` -你也可以在*同一*路由器上使用不同的前缀来多次使用 `.include_router()`。 +You can also use `.include_router()` multiple times with the *same* router using different prefixes. -在有些场景这可能有用,例如以不同的前缀公开同一个的 API,比方说 `/api/v1` 和 `/api/latest`。 +This could be useful, for example, to expose the same API under different prefixes, e.g. `/api/v1` and `/api/latest`. -这是一个你可能并不真正需要的高级用法,但万一你有需要了就能够用上。 +This is an advanced usage that you might not really need, but it's there in case you do. -## 在另一个 `APIRouter` 中包含一个 `APIRouter` +## Include an `APIRouter` in another -与在 `FastAPI` 应用程序中包含 `APIRouter` 的方式相同,你也可以在另一个 `APIRouter` 中包含 `APIRouter`,通过: +The same way you can include an `APIRouter` in a `FastAPI` application, you can include an `APIRouter` in another `APIRouter` using: ```Python router.include_router(other_router) ``` -请确保在你将 `router` 包含到 `FastAPI` 应用程序之前进行此操作,以便 `other_router` 中的`路径操作`也能被包含进来。 +Make sure you do it before including `router` in the `FastAPI` app, so that the *path operations* from `other_router` are also included. From 833551e7721fbbb861dc919ab2f21b4ce7eddd09 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:23 +0800 Subject: [PATCH 062/163] New translations body-fields.md (Chinese Simplified) --- docs/zh/docs/tutorial/body-fields.md | 119 +++++++++++++++++++++------ 1 file changed, 93 insertions(+), 26 deletions(-) diff --git a/docs/zh/docs/tutorial/body-fields.md b/docs/zh/docs/tutorial/body-fields.md index 053cae71c7c6f..36039020923ba 100644 --- a/docs/zh/docs/tutorial/body-fields.md +++ b/docs/zh/docs/tutorial/body-fields.md @@ -1,48 +1,115 @@ -# 请求体 - 字段 +# Body - Fields -与使用 `Query`、`Path` 和 `Body` 在*路径操作函数*中声明额外的校验和元数据的方式相同,你可以使用 Pydantic 的 `Field` 在 Pydantic 模型内部声明校验和元数据。 +The same way you can declare additional validation and metadata in *path operation function* parameters with `Query`, `Path` and `Body`, you can declare validation and metadata inside of Pydantic models using Pydantic's `Field`. -## 导入 `Field` +## Import `Field` -首先,你必须导入它: +First, you have to import it: -```Python hl_lines="2" -{!../../../docs_src/body_fields/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="4" + {!> ../../../docs_src/body_fields/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="4" + {!> ../../../docs_src/body_fields/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="4" + {!> ../../../docs_src/body_fields/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="2" + {!> ../../../docs_src/body_fields/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="4" + {!> ../../../docs_src/body_fields/tutorial001.py!} + ``` !!! warning - 注意,`Field` 是直接从 `pydantic` 导入的,而不是像其他的(`Query`,`Path`,`Body` 等)都从 `fastapi` 导入。 + Notice that `Field` is imported directly from `pydantic`, not from `fastapi` as are all the rest (`Query`, `Path`, `Body`, etc). -## 声明模型属性 +## Declare model attributes -然后,你可以对模型属性使用 `Field`: +You can then use `Field` with model attributes: -```Python hl_lines="9-10" -{!../../../docs_src/body_fields/tutorial001.py!} -``` +=== "Python 3.10+" -`Field` 的工作方式和 `Query`、`Path` 和 `Body` 相同,包括它们的参数等等也完全相同。 + ```Python hl_lines="11-14" + {!> ../../../docs_src/body_fields/tutorial001_an_py310.py!} + ``` -!!! note "技术细节" - 实际上,`Query`、`Path` 和其他你将在之后看到的类,创建的是由一个共同的 `Params` 类派生的子类的对象,该共同类本身又是 Pydantic 的 `FieldInfo` 类的子类。 +=== "Python 3.9+" - Pydantic 的 `Field` 也会返回一个 `FieldInfo` 的实例。 + ```Python hl_lines="11-14" + {!> ../../../docs_src/body_fields/tutorial001_an_py39.py!} + ``` - `Body` 也直接返回 `FieldInfo` 的一个子类的对象。还有其他一些你之后会看到的类是 `Body` 类的子类。 +=== "Python 3.6+" - 请记住当你从 `fastapi` 导入 `Query`、`Path` 等对象时,他们实际上是返回特殊类的函数。 + ```Python hl_lines="12-15" + {!> ../../../docs_src/body_fields/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="9-12" + {!> ../../../docs_src/body_fields/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="11-14" + {!> ../../../docs_src/body_fields/tutorial001.py!} + ``` + +`Field` works the same way as `Query`, `Path` and `Body`, it has all the same parameters, etc. + +!!! note "Technical Details" + Actually, `Query`, `Path` and others you'll see next create objects of subclasses of a common `Param` class, which is itself a subclass of Pydantic's `FieldInfo` class. + + And Pydantic's `Field` returns an instance of `FieldInfo` as well. + + `Body` also returns objects of a subclass of `FieldInfo` directly. And there are others you will see later that are subclasses of the `Body` class. + + Remember that when you import `Query`, `Path`, and others from `fastapi`, those are actually functions that return special classes. !!! tip - 注意每个模型属性如何使用类型、默认值和 `Field` 在代码结构上和*路径操作函数*的参数是相同的,区别是用 `Field` 替换`Path`、`Query` 和 `Body`。 + Notice how each model's attribute with a type, default value and `Field` has the same structure as a *path operation function's* parameter, with `Field` instead of `Path`, `Query` and `Body`. -## 添加额外信息 +## Add extra information -你可以在 `Field`、`Query`、`Body` 中声明额外的信息。这些信息将包含在生成的 JSON Schema 中。 +You can declare extra information in `Field`, `Query`, `Body`, etc. And it will be included in the generated JSON Schema. -你将在文档的后面部分学习声明示例时,了解到更多有关添加额外信息的知识。 +You will learn more about adding extra information later in the docs, when learning to declare examples. + +!!! warning + Extra keys passed to `Field` will also be present in the resulting OpenAPI schema for your application. As these keys may not necessarily be part of the OpenAPI specification, some OpenAPI tools, for example [the OpenAPI validator](https://validator.swagger.io/), may not work with your generated schema. -## 总结 +## Recap -你可以使用 Pydantic 的 `Field` 为模型属性声明额外的校验和元数据。 +You can use Pydantic's `Field` to declare extra validations and metadata for model attributes. -你还可以使用额外的关键字参数来传递额外的 JSON Schema 元数据。 +You can also use the extra keyword arguments to pass additional JSON Schema metadata. From fd156c89bdf657dca92c1ed425344b6bfdfc56e6 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:24 +0800 Subject: [PATCH 063/163] New translations body-multiple-params.md (Chinese Simplified) --- docs/zh/docs/tutorial/body-multiple-params.md | 247 ++++++++++++++---- 1 file changed, 193 insertions(+), 54 deletions(-) diff --git a/docs/zh/docs/tutorial/body-multiple-params.md b/docs/zh/docs/tutorial/body-multiple-params.md index 34fa5b638ff1d..b214092c9b175 100644 --- a/docs/zh/docs/tutorial/body-multiple-params.md +++ b/docs/zh/docs/tutorial/body-multiple-params.md @@ -1,23 +1,55 @@ -# 请求体 - 多个参数 +# Body - Multiple Parameters -既然我们已经知道了如何使用 `Path` 和 `Query`,下面让我们来了解一下请求体声明的更高级用法。 +Now that we have seen how to use `Path` and `Query`, let's see more advanced uses of request body declarations. -## 混合使用 `Path`、`Query` 和请求体参数 +## Mix `Path`, `Query` and body parameters -首先,毫无疑问地,你可以随意地混合使用 `Path`、`Query` 和请求体参数声明,**FastAPI** 会知道该如何处理。 +First, of course, you can mix `Path`, `Query` and request body parameter declarations freely and **FastAPI** will know what to do. -你还可以通过将默认值设置为 `None` 来将请求体参数声明为可选参数: +And you can also declare body parameters as optional, by setting the default to `None`: -```Python hl_lines="17-19" -{!../../../docs_src/body_multiple_params/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="18-20" + {!> ../../../docs_src/body_multiple_params/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="18-20" + {!> ../../../docs_src/body_multiple_params/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="19-21" + {!> ../../../docs_src/body_multiple_params/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="17-19" + {!> ../../../docs_src/body_multiple_params/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="19-21" + {!> ../../../docs_src/body_multiple_params/tutorial001.py!} + ``` !!! note - 请注意,在这种情况下,将从请求体获取的 `item` 是可选的。因为它的默认值为 `None`。 + Notice that, in this case, the `item` that would be taken from the body is optional. As it has a `None` default value. -## 多个请求体参数 +## Multiple body parameters -在上面的示例中,*路径操作*将期望一个具有 `Item` 的属性的 JSON 请求体,就像: +In the previous example, the *path operations* would expect a JSON body with the attributes of an `Item`, like: ```JSON { @@ -28,15 +60,23 @@ } ``` -但是你也可以声明多个请求体参数,例如 `item` 和 `user`: +But you can also declare multiple body parameters, e.g. `item` and `user`: -```Python hl_lines="20" -{!../../../docs_src/body_multiple_params/tutorial002.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="20" + {!> ../../../docs_src/body_multiple_params/tutorial002_py310.py!} + ``` -在这种情况下,**FastAPI** 将注意到该函数中有多个请求体参数(两个 Pydantic 模型参数)。 +=== "Python 3.6+" -因此,它将使用参数名称作为请求体中的键(字段名称),并期望一个类似于以下内容的请求体: + ```Python hl_lines="22" + {!> ../../../docs_src/body_multiple_params/tutorial002.py!} + ``` + +In this case, **FastAPI** will notice that there are more than one body parameters in the function (two parameters that are Pydantic models). + +So, it will then use the parameter names as keys (field names) in the body, and expect a body like: ```JSON { @@ -54,30 +94,60 @@ ``` !!! note - 请注意,即使 `item` 的声明方式与之前相同,但现在它被期望通过 `item` 键内嵌在请求体中。 + Notice that even though the `item` was declared the same way as before, it is now expected to be inside of the body with a key `item`. -**FastAPI** 将自动对请求中的数据进行转换,因此 `item` 参数将接收指定的内容,`user` 参数也是如此。 +**FastAPI** will do the automatic conversion from the request, so that the parameter `item` receives it's specific content and the same for `user`. -它将执行对复合数据的校验,并且像现在这样为 OpenAPI 模式和自动化文档对其进行记录。 +It will perform the validation of the compound data, and will document it like that for the OpenAPI schema and automatic docs. -## 请求体中的单一值 +## Singular values in body -与使用 `Query` 和 `Path` 为查询参数和路径参数定义额外数据的方式相同,**FastAPI** 提供了一个同等的 `Body`。 +The same way there is a `Query` and `Path` to define extra data for query and path parameters, **FastAPI** provides an equivalent `Body`. -例如,为了扩展先前的模型,你可能决定除了 `item` 和 `user` 之外,还想在同一请求体中具有另一个键 `importance`。 +For example, extending the previous model, you could decide that you want to have another key `importance` in the same body, besides the `item` and `user`. -如果你就按原样声明它,因为它是一个单一值,**FastAPI** 将假定它是一个查询参数。 +If you declare it as is, because it is a singular value, **FastAPI** will assume that it is a query parameter. -但是你可以使用 `Body` 指示 **FastAPI** 将其作为请求体的另一个键进行处理。 +But you can instruct **FastAPI** to treat it as another body key using `Body`: +=== "Python 3.10+" -```Python hl_lines="22" -{!../../../docs_src/body_multiple_params/tutorial003.py!} -``` + ```Python hl_lines="23" + {!> ../../../docs_src/body_multiple_params/tutorial003_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="23" + {!> ../../../docs_src/body_multiple_params/tutorial003_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="24" + {!> ../../../docs_src/body_multiple_params/tutorial003_an.py!} + ``` -在这种情况下,**FastAPI** 将期望像这样的请求体: +=== "Python 3.10+ non-Annotated" + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="20" + {!> ../../../docs_src/body_multiple_params/tutorial003_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="22" + {!> ../../../docs_src/body_multiple_params/tutorial003.py!} + ``` + +In this case, **FastAPI** will expect a body like: ```JSON { @@ -95,47 +165,116 @@ } ``` -同样的,它将转换数据类型,校验,生成文档等。 +Again, it will convert the data types, validate, document, etc. -## 多个请求体参数和查询参数 +## Multiple body params and query -当然,除了请求体参数外,你还可以在任何需要的时候声明额外的查询参数。 +Of course, you can also declare additional query parameters whenever you need, additional to any body parameters. -由于默认情况下单一值被解释为查询参数,因此你不必显式地添加 `Query`,你可以仅执行以下操作: +As, by default, singular values are interpreted as query parameters, you don't have to explicitly add a `Query`, you can just do: ```Python -q: str = None +q: Union[str, None] = None ``` -比如: +Or in Python 3.10 and above: -```Python hl_lines="25" -{!../../../docs_src/body_multiple_params/tutorial004.py!} +```Python +q: str | None = None ``` -!!! info - `Body` 同样具有与 `Query`、`Path` 以及其他后面将看到的类完全相同的额外校验和元数据参数。 +For example: + +=== "Python 3.10+" + + ```Python hl_lines="27" + {!> ../../../docs_src/body_multiple_params/tutorial004_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="27" + {!> ../../../docs_src/body_multiple_params/tutorial004_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="28" + {!> ../../../docs_src/body_multiple_params/tutorial004_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="25" + {!> ../../../docs_src/body_multiple_params/tutorial004_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="27" + {!> ../../../docs_src/body_multiple_params/tutorial004.py!} + ``` +!!! info + `Body` also has all the same extra validation and metadata parameters as `Query`,`Path` and others you will see later. -## 嵌入单个请求体参数 +## Embed a single body parameter -假设你只有一个来自 Pydantic 模型 `Item` 的请求体参数 `item`。 +Let's say you only have a single `item` body parameter from a Pydantic model `Item`. -默认情况下,**FastAPI** 将直接期望这样的请求体。 +By default, **FastAPI** will then expect its body directly. -但是,如果你希望它期望一个拥有 `item` 键并在值中包含模型内容的 JSON,就像在声明额外的请求体参数时所做的那样,则可以使用一个特殊的 `Body` 参数 `embed`: +But if you want it to expect a JSON with a key `item` and inside of it the model contents, as it does when you declare extra body parameters, you can use the special `Body` parameter `embed`: ```Python item: Item = Body(embed=True) ``` -比如: +as in: -```Python hl_lines="15" -{!../../../docs_src/body_multiple_params/tutorial005.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="17" + {!> ../../../docs_src/body_multiple_params/tutorial005_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="17" + {!> ../../../docs_src/body_multiple_params/tutorial005_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="18" + {!> ../../../docs_src/body_multiple_params/tutorial005_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="15" + {!> ../../../docs_src/body_multiple_params/tutorial005_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="17" + {!> ../../../docs_src/body_multiple_params/tutorial005.py!} + ``` -在这种情况下,**FastAPI** 将期望像这样的请求体: +In this case **FastAPI** will expect a body like: ```JSON hl_lines="2" { @@ -148,7 +287,7 @@ item: Item = Body(embed=True) } ``` -而不是: +instead of: ```JSON { @@ -159,12 +298,12 @@ item: Item = Body(embed=True) } ``` -## 总结 +## Recap -你可以添加多个请求体参数到*路径操作函数*中,即使一个请求只能有一个请求体。 +You can add multiple body parameters to your *path operation function*, even though a request can only have a single body. -但是 **FastAPI** 会处理它,在函数中为你提供正确的数据,并在*路径操作*中校验并记录正确的模式。 +But **FastAPI** will handle it, give you the correct data in your function, and validate and document the correct schema in the *path operation*. -你还可以声明将作为请求体的一部分所接收的单一值。 +You can also declare singular values to be received as part of the body. -你还可以指示 **FastAPI** 在仅声明了一个请求体参数的情况下,将原本的请求体嵌入到一个键中。 +And you can instruct **FastAPI** to embed the body in a key even when there is only a single parameter declared. From 04ccb7b9d1cf9eb9ed9e8ea42f60fa90c7cca8d4 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:25 +0800 Subject: [PATCH 064/163] New translations body-nested-models.md (Chinese Simplified) --- docs/zh/docs/tutorial/body-nested-models.md | 356 ++++++++++++++------ 1 file changed, 247 insertions(+), 109 deletions(-) diff --git a/docs/zh/docs/tutorial/body-nested-models.md b/docs/zh/docs/tutorial/body-nested-models.md index 7649ee6feafed..7adfd8c03d396 100644 --- a/docs/zh/docs/tutorial/body-nested-models.md +++ b/docs/zh/docs/tutorial/body-nested-models.md @@ -1,35 +1,53 @@ -# 请求体 - 嵌套模型 +# Body - Nested Models -使用 **FastAPI**,你可以定义、校验、记录文档并使用任意深度嵌套的模型(归功于Pydantic)。 +With **FastAPI**, you can define, validate, document, and use arbitrarily deeply nested models (thanks to Pydantic). -## List 字段 +## List fields -你可以将一个属性定义为拥有子元素的类型。例如 Python `list`: +You can define an attribute to be a subtype. For example, a Python `list`: -```Python hl_lines="12" -{!../../../docs_src/body_nested_models/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="12" + {!> ../../../docs_src/body_nested_models/tutorial001_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="14" + {!> ../../../docs_src/body_nested_models/tutorial001.py!} + ``` -这将使 `tags` 成为一个由元素组成的列表。不过它没有声明每个元素的类型。 +This will make `tags` be a list, although it doesn't declare the type of the elements of the list. -## 具有子类型的 List 字段 +## List fields with type parameter -但是 Python 有一种特定的方法来声明具有子类型的列表: +But Python has a specific way to declare lists with internal types, or "type parameters": -### 从 typing 导入 `List` +### Import typing's `List` -首先,从 Python 的标准库 `typing` 模块中导入 `List`: +In Python 3.9 and above you can use the standard `list` to declare these type annotations as we'll see below. 💡 + +But in Python versions before 3.9 (3.6 and above), you first need to import `List` from standard Python's `typing` module: ```Python hl_lines="1" -{!../../../docs_src/body_nested_models/tutorial002.py!} +{!> ../../../docs_src/body_nested_models/tutorial002.py!} ``` -### 声明具有子类型的 List +### Declare a `list` with a type parameter + +To declare types that have type parameters (internal types), like `list`, `dict`, `tuple`: + +* If you are in a Python version lower than 3.9, import their equivalent version from the `typing` module +* Pass the internal type(s) as "type parameters" using square brackets: `[` and `]` + +In Python 3.9 it would be: -要声明具有子类型的类型,例如 `list`、`dict`、`tuple`: +```Python +my_list: list[str] +``` -* 从 `typing` 模块导入它们 -* 使用方括号 `[` 和 `]` 将子类型作为「类型参数」传入 +In versions of Python before 3.9, it would be: ```Python from typing import List @@ -37,61 +55,117 @@ from typing import List my_list: List[str] ``` -这完全是用于类型声明的标准 Python 语法。 +That's all standard Python syntax for type declarations. -对具有子类型的模型属性也使用相同的标准语法。 +Use that same standard syntax for model attributes with internal types. -因此,在我们的示例中,我们可以将 `tags` 明确地指定为一个「字符串列表」: +So, in our example, we can make `tags` be specifically a "list of strings": -```Python hl_lines="14" -{!../../../docs_src/body_nested_models/tutorial002.py!} -``` +=== "Python 3.10+" -## Set 类型 + ```Python hl_lines="12" + {!> ../../../docs_src/body_nested_models/tutorial002_py310.py!} + ``` -但是随后我们考虑了一下,意识到标签不应该重复,它们很大可能会是唯一的字符串。 +=== "Python 3.9+" -Python 具有一种特殊的数据类型来保存一组唯一的元素,即 `set`。 + ```Python hl_lines="14" + {!> ../../../docs_src/body_nested_models/tutorial002_py39.py!} + ``` -然后我们可以导入 `Set` 并将 `tag` 声明为一个由 `str` 组成的 `set`: +=== "Python 3.6+" -```Python hl_lines="1 14" -{!../../../docs_src/body_nested_models/tutorial003.py!} -``` + ```Python hl_lines="14" + {!> ../../../docs_src/body_nested_models/tutorial002.py!} + ``` -这样,即使你收到带有重复数据的请求,这些数据也会被转换为一组唯一项。 +## Set types -而且,每当你输出该数据时,即使源数据有重复,它们也将作为一组唯一项输出。 +But then we think about it, and realize that tags shouldn't repeat, they would probably be unique strings. -并且还会被相应地标注 / 记录文档。 +And Python has a special data type for sets of unique items, the `set`. -## 嵌套模型 +Then we can declare `tags` as a set of strings: -Pydantic 模型的每个属性都具有类型。 +=== "Python 3.10+" -但是这个类型本身可以是另一个 Pydantic 模型。 + ```Python hl_lines="12" + {!> ../../../docs_src/body_nested_models/tutorial003_py310.py!} + ``` -因此,你可以声明拥有特定属性名称、类型和校验的深度嵌套的 JSON 对象。 +=== "Python 3.9+" -上述这些都可以任意的嵌套。 + ```Python hl_lines="14" + {!> ../../../docs_src/body_nested_models/tutorial003_py39.py!} + ``` -### 定义子模型 +=== "Python 3.6+" -例如,我们可以定义一个 `Image` 模型: + ```Python hl_lines="1 14" + {!> ../../../docs_src/body_nested_models/tutorial003.py!} + ``` -```Python hl_lines="9 10 11" -{!../../../docs_src/body_nested_models/tutorial004.py!} -``` +With this, even if you receive a request with duplicate data, it will be converted to a set of unique items. -### 将子模型用作类型 +And whenever you output that data, even if the source had duplicates, it will be output as a set of unique items. -然后我们可以将其用作一个属性的类型: +And it will be annotated / documented accordingly too. -```Python hl_lines="20" -{!../../../docs_src/body_nested_models/tutorial004.py!} -``` +## Nested Models + +Each attribute of a Pydantic model has a type. + +But that type can itself be another Pydantic model. + +So, you can declare deeply nested JSON "objects" with specific attribute names, types and validations. + +All that, arbitrarily nested. + +### Define a submodel + +For example, we can define an `Image` model: + +=== "Python 3.10+" + + ```Python hl_lines="7-9" + {!> ../../../docs_src/body_nested_models/tutorial004_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="9-11" + {!> ../../../docs_src/body_nested_models/tutorial004_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9-11" + {!> ../../../docs_src/body_nested_models/tutorial004.py!} + ``` + +### Use the submodel as a type + +And then we can use it as the type of an attribute: + +=== "Python 3.10+" + + ```Python hl_lines="18" + {!> ../../../docs_src/body_nested_models/tutorial004_py310.py!} + ``` + +=== "Python 3.9+" -这意味着 **FastAPI** 将期望类似于以下内容的请求体: + ```Python hl_lines="20" + {!> ../../../docs_src/body_nested_models/tutorial004_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="20" + {!> ../../../docs_src/body_nested_models/tutorial004.py!} + ``` + +This would mean that **FastAPI** would expect a body similar to: ```JSON { @@ -107,36 +181,64 @@ Pydantic 模型的每个属性都具有类型。 } ``` -再一次,仅仅进行这样的声明,你将通过 **FastAPI** 获得: +Again, doing just that declaration, with **FastAPI** you get: -* 对被嵌入的模型也适用的编辑器支持(自动补全等) -* 数据转换 -* 数据校验 -* 自动生成文档 +* Editor support (completion, etc), even for nested models +* Data conversion +* Data validation +* Automatic documentation -## 特殊的类型和校验 +## Special types and validation -除了普通的单一值类型(如 `str`、`int`、`float` 等)外,你还可以使用从 `str` 继承的更复杂的单一值类型。 +Apart from normal singular types like `str`, `int`, `float`, etc. You can use more complex singular types that inherit from `str`. -要了解所有的可用选项,请查看关于 来自 Pydantic 的外部类型 的文档。你将在下一章节中看到一些示例。 +To see all the options you have, checkout the docs for Pydantic's exotic types. You will see some examples in the next chapter. -例如,在 `Image` 模型中我们有一个 `url` 字段,我们可以把它声明为 Pydantic 的 `HttpUrl`,而不是 `str`: +For example, as in the `Image` model we have a `url` field, we can declare it to be instead of a `str`, a Pydantic's `HttpUrl`: -```Python hl_lines="4 10" -{!../../../docs_src/body_nested_models/tutorial005.py!} -``` +=== "Python 3.10+" -该字符串将被检查是否为有效的 URL,并在 JSON Schema / OpenAPI 文档中进行记录。 + ```Python hl_lines="2 8" + {!> ../../../docs_src/body_nested_models/tutorial005_py310.py!} + ``` -## 带有一组子模型的属性 +=== "Python 3.9+" -你还可以将 Pydantic 模型用作 `list`、`set` 等的子类型: + ```Python hl_lines="4 10" + {!> ../../../docs_src/body_nested_models/tutorial005_py39.py!} + ``` -```Python hl_lines="20" -{!../../../docs_src/body_nested_models/tutorial006.py!} -``` +=== "Python 3.6+" + + ```Python hl_lines="4 10" + {!> ../../../docs_src/body_nested_models/tutorial005.py!} + ``` + +The string will be checked to be a valid URL, and documented in JSON Schema / OpenAPI as such. + +## Attributes with lists of submodels -这将期望(转换,校验,记录文档等)下面这样的 JSON 请求体: +You can also use Pydantic models as subtypes of `list`, `set`, etc: + +=== "Python 3.10+" + + ```Python hl_lines="18" + {!> ../../../docs_src/body_nested_models/tutorial006_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="20" + {!> ../../../docs_src/body_nested_models/tutorial006_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="20" + {!> ../../../docs_src/body_nested_models/tutorial006.py!} + ``` + +This will expect (convert, validate, document, etc) a JSON body like: ```JSON hl_lines="11" { @@ -163,82 +265,118 @@ Pydantic 模型的每个属性都具有类型。 ``` !!! info - 请注意 `images` 键现在具有一组 image 对象是如何发生的。 + Notice how the `images` key now has a list of image objects. -## 深度嵌套模型 +## Deeply nested models -你可以定义任意深度的嵌套模型: +You can define arbitrarily deeply nested models: -```Python hl_lines="9 14 20 23 27" -{!../../../docs_src/body_nested_models/tutorial007.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="7 12 18 21 25" + {!> ../../../docs_src/body_nested_models/tutorial007_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="9 14 20 23 27" + {!> ../../../docs_src/body_nested_models/tutorial007_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9 14 20 23 27" + {!> ../../../docs_src/body_nested_models/tutorial007.py!} + ``` !!! info - 请注意 `Offer` 拥有一组 `Item` 而反过来 `Item` 又是一个可选的 `Image` 列表是如何发生的。 + Notice how `Offer` has a list of `Item`s, which in turn have an optional list of `Image`s -## 纯列表请求体 +## Bodies of pure lists -如果你期望的 JSON 请求体的最外层是一个 JSON `array`(即 Python `list`),则可以在路径操作函数的参数中声明此类型,就像声明 Pydantic 模型一样: +If the top level value of the JSON body you expect is a JSON `array` (a Python `list`), you can declare the type in the parameter of the function, the same as in Pydantic models: ```Python images: List[Image] ``` -例如: +or in Python 3.9 and above: -```Python hl_lines="15" -{!../../../docs_src/body_nested_models/tutorial008.py!} +```Python +images: list[Image] ``` -## 无处不在的编辑器支持 +as in: + +=== "Python 3.9+" + + ```Python hl_lines="13" + {!> ../../../docs_src/body_nested_models/tutorial008_py39.py!} + ``` -你可以随处获得编辑器支持。 +=== "Python 3.6+" -即使是列表中的元素: + ```Python hl_lines="15" + {!> ../../../docs_src/body_nested_models/tutorial008.py!} + ``` - +## Editor support everywhere -如果你直接使用 `dict` 而不是 Pydantic 模型,那你将无法获得这种编辑器支持。 +And you get editor support everywhere. -但是你根本不必担心这两者,传入的字典会自动被转换,你的输出也会自动被转换为 JSON。 +Even for items inside of lists: -## 任意 `dict` 构成的请求体 + -你也可以将请求体声明为使用某类型的键和其他类型值的 `dict`。 +You couldn't get this kind of editor support if you were working directly with `dict` instead of Pydantic models. -无需事先知道有效的字段/属性(在使用 Pydantic 模型的场景)名称是什么。 +But you don't have to worry about them either, incoming dicts are converted automatically and your output is converted automatically to JSON too. -如果你想接收一些尚且未知的键,这将很有用。 +## Bodies of arbitrary `dict`s + +You can also declare a body as a `dict` with keys of some type and values of other type. + +Without having to know beforehand what are the valid field/attribute names (as would be the case with Pydantic models). + +This would be useful if you want to receive keys that you don't already know. --- -其他有用的场景是当你想要接收其他类型的键时,例如 `int`。 +Other useful case is when you want to have keys of other type, e.g. `int`. -这也是我们在接下来将看到的。 +That's what we are going to see here. -在下面的例子中,你将接受任意键为 `int` 类型并且值为 `float` 类型的 `dict`: +In this case, you would accept any `dict` as long as it has `int` keys with `float` values: -```Python hl_lines="15" -{!../../../docs_src/body_nested_models/tutorial009.py!} -``` +=== "Python 3.9+" -!!! tip - 请记住 JSON 仅支持将 `str` 作为键。 + ```Python hl_lines="7" + {!> ../../../docs_src/body_nested_models/tutorial009_py39.py!} + ``` + +=== "Python 3.6+" - 但是 Pydantic 具有自动转换数据的功能。 + ```Python hl_lines="9" + {!> ../../../docs_src/body_nested_models/tutorial009.py!} + ``` - 这意味着,即使你的 API 客户端只能将字符串作为键发送,只要这些字符串内容仅包含整数,Pydantic 就会对其进行转换并校验。 +!!! tip + Have in mind that JSON only supports `str` as keys. - 然后你接收的名为 `weights` 的 `dict` 实际上将具有 `int` 类型的键和 `float` 类型的值。 + But Pydantic has automatic data conversion. + + This means that, even though your API clients can only send strings as keys, as long as those strings contain pure integers, Pydantic will convert them and validate them. + + And the `dict` you receive as `weights` will actually have `int` keys and `float` values. -## 总结 +## Recap -使用 **FastAPI** 你可以拥有 Pydantic 模型提供的极高灵活性,同时保持代码的简单、简短和优雅。 +With **FastAPI** you have the maximum flexibility provided by Pydantic models, while keeping your code simple, short and elegant. -而且还具有下列好处: +But with all the benefits: -* 编辑器支持(处处皆可自动补全!) -* 数据转换(也被称为解析/序列化) -* 数据校验 -* 模式文档 -* 自动生成的文档 +* Editor support (completion everywhere!) +* Data conversion (a.k.a. parsing / serialization) +* Data validation +* Schema documentation +* Automatic docs From 19941983ca0656d07c6a1ead25108532126d7d6e Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:26 +0800 Subject: [PATCH 065/163] New translations body-updates.md (Chinese Simplified) --- docs/zh/docs/tutorial/body-updates.md | 166 +++++++++++++++++--------- 1 file changed, 110 insertions(+), 56 deletions(-) diff --git a/docs/zh/docs/tutorial/body-updates.md b/docs/zh/docs/tutorial/body-updates.md index 43f20f8fcbd9c..1c1dfea522e7d 100644 --- a/docs/zh/docs/tutorial/body-updates.md +++ b/docs/zh/docs/tutorial/body-updates.md @@ -1,20 +1,34 @@ -# 请求体 - 更新数据 +# Body - Updates -## 用 `PUT` 更新数据 +## Update replacing with `PUT` -更新数据请用 HTTP `PUT` 操作。 +To update an item you can use the HTTP `PUT` operation. -把输入数据转换为以 JSON 格式存储的数据(比如,使用 NoSQL 数据库时),可以使用 `jsonable_encoder`。例如,把 `datetime` 转换为 `str`。 +You can use the `jsonable_encoder` to convert the input data to data that can be stored as JSON (e.g. with a NoSQL database). For example, converting `datetime` to `str`. -```Python hl_lines="30-35" -{!../../../docs_src/body_updates/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="28-33" + {!> ../../../docs_src/body_updates/tutorial001_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="30-35" + {!> ../../../docs_src/body_updates/tutorial001_py39.py!} + ``` + +=== "Python 3.6+" -`PUT` 用于接收替换现有数据的数据。 + ```Python hl_lines="30-35" + {!> ../../../docs_src/body_updates/tutorial001.py!} + ``` -### 关于更新数据的警告 +`PUT` is used to receive data that should replace the existing data. -用 `PUT` 把数据项 `bar` 更新为以下内容时: +### Warning about replacing + +That means that if you want to update the item `bar` using `PUT` with a body containing: ```Python { @@ -24,78 +38,118 @@ } ``` -因为上述数据未包含已存储的属性 `"tax": 20.2`,新的输入模型会把 `"tax": 10.5` 作为默认值。 +because it doesn't include the already stored attribute `"tax": 20.2`, the input model would take the default value of `"tax": 10.5`. -因此,本次操作把 `tax` 的值「更新」为 `10.5`。 +And the data would be saved with that "new" `tax` of `10.5`. -## 用 `PATCH` 进行部分更新 +## Partial updates with `PATCH` -HTTP `PATCH` 操作用于更新 *部分* 数据。 +You can also use the HTTP `PATCH` operation to *partially* update data. -即,只发送要更新的数据,其余数据保持不变。 +This means that you can send only the data that you want to update, leaving the rest intact. -!!! Note "笔记" +!!! Note + `PATCH` is less commonly used and known than `PUT`. - `PATCH` 没有 `PUT` 知名,也怎么不常用。 + And many teams use only `PUT`, even for partial updates. + + You are **free** to use them however you want, **FastAPI** doesn't impose any restrictions. + + But this guide shows you, more or less, how they are intended to be used. - 很多人甚至只用 `PUT` 实现部分更新。 +### Using Pydantic's `exclude_unset` parameter - **FastAPI** 对此没有任何限制,可以**随意**互换使用这两种操作。 +If you want to receive partial updates, it's very useful to use the parameter `exclude_unset` in Pydantic's model's `.dict()`. - 但本指南也会分别介绍这两种操作各自的用途。 +Like `item.dict(exclude_unset=True)`. -### 使用 Pydantic 的 `exclude_unset` 参数 +That would generate a `dict` with only the data that was set when creating the `item` model, excluding default values. -更新部分数据时,可以在 Pydantic 模型的 `.dict()` 中使用 `exclude_unset` 参数。 +Then you can use this to generate a `dict` with only the data that was set (sent in the request), omitting default values: -比如,`item.dict(exclude_unset=True)`。 +=== "Python 3.10+" -这段代码生成的 `dict` 只包含创建 `item` 模型时显式设置的数据,而不包括默认值。 + ```Python hl_lines="32" + {!> ../../../docs_src/body_updates/tutorial002_py310.py!} + ``` -然后再用它生成一个只含已设置(在请求中所发送)数据,且省略了默认值的 `dict`: +=== "Python 3.9+" -```Python hl_lines="34" -{!../../../docs_src/body_updates/tutorial002.py!} -``` + ```Python hl_lines="34" + {!> ../../../docs_src/body_updates/tutorial002_py39.py!} + ``` -### 使用 Pydantic 的 `update` 参数 +=== "Python 3.6+" -接下来,用 `.copy()` 为已有模型创建调用 `update` 参数的副本,该参数为包含更新数据的 `dict`。 + ```Python hl_lines="34" + {!> ../../../docs_src/body_updates/tutorial002.py!} + ``` -例如,`stored_item_model.copy(update=update_data)`: +### Using Pydantic's `update` parameter -```Python hl_lines="35" -{!../../../docs_src/body_updates/tutorial002.py!} -``` +Now, you can create a copy of the existing model using `.copy()`, and pass the `update` parameter with a `dict` containing the data to update. -### 更新部分数据小结 +Like `stored_item_model.copy(update=update_data)`: -简而言之,更新部分数据应: +=== "Python 3.10+" -* 使用 `PATCH` 而不是 `PUT` (可选,也可以用 `PUT`); -* 提取存储的数据; -* 把数据放入 Pydantic 模型; -* 生成不含输入模型默认值的 `dict` (使用 `exclude_unset` 参数); - * 只更新用户设置过的值,不用模型中的默认值覆盖已存储过的值。 -* 为已存储的模型创建副本,用接收的数据更新其属性 (使用 `update` 参数)。 -* 把模型副本转换为可存入数据库的形式(比如,使用 `jsonable_encoder`)。 - * 这种方式与 Pydantic 模型的 `.dict()` 方法类似,但能确保把值转换为适配 JSON 的数据类型,例如, 把 `datetime` 转换为 `str` 。 -* 把数据保存至数据库; -* 返回更新后的模型。 + ```Python hl_lines="33" + {!> ../../../docs_src/body_updates/tutorial002_py310.py!} + ``` -```Python hl_lines="30-37" -{!../../../docs_src/body_updates/tutorial002.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="35" + {!> ../../../docs_src/body_updates/tutorial002_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="35" + {!> ../../../docs_src/body_updates/tutorial002.py!} + ``` + +### Partial updates recap + +In summary, to apply partial updates you would: + +* (Optionally) use `PATCH` instead of `PUT`. +* Retrieve the stored data. +* Put that data in a Pydantic model. +* Generate a `dict` without default values from the input model (using `exclude_unset`). + * This way you can update only the values actually set by the user, instead of overriding values already stored with default values in your model. +* Create a copy of the stored model, updating it's attributes with the received partial updates (using the `update` parameter). +* Convert the copied model to something that can be stored in your DB (for example, using the `jsonable_encoder`). + * This is comparable to using the model's `.dict()` method again, but it makes sure (and converts) the values to data types that can be converted to JSON, for example, `datetime` to `str`. +* Save the data to your DB. +* Return the updated model. + +=== "Python 3.10+" + + ```Python hl_lines="28-35" + {!> ../../../docs_src/body_updates/tutorial002_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="30-37" + {!> ../../../docs_src/body_updates/tutorial002_py39.py!} + ``` -!!! tip "提示" +=== "Python 3.6+" - 实际上,HTTP `PUT` 也可以完成相同的操作。 - 但本节以 `PATCH` 为例的原因是,该操作就是为了这种用例创建的。 + ```Python hl_lines="30-37" + {!> ../../../docs_src/body_updates/tutorial002.py!} + ``` -!!! note "笔记" +!!! tip + You can actually use this same technique with an HTTP `PUT` operation. - 注意,输入模型仍需验证。 + But the example here uses `PATCH` because it was created for these use cases. - 因此,如果希望接收的部分更新数据可以省略其他所有属性,则要把模型中所有的属性标记为可选(使用默认值或 `None`)。 +!!! note + Notice that the input model is still validated. - 为了区分用于**更新**所有可选值的模型与用于**创建**包含必选值的模型,请参照[更多模型](extra-models.md){.internal-link target=_blank} 一节中的思路。 + So, if you want to receive partial updates that can omit all the attributes, you need to have a model with all the attributes marked as optional (with default values or `None`). + + To distinguish from the models with all optional values for **updates** and models with required values for **creation**, you can use the ideas described in [Extra Models](extra-models.md){.internal-link target=_blank}. From fb1fa4a4fb94249ccc919619735623c3d927e0d7 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:27 +0800 Subject: [PATCH 066/163] New translations body.md (Chinese Simplified) --- docs/zh/docs/tutorial/body.md | 218 ++++++++++++++++++++++------------ 1 file changed, 142 insertions(+), 76 deletions(-) diff --git a/docs/zh/docs/tutorial/body.md b/docs/zh/docs/tutorial/body.md index f80ab5bf59069..9462df2c65f86 100644 --- a/docs/zh/docs/tutorial/body.md +++ b/docs/zh/docs/tutorial/body.md @@ -1,39 +1,57 @@ -# 请求体 +# Request Body -当你需要将数据从客户端(例如浏览器)发送给 API 时,你将其作为「请求体」发送。 +When you need to send data from a client (let's say, a browser) to your API, you send it as a **request body**. -**请求**体是客户端发送给 API 的数据。**响应**体是 API 发送给客户端的数据。 +A **request** body is data sent by the client to your API. A **response** body is the data your API sends to the client. -你的 API 几乎总是要发送**响应**体。但是客户端并不总是需要发送**请求**体。 +Your API almost always has to send a **response** body. But clients don't necessarily need to send **request** bodies all the time. -我们使用 Pydantic 模型来声明**请求**体,并能够获得它们所具有的所有能力和优点。 +To declare a **request** body, you use Pydantic models with all their power and benefits. !!! info - 你不能使用 `GET` 操作(HTTP 方法)发送请求体。 + To send data, you should use one of: `POST` (the more common), `PUT`, `DELETE` or `PATCH`. - 要发送数据,你必须使用下列方法之一:`POST`(较常见)、`PUT`、`DELETE` 或 `PATCH`。 + Sending a body with a `GET` request has an undefined behavior in the specifications, nevertheless, it is supported by FastAPI, only for very complex/extreme use cases. + + As it is discouraged, the interactive docs with Swagger UI won't show the documentation for the body when using `GET`, and proxies in the middle might not support it. -## 导入 Pydantic 的 `BaseModel` +## Import Pydantic's `BaseModel` -首先,你需要从 `pydantic` 中导入 `BaseModel`: +First, you need to import `BaseModel` from `pydantic`: -```Python hl_lines="2" -{!../../../docs_src/body/tutorial001.py!} -``` +=== "Python 3.10+" -## 创建数据模型 + ```Python hl_lines="2" + {!> ../../../docs_src/body/tutorial001_py310.py!} + ``` -然后,将你的数据模型声明为继承自 `BaseModel` 的类。 +=== "Python 3.6+" -使用标准的 Python 类型来声明所有属性: + ```Python hl_lines="4" + {!> ../../../docs_src/body/tutorial001.py!} + ``` -```Python hl_lines="5-9" -{!../../../docs_src/body/tutorial001.py!} -``` +## Create your data model + +Then you declare your data model as a class that inherits from `BaseModel`. + +Use standard Python types for all the attributes: + +=== "Python 3.10+" + + ```Python hl_lines="5-9" + {!> ../../../docs_src/body/tutorial001_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="7-11" + {!> ../../../docs_src/body/tutorial001.py!} + ``` -和声明查询参数时一样,当一个模型属性具有默认值时,它不是必需的。否则它是一个必需属性。将默认值设为 `None` 可使其成为可选属性。 +The same as when declaring query parameters, when a model attribute has a default value, it is not required. Otherwise, it is required. Use `None` to make it just optional. -例如,上面的模型声明了一个这样的 JSON「`object`」(或 Python `dict`): +For example, this model above declares a JSON "`object`" (or Python `dict`) like: ```JSON { @@ -44,7 +62,7 @@ } ``` -...由于 `description` 和 `tax` 是可选的(它们的默认值为 `None`),下面的 JSON「`object`」也将是有效的: +...as `description` and `tax` are optional (with a default value of `None`), this JSON "`object`" would also be valid: ```JSON { @@ -53,95 +71,143 @@ } ``` -## 声明为参数 +## Declare it as a parameter -使用与声明路径和查询参数的相同方式声明请求体,即可将其添加到「路径操作」中: +To add it to your *path operation*, declare it the same way you declared path and query parameters: -```Python hl_lines="16" -{!../../../docs_src/body/tutorial001.py!} -``` +=== "Python 3.10+" -...并且将它的类型声明为你创建的 `Item` 模型。 + ```Python hl_lines="16" + {!> ../../../docs_src/body/tutorial001_py310.py!} + ``` -## 结果 +=== "Python 3.6+" -仅仅使用了 Python 类型声明,**FastAPI** 将会: + ```Python hl_lines="18" + {!> ../../../docs_src/body/tutorial001.py!} + ``` -* 将请求体作为 JSON 读取。 -* 转换为相应的类型(在需要时)。 -* 校验数据。 - * 如果数据无效,将返回一条清晰易读的错误信息,指出不正确数据的确切位置和内容。 -* 将接收的数据赋值到参数 `item` 中。 - * 由于你已经在函数中将它声明为 `Item` 类型,你还将获得对于所有属性及其类型的一切编辑器支持(代码补全等)。 -* 为你的模型生成 JSON 模式 定义,你还可以在其他任何对你的项目有意义的地方使用它们。 -* 这些模式将成为生成的 OpenAPI 模式的一部分,并且被自动化文档 UI 所使用。 +...and declare its type as the model you created, `Item`. -## 自动化文档 +## Results -你所定义模型的 JSON 模式将成为生成的 OpenAPI 模式的一部分,并且在交互式 API 文档中展示: +With just that Python type declaration, **FastAPI** will: - +* Read the body of the request as JSON. +* Convert the corresponding types (if needed). +* Validate the data. + * If the data is invalid, it will return a nice and clear error, indicating exactly where and what was the incorrect data. +* Give you the received data in the parameter `item`. + * As you declared it in the function to be of type `Item`, you will also have all the editor support (completion, etc) for all of the attributes and their types. +* Generate JSON Schema definitions for your model, you can also use them anywhere else you like if it makes sense for your project. +* Those schemas will be part of the generated OpenAPI schema, and used by the automatic documentation UIs. -而且还将在每一个需要它们的*路径操作*的 API 文档中使用: +## Automatic docs - +The JSON Schemas of your models will be part of your OpenAPI generated schema, and will be shown in the interactive API docs: -## 编辑器支持 + -在你的编辑器中,你会在函数内部的任意地方得到类型提示和代码补全(如果你接收的是一个 `dict` 而不是 Pydantic 模型,则不会发生这种情况): +And will be also used in the API docs inside each *path operation* that needs them: - + -你还会获得对不正确的类型操作的错误检查: +## Editor support - +In your editor, inside your function you will get type hints and completion everywhere (this wouldn't happen if you received a `dict` instead of a Pydantic model): -这并非偶然,整个框架都是围绕该设计而构建。 + -并且在进行任何实现之前,已经在设计阶段经过了全面测试,以确保它可以在所有的编辑器中生效。 +You also get error checks for incorrect type operations: -Pydantic 本身甚至也进行了一些更改以支持此功能。 + -上面的截图取自 Visual Studio Code。 +This is not by chance, the whole framework was built around that design. -但是在 PyCharm 和绝大多数其他 Python 编辑器中你也会获得同样的编辑器支持: +And it was thoroughly tested at the design phase, before any implementation, to ensure it would work with all the editors. - +There were even some changes to Pydantic itself to support this. -## 使用模型 +The previous screenshots were taken with Visual Studio Code. -在函数内部,你可以直接访问模型对象的所有属性: +But you would get the same editor support with PyCharm and most of the other Python editors: -```Python hl_lines="19" -{!../../../docs_src/body/tutorial002.py!} -``` + -## 请求体 + 路径参数 +!!! tip + If you use PyCharm as your editor, you can use the Pydantic PyCharm Plugin. -你可以同时声明路径参数和请求体。 + It improves editor support for Pydantic models, with: -**FastAPI** 将识别出与路径参数匹配的函数参数应**从路径中获取**,而声明为 Pydantic 模型的函数参数应**从请求体中获取**。 + * auto-completion + * type checks + * refactoring + * searching + * inspections -```Python hl_lines="15-16" -{!../../../docs_src/body/tutorial003.py!} -``` +## Use the model -## 请求体 + 路径参数 + 查询参数 +Inside of the function, you can access all the attributes of the model object directly: -你还可以同时声明**请求体**、**路径参数**和**查询参数**。 +=== "Python 3.10+" -**FastAPI** 会识别它们中的每一个,并从正确的位置获取数据。 + ```Python hl_lines="19" + {!> ../../../docs_src/body/tutorial002_py310.py!} + ``` -```Python hl_lines="16" -{!../../../docs_src/body/tutorial004.py!} -``` +=== "Python 3.6+" + + ```Python hl_lines="21" + {!> ../../../docs_src/body/tutorial002.py!} + ``` + +## Request body + path parameters + +You can declare path parameters and request body at the same time. + +**FastAPI** will recognize that the function parameters that match path parameters should be **taken from the path**, and that function parameters that are declared to be Pydantic models should be **taken from the request body**. + +=== "Python 3.10+" + + ```Python hl_lines="15-16" + {!> ../../../docs_src/body/tutorial003_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="17-18" + {!> ../../../docs_src/body/tutorial003.py!} + ``` + +## Request body + path + query parameters + +You can also declare **body**, **path** and **query** parameters, all at the same time. + +**FastAPI** will recognize each of them and take the data from the correct place. + +=== "Python 3.10+" + + ```Python hl_lines="16" + {!> ../../../docs_src/body/tutorial004_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="18" + {!> ../../../docs_src/body/tutorial004.py!} + ``` + +The function parameters will be recognized as follows: + +* If the parameter is also declared in the **path**, it will be used as a path parameter. +* If the parameter is of a **singular type** (like `int`, `float`, `str`, `bool`, etc) it will be interpreted as a **query** parameter. +* If the parameter is declared to be of the type of a **Pydantic model**, it will be interpreted as a request **body**. -函数参数将依次按如下规则进行识别: +!!! note + FastAPI will know that the value of `q` is not required because of the default value `= None`. -* 如果在**路径**中也声明了该参数,它将被用作路径参数。 -* 如果参数属于**单一类型**(比如 `int`、`float`、`str`、`bool` 等)它将被解释为**查询**参数。 -* 如果参数的类型被声明为一个 **Pydantic 模型**,它将被解释为**请求体**。 + The `Union` in `Union[str, None]` is not used by FastAPI, but will allow your editor to give you better support and detect errors. -## 不使用 Pydantic +## Without Pydantic -如果你不想使用 Pydantic 模型,你还可以使用 **Body** 参数。请参阅文档 [请求体 - 多个参数:请求体中的单一值](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}。 +If you don't want to use Pydantic models, you can also use **Body** parameters. See the docs for [Body - Multiple Parameters: Singular values in body](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}. From a6c192d8dfc72afab606d747a1693ba731d58f8f Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:28 +0800 Subject: [PATCH 067/163] New translations cookie-params.md (Chinese Simplified) --- docs/zh/docs/tutorial/cookie-params.md | 101 ++++++++++++++++++++----- 1 file changed, 82 insertions(+), 19 deletions(-) diff --git a/docs/zh/docs/tutorial/cookie-params.md b/docs/zh/docs/tutorial/cookie-params.md index d67daf0f9051e..111e93458e686 100644 --- a/docs/zh/docs/tutorial/cookie-params.md +++ b/docs/zh/docs/tutorial/cookie-params.md @@ -1,34 +1,97 @@ -# Cookie 参数 +# Cookie Parameters -你可以像定义 `Query` 参数和 `Path` 参数一样来定义 `Cookie` 参数。 +You can define Cookie parameters the same way you define `Query` and `Path` parameters. -## 导入 `Cookie` +## Import `Cookie` -首先,导入 `Cookie`: +First import `Cookie`: -```Python hl_lines="3" -{!../../../docs_src/cookie_params/tutorial001.py!} -``` +=== "Python 3.10+" -## 声明 `Cookie` 参数 + ```Python hl_lines="3" + {!> ../../../docs_src/cookie_params/tutorial001_an_py310.py!} + ``` -声明 `Cookie` 参数的结构与声明 `Query` 参数和 `Path` 参数时相同。 +=== "Python 3.9+" -第一个值是参数的默认值,同时也可以传递所有验证参数或注释参数,来校验参数: + ```Python hl_lines="3" + {!> ../../../docs_src/cookie_params/tutorial001_an_py39.py!} + ``` +=== "Python 3.6+" -```Python hl_lines="9" -{!../../../docs_src/cookie_params/tutorial001.py!} -``` + ```Python hl_lines="3" + {!> ../../../docs_src/cookie_params/tutorial001_an.py!} + ``` -!!! note "技术细节" - `Cookie` 、`Path` 、`Query`是兄弟类,它们都继承自公共的 `Param` 类 +=== "Python 3.10+ non-Annotated" - 但请记住,当你从 `fastapi` 导入的 `Query`、`Path`、`Cookie` 或其他参数声明函数,这些实际上是返回特殊类的函数。 + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="1" + {!> ../../../docs_src/cookie_params/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="3" + {!> ../../../docs_src/cookie_params/tutorial001.py!} + ``` + +## Declare `Cookie` parameters + +Then declare the cookie parameters using the same structure as with `Path` and `Query`. + +The first value is the default value, you can pass all the extra validation or annotation parameters: + +=== "Python 3.10+" + + ```Python hl_lines="9" + {!> ../../../docs_src/cookie_params/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/cookie_params/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="10" + {!> ../../../docs_src/cookie_params/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7" + {!> ../../../docs_src/cookie_params/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="9" + {!> ../../../docs_src/cookie_params/tutorial001.py!} + ``` + +!!! note "Technical Details" + `Cookie` is a "sister" class of `Path` and `Query`. It also inherits from the same common `Param` class. + + But remember that when you import `Query`, `Path`, `Cookie` and others from `fastapi`, those are actually functions that return special classes. !!! info - 你需要使用 `Cookie` 来声明 cookie 参数,否则参数将会被解释为查询参数。 + To declare cookies, you need to use `Cookie`, because otherwise the parameters would be interpreted as query parameters. -## 总结 +## Recap -使用 `Cookie` 声明 cookie 参数,使用方式与 `Query` 和 `Path` 类似。 +Declare cookies with `Cookie`, using the same common pattern as `Query` and `Path`. From ff6a7e191ae0bc55509ff956ff916e8c83d23a04 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:29 +0800 Subject: [PATCH 068/163] New translations cors.md (Chinese Simplified) --- docs/zh/docs/tutorial/cors.md | 88 +++++++++++++++++------------------ 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/docs/zh/docs/tutorial/cors.md b/docs/zh/docs/tutorial/cors.md index ddd4e76825366..33b11983b32b5 100644 --- a/docs/zh/docs/tutorial/cors.md +++ b/docs/zh/docs/tutorial/cors.md @@ -1,84 +1,84 @@ -# CORS(跨域资源共享) +# CORS (Cross-Origin Resource Sharing) -CORS 或者「跨域资源共享」 指浏览器中运行的前端拥有与后端通信的 JavaScript 代码,而后端处于与前端不同的「源」的情况。 +CORS or "Cross-Origin Resource Sharing" refers to the situations when a frontend running in a browser has JavaScript code that communicates with a backend, and the backend is in a different "origin" than the frontend. -## 源 +## Origin -源是协议(`http`,`https`)、域(`myapp.com`,`localhost`,`localhost.tiangolo.com`)以及端口(`80`、`443`、`8080`)的组合。 +An origin is the combination of protocol (`http`, `https`), domain (`myapp.com`, `localhost`, `localhost.tiangolo.com`), and port (`80`, `443`, `8080`). -因此,这些都是不同的源: +So, all these are different origins: * `http://localhost` * `https://localhost` * `http://localhost:8080` -即使它们都在 `localhost` 中,但是它们使用不同的协议或者端口,所以它们都是不同的「源」。 +Even if they are all in `localhost`, they use different protocols or ports, so, they are different "origins". -## 步骤 +## Steps -假设你的浏览器中有一个前端运行在 `http://localhost:8080`,并且它的 JavaScript 正在尝试与运行在 `http://localhost` 的后端通信(因为我们没有指定端口,浏览器会采用默认的端口 `80`)。 +So, let's say you have a frontend running in your browser at `http://localhost:8080`, and its JavaScript is trying to communicate with a backend running at `http://localhost` (because we don't specify a port, the browser will assume the default port `80`). -然后,浏览器会向后端发送一个 HTTP `OPTIONS` 请求,如果后端发送适当的 headers 来授权来自这个不同源(`http://localhost:8080`)的通信,浏览器将允许前端的 JavaScript 向后端发送请求。 +Then, the browser will send an HTTP `OPTIONS` request to the backend, and if the backend sends the appropriate headers authorizing the communication from this different origin (`http://localhost:8080`) then the browser will let the JavaScript in the frontend send its request to the backend. -为此,后端必须有一个「允许的源」列表。 +To achieve this, the backend must have a list of "allowed origins". -在这种情况下,它必须包含 `http://localhost:8080`,前端才能正常工作。 +In this case, it would have to include `http://localhost:8080` for the frontend to work correctly. -## 通配符 +## Wildcards -也可以使用 `"*"`(一个「通配符」)声明这个列表,表示全部都是允许的。 +It's also possible to declare the list as `"*"` (a "wildcard") to say that all are allowed. -但这仅允许某些类型的通信,不包括所有涉及凭据的内容:像 Cookies 以及那些使用 Bearer 令牌的授权 headers 等。 +But that will only allow certain types of communication, excluding everything that involves credentials: Cookies, Authorization headers like those used with Bearer Tokens, etc. -因此,为了一切都能正常工作,最好显式地指定允许的源。 +So, for everything to work correctly, it's better to specify explicitly the allowed origins. -## 使用 `CORSMiddleware` +## Use `CORSMiddleware` -你可以在 **FastAPI** 应用中使用 `CORSMiddleware` 来配置它。 +You can configure it in your **FastAPI** application using the `CORSMiddleware`. -* 导入 `CORSMiddleware`。 -* 创建一个允许的源列表(由字符串组成)。 -* 将其作为「中间件」添加到你的 **FastAPI** 应用中。 +* Import `CORSMiddleware`. +* Create a list of allowed origins (as strings). +* Add it as a "middleware" to your **FastAPI** application. -你也可以指定后端是否允许: +You can also specify if your backend allows: -* 凭证(授权 headers,Cookies 等)。 -* 特定的 HTTP 方法(`POST`,`PUT`)或者使用通配符 `"*"` 允许所有方法。 -* 特定的 HTTP headers 或者使用通配符 `"*"` 允许所有 headers。 +* Credentials (Authorization headers, Cookies, etc). +* Specific HTTP methods (`POST`, `PUT`) or all of them with the wildcard `"*"`. +* Specific HTTP headers or all of them with the wildcard `"*"`. ```Python hl_lines="2 6-11 13-19" {!../../../docs_src/cors/tutorial001.py!} ``` -默认情况下,这个 `CORSMiddleware` 实现所使用的默认参数较为保守,所以你需要显式地启用特定的源、方法或者 headers,以便浏览器能够在跨域上下文中使用它们。 +The default parameters used by the `CORSMiddleware` implementation are restrictive by default, so you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be permitted to use them in a Cross-Domain context. -支持以下参数: +The following arguments are supported: -* `allow_origins` - 一个允许跨域请求的源列表。例如 `['https://example.org', 'https://www.example.org']`。你可以使用 `['*']` 允许任何源。 -* `allow_origin_regex` - 一个正则表达式字符串,匹配的源允许跨域请求。例如 `'https://.*\.example\.org'`。 -* `allow_methods` - 一个允许跨域请求的 HTTP 方法列表。默认为 `['GET']`。你可以使用 `['*']` 来允许所有标准方法。 -* `allow_headers` - 一个允许跨域请求的 HTTP 请求头列表。默认为 `[]`。你可以使用 `['*']` 允许所有的请求头。`Accept`、`Accept-Language`、`Content-Language` 以及 `Content-Type` 请求头总是允许 CORS 请求。 -* `allow_credentials` - 指示跨域请求支持 cookies。默认是 `False`。另外,允许凭证时 `allow_origins` 不能设定为 `['*']`,必须指定源。 -* `expose_headers` - 指示可以被浏览器访问的响应头。默认为 `[]`。 -* `max_age` - 设定浏览器缓存 CORS 响应的最长时间,单位是秒。默认为 `600`。 +* `allow_origins` - A list of origins that should be permitted to make cross-origin requests. E.g. `['https://example.org', 'https://www.example.org']`. You can use `['*']` to allow any origin. +* `allow_origin_regex` - A regex string to match against origins that should be permitted to make cross-origin requests. e.g. `'https://.*\.example\.org'`. +* `allow_methods` - A list of HTTP methods that should be allowed for cross-origin requests. Defaults to `['GET']`. You can use `['*']` to allow all standard methods. +* `allow_headers` - A list of HTTP request headers that should be supported for cross-origin requests. Defaults to `[]`. You can use `['*']` to allow all headers. The `Accept`, `Accept-Language`, `Content-Language` and `Content-Type` headers are always allowed for simple CORS requests. +* `allow_credentials` - Indicate that cookies should be supported for cross-origin requests. Defaults to `False`. Also, `allow_origins` cannot be set to `['*']` for credentials to be allowed, origins must be specified. +* `expose_headers` - Indicate any response headers that should be made accessible to the browser. Defaults to `[]`. +* `max_age` - Sets a maximum time in seconds for browsers to cache CORS responses. Defaults to `600`. -中间件响应两种特定类型的 HTTP 请求…… +The middleware responds to two particular types of HTTP request... -### CORS 预检请求 +### CORS preflight requests -这是些带有 `Origin` 和 `Access-Control-Request-Method` 请求头的 `OPTIONS` 请求。 +These are any `OPTIONS` request with `Origin` and `Access-Control-Request-Method` headers. -在这种情况下,中间件将拦截传入的请求并进行响应,出于提供信息的目的返回一个使用了适当的 CORS headers 的 `200` 或 `400` 响应。 +In this case the middleware will intercept the incoming request and respond with appropriate CORS headers, and either a `200` or `400` response for informational purposes. -### 简单请求 +### Simple requests -任何带有 `Origin` 请求头的请求。在这种情况下,中间件将像平常一样传递请求,但是在响应中包含适当的 CORS headers。 +Any request with an `Origin` header. In this case the middleware will pass the request through as normal, but will include appropriate CORS headers on the response. -## 更多信息 +## More info -更多关于 CORS 的信息,请查看 Mozilla CORS 文档。 +For more info about CORS, check the Mozilla CORS documentation. -!!! note "技术细节" - 你也可以使用 `from starlette.middleware.cors import CORSMiddleware`。 +!!! note "Technical Details" + You could also use `from starlette.middleware.cors import CORSMiddleware`. - 出于方便,**FastAPI** 在 `fastapi.middleware` 中为开发者提供了几个中间件。但是大多数可用的中间件都是直接来自 Starlette。 + **FastAPI** provides several middlewares in `fastapi.middleware` just as a convenience for you, the developer. But most of the available middlewares come directly from Starlette. From fdbb610d63746b87f31cfb48b224973566062f55 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:30 +0800 Subject: [PATCH 069/163] New translations debugging.md (Chinese Simplified) --- docs/zh/docs/tutorial/debugging.md | 74 ++++++++++++++++-------------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/docs/zh/docs/tutorial/debugging.md b/docs/zh/docs/tutorial/debugging.md index 51801d4984b9e..8d76347cffcba 100644 --- a/docs/zh/docs/tutorial/debugging.md +++ b/docs/zh/docs/tutorial/debugging.md @@ -1,18 +1,18 @@ -# 调试 +# Debugging -你可以在编辑器中连接调试器,例如使用 Visual Studio Code 或 PyCharm。 +You can connect the debugger in your editor, for example with Visual Studio Code or PyCharm. -## 调用 `uvicorn` +## Call `uvicorn` -在你的 FastAPI 应用中直接导入 `uvicorn` 并运行: +In your FastAPI application, import and run `uvicorn` directly: ```Python hl_lines="1 15" {!../../../docs_src/debugging/tutorial001.py!} ``` -### 关于 `__name__ == "__main__"` +### About `__name__ == "__main__"` -`__name__ == "__main__"` 的主要目的是使用以下代码调用文件时执行一些代码: +The main purpose of the `__name__ == "__main__"` is to have some code that is executed when your file is called with:
@@ -22,17 +22,17 @@ $ python myapp.py
-而当其它文件导入它时并不会被调用,像这样: +but is not called when another file imports it, like in: ```Python from myapp import app ``` -#### 更多细节 +#### More details -假设你的文件命名为 `myapp.py`。 +Let's say your file is named `myapp.py`. -如果你这样运行: +If you run it with:
@@ -42,19 +42,21 @@ $ python myapp.py
-那么文件中由 Python 自动创建的内部变量 `__name__`,会将字符串 `"__main__"` 作为值。 +then the internal variable `__name__` in your file, created automatically by Python, will have as value the string `"__main__"`. -所以,下面这部分代码才会运行: +So, the section: ```Python uvicorn.run(app, host="0.0.0.0", port=8000) ``` +will run. + --- -如果你是导入这个模块(文件)就不会这样。 +This won't happen if you import that module (file). -因此,如果你的另一个文件 `importer.py` 像这样: +So, if you have another file `importer.py` with: ```Python from myapp import app @@ -62,47 +64,49 @@ from myapp import app # Some more code ``` -在这种情况下,`myapp.py` 内部的自动变量不会有值为 `"__main__"` 的变量 `__name__`。 +in that case, the automatically created variable inside of `myapp.py` will not have the variable `__name__` with a value of `"__main__"`. -所以,下面这一行不会被执行: +So, the line: ```Python uvicorn.run(app, host="0.0.0.0", port=8000) ``` +will not be executed. + !!! info - 更多信息请检查 Python 官方文档. + For more information, check the official Python docs. -## 使用你的调试器运行代码 +## Run your code with your debugger -由于是从代码直接运行的 Uvicorn 服务器,所以你可以从调试器直接调用 Python 程序(你的 FastAPI 应用)。 +Because you are running the Uvicorn server directly from your code, you can call your Python program (your FastAPI application) directly from the debugger. --- -例如,你可以在 Visual Studio Code 中: +For example, in Visual Studio Code, you can: -* 进入到「调试」面板。 -* 「添加配置...」。 -* 选中「Python」 -* 运行「Python:当前文件(集成终端)」选项的调试器。 +* Go to the "Debug" panel. +* "Add configuration...". +* Select "Python" +* Run the debugger with the option "`Python: Current File (Integrated Terminal)`". -然后它会使用你的 **FastAPI** 代码开启服务器,停在断点处,等等。 +It will then start the server with your **FastAPI** code, stop at your breakpoints, etc. -看起来可能是这样: +Here's how it might look: - + --- -如果使用 Pycharm,你可以: +If you use Pycharm, you can: -* 打开「运行」菜单。 -* 选中「调试...」。 -* 然后出现一个上下文菜单。 -* 选择要调试的文件(本例中的 `main.py`)。 +* Open the "Run" menu. +* Select the option "Debug...". +* Then a context menu shows up. +* Select the file to debug (in this case, `main.py`). -然后它会使用你的 **FastAPI** 代码开启服务器,停在断点处,等等。 +It will then start the server with your **FastAPI** code, stop at your breakpoints, etc. -看起来可能是这样: +Here's how it might look: - + From 8c6d11dcb0c1a8e481d5e43ecc42ad172feafa0c Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:31 +0800 Subject: [PATCH 070/163] New translations classes-as-dependencies.md (Chinese Simplified) --- .../dependencies/classes-as-dependencies.md | 406 ++++++++++++++---- 1 file changed, 320 insertions(+), 86 deletions(-) diff --git a/docs/zh/docs/tutorial/dependencies/classes-as-dependencies.md b/docs/zh/docs/tutorial/dependencies/classes-as-dependencies.md index f404820df0119..a1409c8f88701 100644 --- a/docs/zh/docs/tutorial/dependencies/classes-as-dependencies.md +++ b/docs/zh/docs/tutorial/dependencies/classes-as-dependencies.md @@ -1,58 +1,82 @@ -# 类作为依赖项 +# Classes as Dependencies -在深入探究 **依赖注入** 系统之前,让我们升级之前的例子。 +Before diving deeper into the **Dependency Injection** system, let's upgrade the previous example. -## 来自前一个例子的`dict` +## A `dict` from the previous example -在前面的例子中, 我们从依赖项 ("可依赖对象") 中返回了一个 `dict`: +In the previous example, we were returning a `dict` from our dependency ("dependable"): === "Python 3.10+" + ```Python hl_lines="9" + {!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="11" + {!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="12" + {!> ../../../docs_src/dependencies/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + ```Python hl_lines="7" {!> ../../../docs_src/dependencies/tutorial001_py310.py!} ``` -=== "Python 3.6+" +=== "Python 3.6+ non-Annotated" - ```Python hl_lines="9" + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="11" {!> ../../../docs_src/dependencies/tutorial001.py!} ``` -但是后面我们在路径操作函数的参数 `commons` 中得到了一个 `dict`。 +But then we get a `dict` in the parameter `commons` of the *path operation function*. -我们知道编辑器不能为 `dict` 提供很多支持(比如补全),因为编辑器不知道 `dict` 的键和值类型。 +And we know that editors can't provide a lot of support (like completion) for `dict`s, because they can't know their keys and value types. -对此,我们可以做的更好... +We can do better... -## 什么构成了依赖项? +## What makes a dependency -到目前为止,您看到的依赖项都被声明为函数。 +Up to now you have seen dependencies declared as functions. -但这并不是声明依赖项的唯一方法(尽管它可能是更常见的方法)。 +But that's not the only way to declare dependencies (although it would probably be the more common). -关键因素是依赖项应该是 "可调用对象"。 +The key factor is that a dependency should be a "callable". -Python 中的 "**可调用对象**" 是指任何 Python 可以像函数一样 "调用" 的对象。 +A "**callable**" in Python is anything that Python can "call" like a function. -所以,如果你有一个对象 `something` (可能*不是*一个函数),你可以 "调用" 它(执行它),就像: +So, if you have an object `something` (that might _not_ be a function) and you can "call" it (execute it) like: ```Python something() ``` -或者 +or ```Python something(some_argument, some_keyword_argument="foo") ``` -这就是 "可调用对象"。 +then it is a "callable". -## 类作为依赖项 +## Classes as dependencies -您可能会注意到,要创建一个 Python 类的实例,您可以使用相同的语法。 +You might notice that to create an instance of a Python class, you use that same syntax. -举个例子: +For example: ```Python class Cat: @@ -63,185 +87,395 @@ class Cat: fluffy = Cat(name="Mr Fluffy") ``` -在这个例子中, `fluffy` 是一个 `Cat` 类的实例。 +In this case, `fluffy` is an instance of the class `Cat`. -为了创建 `fluffy`,你调用了 `Cat` 。 +And to create `fluffy`, you are "calling" `Cat`. -所以,Python 类也是 **可调用对象**。 +So, a Python class is also a **callable**. -因此,在 **FastAPI** 中,你可以使用一个 Python 类作为一个依赖项。 +Then, in **FastAPI**, you could use a Python class as a dependency. -实际上 FastAPI 检查的是它是一个 "可调用对象"(函数,类或其他任何类型)以及定义的参数。 +What FastAPI actually checks is that it is a "callable" (function, class or anything else) and the parameters defined. -如果您在 **FastAPI** 中传递一个 "可调用对象" 作为依赖项,它将分析该 "可调用对象" 的参数,并以处理路径操作函数的参数的方式来处理它们。包括子依赖项。 +If you pass a "callable" as a dependency in **FastAPI**, it will analyze the parameters for that "callable", and process them in the same way as the parameters for a *path operation function*. Including sub-dependencies. -这也适用于完全没有参数的可调用对象。这与不带参数的路径操作函数一样。 +That also applies to callables with no parameters at all. The same as it would be for *path operation functions* with no parameters. -所以,我们可以将上面的依赖项 "可依赖对象" `common_parameters` 更改为类 `CommonQueryParams`: +Then, we can change the dependency "dependable" `common_parameters` from above to the class `CommonQueryParams`: === "Python 3.10+" + ```Python hl_lines="11-15" + {!> ../../../docs_src/dependencies/tutorial002_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="11-15" + {!> ../../../docs_src/dependencies/tutorial002_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="12-16" + {!> ../../../docs_src/dependencies/tutorial002_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + ```Python hl_lines="9-13" {!> ../../../docs_src/dependencies/tutorial002_py310.py!} ``` -=== "Python 3.6+" +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. ```Python hl_lines="11-15" {!> ../../../docs_src/dependencies/tutorial002.py!} ``` -注意用于创建类实例的 `__init__` 方法: +Pay attention to the `__init__` method used to create the instance of the class: === "Python 3.10+" + ```Python hl_lines="12" + {!> ../../../docs_src/dependencies/tutorial002_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="12" + {!> ../../../docs_src/dependencies/tutorial002_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="13" + {!> ../../../docs_src/dependencies/tutorial002_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + ```Python hl_lines="10" {!> ../../../docs_src/dependencies/tutorial002_py310.py!} ``` -=== "Python 3.6+" +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. ```Python hl_lines="12" {!> ../../../docs_src/dependencies/tutorial002.py!} ``` -...它与我们以前的 `common_parameters` 具有相同的参数: +...it has the same parameters as our previous `common_parameters`: === "Python 3.10+" + ```Python hl_lines="8" + {!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="10" + {!> ../../../docs_src/dependencies/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + ```Python hl_lines="6" {!> ../../../docs_src/dependencies/tutorial001_py310.py!} ``` -=== "Python 3.6+" +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. ```Python hl_lines="9" {!> ../../../docs_src/dependencies/tutorial001.py!} ``` -这些参数就是 **FastAPI** 用来 "处理" 依赖项的。 +Those parameters are what **FastAPI** will use to "solve" the dependency. -在两个例子下,都有: +In both cases, it will have: -* 一个可选的 `q` 查询参数,是 `str` 类型。 -* 一个 `skip` 查询参数,是 `int` 类型,默认值为 `0`。 -* 一个 `limit` 查询参数,是 `int` 类型,默认值为 `100`。 +* An optional `q` query parameter that is a `str`. +* A `skip` query parameter that is an `int`, with a default of `0`. +* A `limit` query parameter that is an `int`, with a default of `100`. -在两个例子下,数据都将被转换、验证、在 OpenAPI schema 上文档化,等等。 +In both cases the data will be converted, validated, documented on the OpenAPI schema, etc. -## 使用它 +## Use it -现在,您可以使用这个类来声明你的依赖项了。 +Now you can declare your dependency using this class. === "Python 3.10+" + ```Python hl_lines="19" + {!> ../../../docs_src/dependencies/tutorial002_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="19" + {!> ../../../docs_src/dependencies/tutorial002_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="20" + {!> ../../../docs_src/dependencies/tutorial002_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + ```Python hl_lines="17" {!> ../../../docs_src/dependencies/tutorial002_py310.py!} ``` -=== "Python 3.6+" +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. ```Python hl_lines="19" {!> ../../../docs_src/dependencies/tutorial002.py!} ``` -**FastAPI** 调用 `CommonQueryParams` 类。这将创建该类的一个 "实例",该实例将作为参数 `commons` 被传递给你的函数。 +**FastAPI** calls the `CommonQueryParams` class. This creates an "instance" of that class and the instance will be passed as the parameter `commons` to your function. -## 类型注解 vs `Depends` +## Type annotation vs `Depends` -注意,我们在上面的代码中编写了两次`CommonQueryParams`: +Notice how we write `CommonQueryParams` twice in the above code: -```Python -commons: CommonQueryParams = Depends(CommonQueryParams) -``` +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python + commons: CommonQueryParams = Depends(CommonQueryParams) + ``` + +=== "Python 3.6+" + + ```Python + commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] + ``` -最后的 `CommonQueryParams`: +The last `CommonQueryParams`, in: ```Python -... = Depends(CommonQueryParams) +... Depends(CommonQueryParams) ``` -...实际上是 **Fastapi** 用来知道依赖项是什么的。 +...is what **FastAPI** will actually use to know what is the dependency. -FastAPI 将从依赖项中提取声明的参数,这才是 FastAPI 实际调用的。 +From it is that FastAPI will extract the declared parameters and that is what FastAPI will actually call. --- -在本例中,第一个 `CommonQueryParams` : +In this case, the first `CommonQueryParams`, in: -```Python -commons: CommonQueryParams ... -``` +=== "Python 3.6+" -...对于 **FastAPI** 没有任何特殊的意义。FastAPI 不会使用它进行数据转换、验证等 (因为对于这,它使用 `= Depends(CommonQueryParams)`)。 + ```Python + commons: Annotated[CommonQueryParams, ... + ``` -你实际上可以只这样编写: +=== "Python 3.6+ non-Annotated" -```Python -commons = Depends(CommonQueryParams) -``` + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python + commons: CommonQueryParams ... + ``` + +...doesn't have any special meaning for **FastAPI**. FastAPI won't use it for data conversion, validation, etc. (as it is using the `Depends(CommonQueryParams)` for that). + +You could actually write just: + +=== "Python 3.6+" + + ```Python + commons: Annotated[Any, Depends(CommonQueryParams)] + ``` + +=== "Python 3.6+ non-Annotated" -..就像: + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python + commons = Depends(CommonQueryParams) + ``` + +..as in: === "Python 3.10+" + ```Python hl_lines="19" + {!> ../../../docs_src/dependencies/tutorial003_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="19" + {!> ../../../docs_src/dependencies/tutorial003_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="20" + {!> ../../../docs_src/dependencies/tutorial003_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + ```Python hl_lines="17" {!> ../../../docs_src/dependencies/tutorial003_py310.py!} ``` -=== "Python 3.6+" +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. ```Python hl_lines="19" {!> ../../../docs_src/dependencies/tutorial003.py!} ``` -但是声明类型是被鼓励的,因为那样你的编辑器就会知道将传递什么作为参数 `commons` ,然后它可以帮助你完成代码,类型检查,等等: +But declaring the type is encouraged as that way your editor will know what will be passed as the parameter `commons`, and then it can help you with code completion, type checks, etc: - + -## 快捷方式 +## Shortcut -但是您可以看到,我们在这里有一些代码重复了,编写了`CommonQueryParams`两次: +But you see that we are having some code repetition here, writing `CommonQueryParams` twice: -```Python -commons: CommonQueryParams = Depends(CommonQueryParams) -``` +=== "Python 3.6+ non-Annotated" -**FastAPI** 为这些情况提供了一个快捷方式,在这些情况下,依赖项 *明确地* 是一个类,**FastAPI** 将 "调用" 它来创建类本身的一个实例。 + !!! tip + Prefer to use the `Annotated` version if possible. -对于这些特定的情况,您可以跟随以下操作: + ```Python + commons: CommonQueryParams = Depends(CommonQueryParams) + ``` -不是写成这样: +=== "Python 3.6+" -```Python -commons: CommonQueryParams = Depends(CommonQueryParams) -``` + ```Python + commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] + ``` -...而是这样写: +**FastAPI** provides a shortcut for these cases, in where the dependency is *specifically* a class that **FastAPI** will "call" to create an instance of the class itself. -```Python -commons: CommonQueryParams = Depends() -``` +For those specific cases, you can do the following: + +Instead of writing: + +=== "Python 3.6+" + + ```Python + commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python + commons: CommonQueryParams = Depends(CommonQueryParams) + ``` + +...you write: + +=== "Python 3.6+" + + ```Python + commons: Annotated[CommonQueryParams, Depends()] + ``` + +=== "Python 3.6 non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python + commons: CommonQueryParams = Depends() + ``` -您声明依赖项作为参数的类型,并使用 `Depends()` 作为该函数的参数的 "默认" 值(在 `=` 之后),而在 `Depends()` 中没有任何参数,而不是在 `Depends(CommonQueryParams)` 编写完整的类。 +You declare the dependency as the type of the parameter, and you use `Depends()` without any parameter, instead of having to write the full class *again* inside of `Depends(CommonQueryParams)`. -同样的例子看起来像这样: +The same example would then look like: === "Python 3.10+" + ```Python hl_lines="19" + {!> ../../../docs_src/dependencies/tutorial004_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="19" + {!> ../../../docs_src/dependencies/tutorial004_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="20" + {!> ../../../docs_src/dependencies/tutorial004_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + ```Python hl_lines="17" {!> ../../../docs_src/dependencies/tutorial004_py310.py!} ``` -=== "Python 3.6+" +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. ```Python hl_lines="19" {!> ../../../docs_src/dependencies/tutorial004.py!} ``` -... **FastAPI** 会知道怎么处理。 +...and **FastAPI** will know what to do. !!! tip - 如果这看起来更加混乱而不是更加有帮助,那么请忽略它,你不*需要*它。 + If that seems more confusing than helpful, disregard it, you don't *need* it. - 这只是一个快捷方式。因为 **FastAPI** 关心的是帮助您减少代码重复。 + It is just a shortcut. Because **FastAPI** cares about helping you minimize code repetition. From fcc5ba1f2385fed8ab44c671099947e380169e9d Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:31 +0800 Subject: [PATCH 071/163] New translations dependencies-in-path-operation-decorators.md (Chinese Simplified) --- ...pendencies-in-path-operation-decorators.md | 148 +++++++++++++----- 1 file changed, 107 insertions(+), 41 deletions(-) diff --git a/docs/zh/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md b/docs/zh/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md index 61ea371e5453e..00a90424934af 100644 --- a/docs/zh/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md +++ b/docs/zh/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md @@ -1,73 +1,139 @@ -# 路径操作装饰器依赖项 +# Dependencies in path operation decorators -有时,我们并不需要在*路径操作函数*中使用依赖项的返回值。 +In some cases you don't really need the return value of a dependency inside your *path operation function*. -或者说,有些依赖项不返回值。 +Or the dependency doesn't return a value. -但仍要执行或解析该依赖项。 +But you still need it to be executed/solved. -对于这种情况,不必在声明*路径操作函数*的参数时使用 `Depends`,而是可以在*路径操作装饰器*中添加一个由 `dependencies` 组成的 `list`。 +For those cases, instead of declaring a *path operation function* parameter with `Depends`, you can add a `list` of `dependencies` to the *path operation decorator*. -## 在*路径操作装饰器*中添加 `dependencies` 参数 +## Add `dependencies` to the *path operation decorator* -*路径操作装饰器*支持可选参数 ~ `dependencies`。 +The *path operation decorator* receives an optional argument `dependencies`. -该参数的值是由 `Depends()` 组成的 `list`: +It should be a `list` of `Depends()`: -```Python hl_lines="17" -{!../../../docs_src/dependencies/tutorial006.py!} -``` +=== "Python 3.9+" -路径操作装饰器依赖项(以下简称为**“路径装饰器依赖项”**)的执行或解析方式和普通依赖项一样,但就算这些依赖项会返回值,它们的值也不会传递给*路径操作函数*。 + ```Python hl_lines="19" + {!> ../../../docs_src/dependencies/tutorial006_an_py39.py!} + ``` -!!! tip "提示" +=== "Python 3.6+" - 有些编辑器会检查代码中没使用过的函数参数,并显示错误提示。 + ```Python hl_lines="18" + {!> ../../../docs_src/dependencies/tutorial006_an.py!} + ``` - 在*路径操作装饰器*中使用 `dependencies` 参数,可以确保在执行依赖项的同时,避免编辑器显示错误提示。 +=== "Python 3.6 non-Annotated" - 使用路径装饰器依赖项还可以避免开发新人误会代码中包含无用的未使用参数。 + !!! tip + Prefer to use the `Annotated` version if possible. -!!! info "说明" + ```Python hl_lines="17" + {!> ../../../docs_src/dependencies/tutorial006.py!} + ``` - 本例中,使用的是自定义响应头 `X-Key` 和 `X-Token`。 +These dependencies will be executed/solved the same way normal dependencies. But their value (if they return any) won't be passed to your *path operation function*. - 但实际开发中,尤其是在实现安全措施时,最好使用 FastAPI 内置的[安全工具](../security/index.md){.internal-link target=_blank}(详见下一章)。 +!!! tip + Some editors check for unused function parameters, and show them as errors. -## 依赖项错误和返回值 + Using these `dependencies` in the *path operation decorator* you can make sure they are executed while avoiding editor/tooling errors. + + It might also help avoid confusion for new developers that see an unused parameter in your code and could think it's unnecessary. -路径装饰器依赖项也可以使用普通的依赖项*函数*。 +!!! info + In this example we use invented custom headers `X-Key` and `X-Token`. -### 依赖项的需求项 + But in real cases, when implementing security, you would get more benefits from using the integrated [Security utilities (the next chapter)](../security/index.md){.internal-link target=_blank}. -路径装饰器依赖项可以声明请求的需求项(比如响应头)或其他子依赖项: +## Dependencies errors and return values -```Python hl_lines="6 11" -{!../../../docs_src/dependencies/tutorial006.py!} -``` +You can use the same dependency *functions* you use normally. -### 触发异常 +### Dependency requirements -路径装饰器依赖项与正常的依赖项一样,可以 `raise` 异常: +They can declare request requirements (like headers) or other sub-dependencies: -```Python hl_lines="8 13" -{!../../../docs_src/dependencies/tutorial006.py!} -``` +=== "Python 3.9+" -### 返回值 + ```Python hl_lines="8 13" + {!> ../../../docs_src/dependencies/tutorial006_an_py39.py!} + ``` -无论路径装饰器依赖项是否返回值,路径操作都不会使用这些值。 +=== "Python 3.6+" -因此,可以复用在其他位置使用过的、(能返回值的)普通依赖项,即使没有使用这个值,也会执行该依赖项: + ```Python hl_lines="7 12" + {!> ../../../docs_src/dependencies/tutorial006_an.py!} + ``` -```Python hl_lines="9 14" -{!../../../docs_src/dependencies/tutorial006.py!} -``` +=== "Python 3.6 non-Annotated" -## 为一组路径操作定义依赖项 + !!! tip + Prefer to use the `Annotated` version if possible. -稍后,[大型应用 - 多文件](../../tutorial/bigger-applications.md){.internal-link target=\_blank}一章中会介绍如何使用多个文件创建大型应用程序,在这一章中,您将了解到如何为一组*路径操作*声明单个 `dependencies` 参数。 + ```Python hl_lines="6 11" + {!> ../../../docs_src/dependencies/tutorial006.py!} + ``` -## 全局依赖项 +### Raise exceptions -接下来,我们将学习如何为 `FastAPI` 应用程序添加全局依赖项,创建应用于每个*路径操作*的依赖项。 +These dependencies can `raise` exceptions, the same as normal dependencies: + +=== "Python 3.9+" + + ```Python hl_lines="10 15" + {!> ../../../docs_src/dependencies/tutorial006_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9 14" + {!> ../../../docs_src/dependencies/tutorial006_an.py!} + ``` + +=== "Python 3.6 non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="8 13" + {!> ../../../docs_src/dependencies/tutorial006.py!} + ``` + +### Return values + +And they can return values or not, the values won't be used. + +So, you can re-use a normal dependency (that returns a value) you already use somewhere else, and even though the value won't be used, the dependency will be executed: + +=== "Python 3.9+" + + ```Python hl_lines="11 16" + {!> ../../../docs_src/dependencies/tutorial006_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="10 15" + {!> ../../../docs_src/dependencies/tutorial006_an.py!} + ``` + +=== "Python 3.6 non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="9 14" + {!> ../../../docs_src/dependencies/tutorial006.py!} + ``` + +## Dependencies for a group of *path operations* + +Later, when reading about how to structure bigger applications ([Bigger Applications - Multiple Files](../../tutorial/bigger-applications.md){.internal-link target=_blank}), possibly with multiple files, you will learn how to declare a single `dependencies` parameter for a group of *path operations*. + +## Global Dependencies + +Next we will see how to add dependencies to the whole `FastAPI` application, so that they apply to each *path operation*. From 61fb1f63e74892f10cdd4192beb7b5676cde6748 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:32 +0800 Subject: [PATCH 072/163] New translations dependencies-with-yield.md (Chinese Simplified) --- .../dependencies/dependencies-with-yield.md | 252 ++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 docs/zh/docs/tutorial/dependencies/dependencies-with-yield.md diff --git a/docs/zh/docs/tutorial/dependencies/dependencies-with-yield.md b/docs/zh/docs/tutorial/dependencies/dependencies-with-yield.md new file mode 100644 index 0000000000000..c0883364aff4c --- /dev/null +++ b/docs/zh/docs/tutorial/dependencies/dependencies-with-yield.md @@ -0,0 +1,252 @@ +# Dependencies with yield + +FastAPI supports dependencies that do some extra steps after finishing. + +To do this, use `yield` instead of `return`, and write the extra steps after. + +!!! tip + Make sure to use `yield` one single time. + +!!! note "Technical Details" + Any function that is valid to use with: + + * `@contextlib.contextmanager` or + * `@contextlib.asynccontextmanager` + + would be valid to use as a **FastAPI** dependency. + + In fact, FastAPI uses those two decorators internally. + +## A database dependency with `yield` + +For example, you could use this to create a database session and close it after finishing. + +Only the code prior to and including the `yield` statement is executed before sending a response: + +```Python hl_lines="2-4" +{!../../../docs_src/dependencies/tutorial007.py!} +``` + +The yielded value is what is injected into *path operations* and other dependencies: + +```Python hl_lines="4" +{!../../../docs_src/dependencies/tutorial007.py!} +``` + +The code following the `yield` statement is executed after the response has been delivered: + +```Python hl_lines="5-6" +{!../../../docs_src/dependencies/tutorial007.py!} +``` + +!!! tip + You can use `async` or normal functions. + + **FastAPI** will do the right thing with each, the same as with normal dependencies. + +## A dependency with `yield` and `try` + +If you use a `try` block in a dependency with `yield`, you'll receive any exception that was thrown when using the dependency. + +For example, if some code at some point in the middle, in another dependency or in a *path operation*, made a database transaction "rollback" or create any other error, you will receive the exception in your dependency. + +So, you can look for that specific exception inside the dependency with `except SomeException`. + +In the same way, you can use `finally` to make sure the exit steps are executed, no matter if there was an exception or not. + +```Python hl_lines="3 5" +{!../../../docs_src/dependencies/tutorial007.py!} +``` + +## Sub-dependencies with `yield` + +You can have sub-dependencies and "trees" of sub-dependencies of any size and shape, and any or all of them can use `yield`. + +**FastAPI** will make sure that the "exit code" in each dependency with `yield` is run in the correct order. + +For example, `dependency_c` can have a dependency on `dependency_b`, and `dependency_b` on `dependency_a`: + +=== "Python 3.9+" + + ```Python hl_lines="6 14 22" + {!> ../../../docs_src/dependencies/tutorial008_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="5 13 21" + {!> ../../../docs_src/dependencies/tutorial008_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="4 12 20" + {!> ../../../docs_src/dependencies/tutorial008.py!} + ``` + +And all of them can use `yield`. + +In this case `dependency_c`, to execute its exit code, needs the value from `dependency_b` (here named `dep_b`) to still be available. + +And, in turn, `dependency_b` needs the value from `dependency_a` (here named `dep_a`) to be available for its exit code. + +=== "Python 3.9+" + + ```Python hl_lines="18-19 26-27" + {!> ../../../docs_src/dependencies/tutorial008_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="17-18 25-26" + {!> ../../../docs_src/dependencies/tutorial008_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="16-17 24-25" + {!> ../../../docs_src/dependencies/tutorial008.py!} + ``` + +The same way, you could have dependencies with `yield` and `return` mixed. + +And you could have a single dependency that requires several other dependencies with `yield`, etc. + +You can have any combinations of dependencies that you want. + +**FastAPI** will make sure everything is run in the correct order. + +!!! note "Technical Details" + This works thanks to Python's Context Managers. + + **FastAPI** uses them internally to achieve this. + +## Dependencies with `yield` and `HTTPException` + +You saw that you can use dependencies with `yield` and have `try` blocks that catch exceptions. + +It might be tempting to raise an `HTTPException` or similar in the exit code, after the `yield`. But **it won't work**. + +The exit code in dependencies with `yield` is executed *after* the response is sent, so [Exception Handlers](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} will have already run. There's nothing catching exceptions thrown by your dependencies in the exit code (after the `yield`). + +So, if you raise an `HTTPException` after the `yield`, the default (or any custom) exception handler that catches `HTTPException`s and returns an HTTP 400 response won't be there to catch that exception anymore. + +This is what allows anything set in the dependency (e.g. a DB session) to, for example, be used by background tasks. + +Background tasks are run *after* the response has been sent. So there's no way to raise an `HTTPException` because there's not even a way to change the response that is *already sent*. + +But if a background task creates a DB error, at least you can rollback or cleanly close the session in the dependency with `yield`, and maybe log the error or report it to a remote tracking system. + +If you have some code that you know could raise an exception, do the most normal/"Pythonic" thing and add a `try` block in that section of the code. + +If you have custom exceptions that you would like to handle *before* returning the response and possibly modifying the response, maybe even raising an `HTTPException`, create a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}. + +!!! tip + You can still raise exceptions including `HTTPException` *before* the `yield`. But not after. + +The sequence of execution is more or less like this diagram. Time flows from top to bottom. And each column is one of the parts interacting or executing code. + +```mermaid +sequenceDiagram + +participant client as Client +participant handler as Exception handler +participant dep as Dep with yield +participant operation as Path Operation +participant tasks as Background tasks + + Note over client,tasks: Can raise exception for dependency, handled after response is sent + Note over client,operation: Can raise HTTPException and can change the response + client ->> dep: Start request + Note over dep: Run code up to yield + opt raise + dep -->> handler: Raise HTTPException + handler -->> client: HTTP error response + dep -->> dep: Raise other exception + end + dep ->> operation: Run dependency, e.g. DB session + opt raise + operation -->> dep: Raise HTTPException + dep -->> handler: Auto forward exception + handler -->> client: HTTP error response + operation -->> dep: Raise other exception + dep -->> handler: Auto forward exception + end + operation ->> client: Return response to client + Note over client,operation: Response is already sent, can't change it anymore + opt Tasks + operation -->> tasks: Send background tasks + end + opt Raise other exception + tasks -->> dep: Raise other exception + end + Note over dep: After yield + opt Handle other exception + dep -->> dep: Handle exception, can't change response. E.g. close DB session. + end +``` + +!!! info + Only **one response** will be sent to the client. It might be one of the error responses or it will be the response from the *path operation*. + + After one of those responses is sent, no other response can be sent. + +!!! tip + This diagram shows `HTTPException`, but you could also raise any other exception for which you create a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}. + + If you raise any exception, it will be passed to the dependencies with yield, including `HTTPException`, and then **again** to the exception handlers. If there's no exception handler for that exception, it will then be handled by the default internal `ServerErrorMiddleware`, returning a 500 HTTP status code, to let the client know that there was an error in the server. + +## Context Managers + +### What are "Context Managers" + +"Context Managers" are any of those Python objects that you can use in a `with` statement. + +For example, you can use `with` to read a file: + +```Python +with open("./somefile.txt") as f: + contents = f.read() + print(contents) +``` + +Underneath, the `open("./somefile.txt")` creates an object that is a called a "Context Manager". + +When the `with` block finishes, it makes sure to close the file, even if there were exceptions. + +When you create a dependency with `yield`, **FastAPI** will internally convert it to a context manager, and combine it with some other related tools. + +### Using context managers in dependencies with `yield` + +!!! warning + This is, more or less, an "advanced" idea. + + If you are just starting with **FastAPI** you might want to skip it for now. + +In Python, you can create Context Managers by creating a class with two methods: `__enter__()` and `__exit__()`. + +You can also use them inside of **FastAPI** dependencies with `yield` by using `with` or `async with` statements inside of the dependency function: + +```Python hl_lines="1-9 13" +{!../../../docs_src/dependencies/tutorial010.py!} +``` + +!!! tip + Another way to create a context manager is with: + + * `@contextlib.contextmanager` or + * `@contextlib.asynccontextmanager` + + using them to decorate a function with a single `yield`. + + That's what **FastAPI** uses internally for dependencies with `yield`. + + But you don't have to use the decorators for FastAPI dependencies (and you shouldn't). + + FastAPI will do it for you internally. From e4f0e477532a7048ee06b92105cb2167809a6d72 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:33 +0800 Subject: [PATCH 073/163] New translations global-dependencies.md (Chinese Simplified) --- .../dependencies/global-dependencies.md | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/docs/zh/docs/tutorial/dependencies/global-dependencies.md b/docs/zh/docs/tutorial/dependencies/global-dependencies.md index 3f7afa32cd1b3..0989b31d46205 100644 --- a/docs/zh/docs/tutorial/dependencies/global-dependencies.md +++ b/docs/zh/docs/tutorial/dependencies/global-dependencies.md @@ -1,17 +1,34 @@ -# 全局依赖项 +# Global Dependencies -有时,我们要为整个应用添加依赖项。 +For some types of applications you might want to add dependencies to the whole application. -通过与定义[*路径装饰器依赖项*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} 类似的方式,可以把依赖项添加至整个 `FastAPI` 应用。 +Similar to the way you can [add `dependencies` to the *path operation decorators*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, you can add them to the `FastAPI` application. -这样一来,就可以为所有*路径操作*应用该依赖项: +In that case, they will be applied to all the *path operations* in the application: -```Python hl_lines="15" -{!../../../docs_src/dependencies/tutorial012.py!} -``` +=== "Python 3.9+" -[*路径装饰器依赖项*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} 一章的思路均适用于全局依赖项, 在本例中,这些依赖项可以用于应用中的所有*路径操作*。 + ```Python hl_lines="16" + {!> ../../../docs_src/dependencies/tutorial012_an_py39.py!} + ``` -## 为一组路径操作定义依赖项 +=== "Python 3.6+" -稍后,[大型应用 - 多文件](../../tutorial/bigger-applications.md){.internal-link target=_blank}一章中会介绍如何使用多个文件创建大型应用程序,在这一章中,您将了解到如何为一组*路径操作*声明单个 `dependencies` 参数。 + ```Python hl_lines="16" + {!> ../../../docs_src/dependencies/tutorial012_an.py!} + ``` + +=== "Python 3.6 non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="15" + {!> ../../../docs_src/dependencies/tutorial012.py!} + ``` + +And all the ideas in the section about [adding `dependencies` to the *path operation decorators*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} still apply, but in this case, to all of the *path operations* in the app. + +## Dependencies for groups of *path operations* + +Later, when reading about how to structure bigger applications ([Bigger Applications - Multiple Files](../../tutorial/bigger-applications.md){.internal-link target=_blank}), possibly with multiple files, you will learn how to declare a single `dependencies` parameter for a group of *path operations*. From 52f653637c4b4eed8e5ed9f1cdada03422c00c51 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:34 +0800 Subject: [PATCH 074/163] New translations index.md (Chinese Simplified) --- docs/zh/docs/tutorial/dependencies/index.md | 339 ++++++++++++++------ 1 file changed, 240 insertions(+), 99 deletions(-) diff --git a/docs/zh/docs/tutorial/dependencies/index.md b/docs/zh/docs/tutorial/dependencies/index.md index 7a133061de797..a8679d1d8175a 100644 --- a/docs/zh/docs/tutorial/dependencies/index.md +++ b/docs/zh/docs/tutorial/dependencies/index.md @@ -1,89 +1,193 @@ -# 依赖项 +# Dependencies -FastAPI 提供了简单易用,但功能强大的**依赖注入**系统。 +**FastAPI** has a very powerful but intuitive **Dependency Injection** system. -这个依赖系统设计的简单易用,可以让开发人员轻松地把组件集成至 **FastAPI**。 +It is designed to be very simple to use, and to make it very easy for any developer to integrate other components with **FastAPI**. -## 什么是「依赖注入」 +## What is "Dependency Injection" -编程中的**「依赖注入」**是声明代码(本文中为*路径操作函数* )运行所需的,或要使用的「依赖」的一种方式。 +**"Dependency Injection"** means, in programming, that there is a way for your code (in this case, your *path operation functions*) to declare things that it requires to work and use: "dependencies". -然后,由系统(本文中为 **FastAPI**)负责执行任意需要的逻辑,为代码提供这些依赖(「注入」依赖项)。 +And then, that system (in this case **FastAPI**) will take care of doing whatever is needed to provide your code with those needed dependencies ("inject" the dependencies). -依赖注入常用于以下场景: +This is very useful when you need to: -* 共享业务逻辑(复用相同的代码逻辑) -* 共享数据库连接 -* 实现安全、验证、角色权限 -* 等…… +* Have shared logic (the same code logic again and again). +* Share database connections. +* Enforce security, authentication, role requirements, etc. +* And many other things... -上述场景均可以使用**依赖注入**,将代码重复最小化。 +All these, while minimizing code repetition. -## 第一步 +## First Steps -接下来,我们学习一个非常简单的例子,尽管它过于简单,不是很实用。 +Let's see a very simple example. It will be so simple that it is not very useful, for now. -但通过这个例子,您可以初步了解「依赖注入」的工作机制。 +But this way we can focus on how the **Dependency Injection** system works. -### 创建依赖项 +### Create a dependency, or "dependable" -首先,要关注的是依赖项。 +Let's first focus on the dependency. -依赖项就是一个函数,且可以使用与*路径操作函数*相同的参数: +It is just a function that can take all the same parameters that a *path operation function* can take: -```Python hl_lines="8-11" -{!../../../docs_src/dependencies/tutorial001.py!} -``` +=== "Python 3.10+" -大功告成。 + ```Python hl_lines="8-9" + {!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} + ``` -只用了**2 行**代码。 +=== "Python 3.9+" -依赖项函数的形式和结构与*路径操作函数*一样。 + ```Python hl_lines="8-11" + {!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} + ``` -因此,可以把依赖项当作没有「装饰器」(即,没有 `@app.get("/some-path")` )的路径操作函数。 +=== "Python 3.6+" -依赖项可以返回各种内容。 + ```Python hl_lines="9-12" + {!> ../../../docs_src/dependencies/tutorial001_an.py!} + ``` -本例中的依赖项预期接收如下参数: +=== "Python 3.10+ non-Annotated" -* 类型为 `str` 的可选查询参数 `q` -* 类型为 `int` 的可选查询参数 `skip`,默认值是 `0` -* 类型为 `int` 的可选查询参数 `limit`,默认值是 `100` + !!! tip + Prefer to use the `Annotated` version if possible. -然后,依赖项函数返回包含这些值的 `dict`。 + ```Python hl_lines="6-7" + {!> ../../../docs_src/dependencies/tutorial001_py310.py!} + ``` -### 导入 `Depends` +=== "Python 3.6+ non-Annotated" -```Python hl_lines="3" -{!../../../docs_src/dependencies/tutorial001.py!} -``` + !!! tip + Prefer to use the `Annotated` version if possible. -### 声明依赖项 + ```Python hl_lines="8-11" + {!> ../../../docs_src/dependencies/tutorial001.py!} + ``` -与在*路径操作函数*参数中使用 `Body`、`Query` 的方式相同,声明依赖项需要使用 `Depends` 和一个新的参数: +That's it. -```Python hl_lines="15 20" -{!../../../docs_src/dependencies/tutorial001.py!} -``` +**2 lines**. + +And it has the same shape and structure that all your *path operation functions* have. + +You can think of it as a *path operation function* without the "decorator" (without the `@app.get("/some-path")`). + +And it can return anything you want. + +In this case, this dependency expects: + +* An optional query parameter `q` that is a `str`. +* An optional query parameter `skip` that is an `int`, and by default is `0`. +* An optional query parameter `limit` that is an `int`, and by default is `100`. + +And then it just returns a `dict` containing those values. + +!!! info + FastAPI added support for `Annotated` (and started recommending it) in version 0.95.0. + + If you have an older version, you would get errors when trying to use `Annotated`. + + Make sure you [Upgrade the FastAPI version](../../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} to at least 0.95.1 before using `Annotated`. + +### Import `Depends` + +=== "Python 3.10+" + + ```Python hl_lines="3" + {!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="3" + {!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="3" + {!> ../../../docs_src/dependencies/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="1" + {!> ../../../docs_src/dependencies/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="3" + {!> ../../../docs_src/dependencies/tutorial001.py!} + ``` + +### Declare the dependency, in the "dependant" + +The same way you use `Body`, `Query`, etc. with your *path operation function* parameters, use `Depends` with a new parameter: -虽然,在路径操作函数的参数中使用 `Depends` 的方式与 `Body`、`Query` 相同,但 `Depends` 的工作方式略有不同。 +=== "Python 3.10+" -这里只能传给 Depends 一个参数。 + ```Python hl_lines="13 18" + {!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} + ``` -且该参数必须是可调用对象,比如函数。 +=== "Python 3.9+" -该函数接收的参数和*路径操作函数*的参数一样。 + ```Python hl_lines="15 20" + {!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} + ``` -!!! tip "提示" +=== "Python 3.6+" - 下一章介绍,除了函数还有哪些「对象」可以用作依赖项。 + ```Python hl_lines="16 21" + {!> ../../../docs_src/dependencies/tutorial001_an.py!} + ``` -接收到新的请求时,**FastAPI** 执行如下操作: +=== "Python 3.10+ non-Annotated" -* 用正确的参数调用依赖项函数(「可依赖项」) -* 获取函数返回的结果 -* 把函数返回的结果赋值给*路径操作函数*的参数 + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="11 16" + {!> ../../../docs_src/dependencies/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="15 20" + {!> ../../../docs_src/dependencies/tutorial001.py!} + ``` + +Although you use `Depends` in the parameters of your function the same way you use `Body`, `Query`, etc, `Depends` works a bit differently. + +You only give `Depends` a single parameter. + +This parameter must be something like a function. + +You **don't call it** directly (don't add the parenthesis at the end), you just pass it as a parameter to `Depends()`. + +And that function takes parameters in the same way that *path operation functions* do. + +!!! tip + You'll see what other "things", apart from functions, can be used as dependencies in the next chapter. + +Whenever a new request arrives, **FastAPI** will take care of: + +* Calling your dependency ("dependable") function with the correct parameters. +* Get the result from your function. +* Assign that result to the parameter in your *path operation function*. ```mermaid graph TB @@ -96,91 +200,128 @@ common_parameters --> read_items common_parameters --> read_users ``` -这样,只编写一次代码,**FastAPI** 就可以为多个*路径操作*共享这段代码 。 +This way you write shared code once and **FastAPI** takes care of calling it for your *path operations*. + +!!! check + Notice that you don't have to create a special class and pass it somewhere to **FastAPI** to "register" it or anything similar. + + You just pass it to `Depends` and **FastAPI** knows how to do the rest. + +## Share `Annotated` dependencies + +In the examples above, you see that there's a tiny bit of **code duplication**. + +When you need to use the `common_parameters()` dependency, you have to write the whole parameter with the type annotation and `Depends()`: + +```Python +commons: Annotated[dict, Depends(common_parameters)] +``` + +But because we are using `Annotated`, we can store that `Annotated` value in a variable and use it in multiple places: + +=== "Python 3.10+" + + ```Python hl_lines="12 16 21" + {!> ../../../docs_src/dependencies/tutorial001_02_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="14 18 23" + {!> ../../../docs_src/dependencies/tutorial001_02_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="15 19 24" + {!> ../../../docs_src/dependencies/tutorial001_02_an.py!} + ``` -!!! check "检查" +!!! tip + This is just standard Python, it's called a "type alias", it's actually not specific to **FastAPI**. - 注意,无需创建专门的类,并将之传递给 **FastAPI** 以进行「注册」或执行类似的操作。 + But because **FastAPI** is based on the Python standards, including `Annotated`, you can use this trick in your code. 😎 - 只要把它传递给 `Depends`,**FastAPI** 就知道该如何执行后续操作。 +The dependencies will keep working as expected, and the **best part** is that the **type information will be preserved**, which means that your editor will be able to keep providing you with **autocompletion**, **inline errors**, etc. The same for other tools like `mypy`. -## 要不要使用 `async`? +This will be especially useful when you use it in a **large code base** where you use **the same dependencies** over and over again in ***many ***path operations******. -**FastAPI** 调用依赖项的方式与*路径操作函数*一样,因此,定义依赖项函数,也要应用与路径操作函数相同的规则。 +## To `async` or not to `async` -即,既可以使用异步的 `async def`,也可以使用普通的 `def` 定义依赖项。 +As dependencies will also be called by **FastAPI** (the same as your *path operation functions*), the same rules apply while defining your functions. -在普通的 `def` *路径操作函数*中,可以声明异步的 `async def` 依赖项;也可以在异步的 `async def` *路径操作函数*中声明普通的 `def` 依赖项。 +You can use `async def` or normal `def`. -上述这些操作都是可行的,**FastAPI** 知道该怎么处理。 +And you can declare dependencies with `async def` inside of normal `def` *path operation functions*, or `def` dependencies inside of `async def` *path operation functions*, etc. -!!! note "笔记" +It doesn't matter. **FastAPI** will know what to do. - 如里不了解异步,请参阅[异步:*“着急了?”*](../../async.md){.internal-link target=_blank} 一章中 `async` 和 `await` 的内容。 +!!! note + If you don't know, check the [Async: *"In a hurry?"*](../../async.md){.internal-link target=_blank} section about `async` and `await` in the docs. -## 与 OpenAPI 集成 +## Integrated with OpenAPI -依赖项及子依赖项的所有请求声明、验证和需求都可以集成至同一个 OpenAPI 概图。 +All the request declarations, validations and requirements of your dependencies (and sub-dependencies) will be integrated in the same OpenAPI schema. -所以,交互文档里也会显示依赖项的所有信息: +So, the interactive docs will have all the information from these dependencies too: - + -## 简单用法 +## Simple usage -观察一下就会发现,只要*路径* 和*操作*匹配,就可以使用声明的路径操作函数。然后,**FastAPI** 会用正确的参数调用函数,并提取请求中的数据。 +If you look at it, *path operation functions* are declared to be used whenever a *path* and *operation* matches, and then **FastAPI** takes care of calling the function with the correct parameters, extracting the data from the request. -实际上,所有(或大多数)网络框架的工作方式都是这样的。 +Actually, all (or most) of the web frameworks work in this same way. -开发人员永远都不需要直接调用这些函数,这些函数是由框架(在此为 **FastAPI** )调用的。 +You never call those functions directly. They are called by your framework (in this case, **FastAPI**). -通过依赖注入系统,只要告诉 **FastAPI** *路径操作函数* 还要「依赖」其他在*路径操作函数*之前执行的内容,**FastAPI** 就会执行函数代码,并「注入」函数返回的结果。 +With the Dependency Injection system, you can also tell **FastAPI** that your *path operation function* also "depends" on something else that should be executed before your *path operation function*, and **FastAPI** will take care of executing it and "injecting" the results. -其他与「依赖注入」概念相同的术语为: +Other common terms for this same idea of "dependency injection" are: -* 资源(Resource) -* 提供方(Provider) -* 服务(Service) -* 可注入(Injectable) -* 组件(Component) +* resources +* providers +* services +* injectables +* components -## **FastAPI** 插件 +## **FastAPI** plug-ins -**依赖注入**系统支持构建集成和「插件」。但实际上,FastAPI 根本**不需要创建「插件」**,因为使用依赖项可以声明不限数量的、可用于*路径操作函数*的集成与交互。 +Integrations and "plug-in"s can be built using the **Dependency Injection** system. But in fact, there is actually **no need to create "plug-ins"**, as by using dependencies it's possible to declare an infinite number of integrations and interactions that become available to your *path operation functions*. -创建依赖项非常简单、直观,并且还支持导入 Python 包。毫不夸张地说,只要几行代码就可以把需要的 Python 包与 API 函数集成在一起。 +And dependencies can be created in a very simple and intuitive way that allow you to just import the Python packages you need, and integrate them with your API functions in a couple of lines of code, *literally*. -下一章将详细介绍在关系型数据库、NoSQL 数据库、安全等方面使用依赖项的例子。 +You will see examples of this in the next chapters, about relational and NoSQL databases, security, etc. -## **FastAPI** 兼容性 +## **FastAPI** compatibility -依赖注入系统如此简洁的特性,让 **FastAPI** 可以与下列系统兼容: +The simplicity of the dependency injection system makes **FastAPI** compatible with: -* 关系型数据库 -* NoSQL 数据库 -* 外部支持库 -* 外部 API -* 认证和鉴权系统 -* API 使用监控系统 -* 响应数据注入系统 -* 等等…… +* all the relational databases +* NoSQL databases +* external packages +* external APIs +* authentication and authorization systems +* API usage monitoring systems +* response data injection systems +* etc. -## 简单而强大 +## Simple and Powerful -虽然,**层级式依赖注入系统**的定义与使用十分简单,但它却非常强大。 +Although the hierarchical dependency injection system is very simple to define and use, it's still very powerful. -比如,可以定义依赖其他依赖项的依赖项。 +You can define dependencies that in turn can define dependencies themselves. -最后,依赖项层级树构建后,**依赖注入系统**会处理所有依赖项及其子依赖项,并为每一步操作提供(注入)结果。 +In the end, a hierarchical tree of dependencies is built, and the **Dependency Injection** system takes care of solving all these dependencies for you (and their sub-dependencies) and providing (injecting) the results at each step. -比如,下面有 4 个 API 路径操作(*端点*): +For example, let's say you have 4 API endpoints (*path operations*): * `/items/public/` * `/items/private/` * `/users/{user_id}/activate` * `/items/pro/` -开发人员可以使用依赖项及其子依赖项为这些路径操作添加不同的权限: +then you could add different permission requirements for each of them just with dependencies and sub-dependencies: ```mermaid graph TB @@ -205,8 +346,8 @@ admin_user --> activate_user paying_user --> pro_items ``` -## 与 **OpenAPI** 集成 +## Integrated with **OpenAPI** -在声明需求时,所有这些依赖项还会把参数、验证等功能添加至路径操作。 +All these dependencies, while declaring their requirements, also add parameters, validations, etc. to your *path operations*. -**FastAPI** 负责把上述内容全部添加到 OpenAPI 概图,并显示在交互文档中。 +**FastAPI** will take care of adding it all to the OpenAPI schema, so that it is shown in the interactive documentation systems. From be1ff5b12fe35d55795bc1a02fe15aa48d89a621 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:35 +0800 Subject: [PATCH 075/163] New translations sub-dependencies.md (Chinese Simplified) --- .../tutorial/dependencies/sub-dependencies.md | 196 ++++++++++++++---- 1 file changed, 151 insertions(+), 45 deletions(-) diff --git a/docs/zh/docs/tutorial/dependencies/sub-dependencies.md b/docs/zh/docs/tutorial/dependencies/sub-dependencies.md index 58377bbfecd8d..b89844a1ffd66 100644 --- a/docs/zh/docs/tutorial/dependencies/sub-dependencies.md +++ b/docs/zh/docs/tutorial/dependencies/sub-dependencies.md @@ -1,51 +1,146 @@ -# 子依赖项 +# Sub-dependencies -FastAPI 支持创建含**子依赖项**的依赖项。 +You can create dependencies that have **sub-dependencies**. -并且,可以按需声明任意**深度**的子依赖项嵌套层级。 +They can be as **deep** as you need them to be. -**FastAPI** 负责处理解析不同深度的子依赖项。 +**FastAPI** will take care of solving them. -### 第一层依赖项 +## First dependency "dependable" -下列代码创建了第一层依赖项: +You could create a first dependency ("dependable") like: -```Python hl_lines="8-9" -{!../../../docs_src/dependencies/tutorial005.py!} -``` +=== "Python 3.10+" -这段代码声明了类型为 `str` 的可选查询参数 `q`,然后返回这个查询参数。 + ```Python hl_lines="8-9" + {!> ../../../docs_src/dependencies/tutorial005_an_py310.py!} + ``` -这个函数很简单(不过也没什么用),但却有助于让我们专注于了解子依赖项的工作方式。 +=== "Python 3.9+" -### 第二层依赖项 + ```Python hl_lines="8-9" + {!> ../../../docs_src/dependencies/tutorial005_an_py39.py!} + ``` -接下来,创建另一个依赖项函数,并同时用该依赖项自身再声明一个依赖项(所以这也是一个「依赖项」): +=== "Python 3.6+" -```Python hl_lines="13" -{!../../../docs_src/dependencies/tutorial005.py!} -``` + ```Python hl_lines="9-10" + {!> ../../../docs_src/dependencies/tutorial005_an.py!} + ``` -这里重点说明一下声明的参数: +=== "Python 3.10 non-Annotated" -* 尽管该函数自身是依赖项,但还声明了另一个依赖项(它「依赖」于其他对象) - * 该函数依赖 `query_extractor`, 并把 `query_extractor` 的返回值赋给参数 `q` -* 同时,该函数还声明了类型是 `str` 的可选 cookie(`last_query`) - * 用户未提供查询参数 `q` 时,则使用上次使用后保存在 cookie 中的查询 + !!! tip + Prefer to use the `Annotated` version if possible. -### 使用依赖项 + ```Python hl_lines="6-7" + {!> ../../../docs_src/dependencies/tutorial005_py310.py!} + ``` -接下来,就可以使用依赖项: +=== "Python 3.6 non-Annotated" -```Python hl_lines="22" -{!../../../docs_src/dependencies/tutorial005.py!} -``` + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="8-9" + {!> ../../../docs_src/dependencies/tutorial005.py!} + ``` + +It declares an optional query parameter `q` as a `str`, and then it just returns it. + +This is quite simple (not very useful), but will help us focus on how the sub-dependencies work. + +## Second dependency, "dependable" and "dependant" + +Then you can create another dependency function (a "dependable") that at the same time declares a dependency of its own (so it is a "dependant" too): + +=== "Python 3.10+" + + ```Python hl_lines="13" + {!> ../../../docs_src/dependencies/tutorial005_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="13" + {!> ../../../docs_src/dependencies/tutorial005_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="14" + {!> ../../../docs_src/dependencies/tutorial005_an.py!} + ``` + +=== "Python 3.10 non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="11" + {!> ../../../docs_src/dependencies/tutorial005_py310.py!} + ``` + +=== "Python 3.6 non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. -!!! info "信息" + ```Python hl_lines="13" + {!> ../../../docs_src/dependencies/tutorial005.py!} + ``` - 注意,这里在*路径操作函数*中只声明了一个依赖项,即 `query_or_cookie_extractor` 。 +Let's focus on the parameters declared: - 但 **FastAPI** 必须先处理 `query_extractor`,以便在调用 `query_or_cookie_extractor` 时使用 `query_extractor` 返回的结果。 +* Even though this function is a dependency ("dependable") itself, it also declares another dependency (it "depends" on something else). + * It depends on the `query_extractor`, and assigns the value returned by it to the parameter `q`. +* It also declares an optional `last_query` cookie, as a `str`. + * If the user didn't provide any query `q`, we use the last query used, which we saved to a cookie before. + +## Use the dependency + +Then we can use the dependency with: + +=== "Python 3.10+" + + ```Python hl_lines="23" + {!> ../../../docs_src/dependencies/tutorial005_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="23" + {!> ../../../docs_src/dependencies/tutorial005_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="24" + {!> ../../../docs_src/dependencies/tutorial005_an.py!} + ``` + +=== "Python 3.10 non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="19" + {!> ../../../docs_src/dependencies/tutorial005_py310.py!} + ``` + +=== "Python 3.6 non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="22" + {!> ../../../docs_src/dependencies/tutorial005.py!} + ``` + +!!! info + Notice that we are only declaring one dependency in the *path operation function*, the `query_or_cookie_extractor`. + + But **FastAPI** will know that it has to solve `query_extractor` first, to pass the results of that to `query_or_cookie_extractor` while calling it. ```mermaid graph TB @@ -58,31 +153,42 @@ read_query["/items/"] query_extractor --> query_or_cookie_extractor --> read_query ``` -## 多次使用同一个依赖项 +## Using the same dependency multiple times -如果在同一个*路径操作* 多次声明了同一个依赖项,例如,多个依赖项共用一个子依赖项,**FastAPI** 在处理同一请求时,只调用一次该子依赖项。 +If one of your dependencies is declared multiple times for the same *path operation*, for example, multiple dependencies have a common sub-dependency, **FastAPI** will know to call that sub-dependency only once per request. -FastAPI 不会为同一个请求多次调用同一个依赖项,而是把依赖项的返回值进行「缓存」,并把它传递给同一请求中所有需要使用该返回值的「依赖项」。 +And it will save the returned value in a "cache" and pass it to all the "dependants" that need it in that specific request, instead of calling the dependency multiple times for the same request. -在高级使用场景中,如果不想使用「缓存」值,而是为需要在同一请求的每一步操作(多次)中都实际调用依赖项,可以把 `Depends` 的参数 `use_cache` 的值设置为 `False` : +In an advanced scenario where you know you need the dependency to be called at every step (possibly multiple times) in the same request instead of using the "cached" value, you can set the parameter `use_cache=False` when using `Depends`: -```Python hl_lines="1" -async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)): - return {"fresh_value": fresh_value} -``` +=== "Python 3.6+" + + ```Python hl_lines="1" + async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]): + return {"fresh_value": fresh_value} + ``` + +=== "Python 3.6+ non-Annotated" -## 小结 + !!! tip + Prefer to use the `Annotated` version if possible. -千万别被本章里这些花里胡哨的词藻吓倒了,其实**依赖注入**系统非常简单。 + ```Python hl_lines="1" + async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)): + return {"fresh_value": fresh_value} + ``` -依赖注入无非是与*路径操作函数*一样的函数罢了。 +## Recap -但它依然非常强大,能够声明任意嵌套深度的「图」或树状的依赖结构。 +Apart from all the fancy words used here, the **Dependency Injection** system is quite simple. -!!! tip "提示" +Just functions that look the same as the *path operation functions*. - 这些简单的例子现在看上去虽然没有什么实用价值, +But still, it is very powerful, and allows you to declare arbitrarily deeply nested dependency "graphs" (trees). - 但在**安全**一章中,您会了解到这些例子的用途, +!!! tip + All this might not seem as useful with these simple examples. - 以及这些例子所能节省的代码量。 + But you will see how useful it is in the chapters about **security**. + + And you will also see the amounts of code it will save you. From f5f3098503738daed25d92745689ea9cf6047b22 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:36 +0800 Subject: [PATCH 076/163] New translations encoder.md (Chinese Simplified) --- docs/zh/docs/tutorial/encoder.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/zh/docs/tutorial/encoder.md b/docs/zh/docs/tutorial/encoder.md index 76ed846ce35e4..735aa2209ffed 100644 --- a/docs/zh/docs/tutorial/encoder.md +++ b/docs/zh/docs/tutorial/encoder.md @@ -1,24 +1,24 @@ -# JSON 兼容编码器 +# JSON Compatible Encoder -在某些情况下,您可能需要将数据类型(如Pydantic模型)转换为与JSON兼容的数据类型(如`dict`、`list`等)。 +There are some cases where you might need to convert a data type (like a Pydantic model) to something compatible with JSON (like a `dict`, `list`, etc). -比如,如果您需要将其存储在数据库中。 +For example, if you need to store it in a database. -对于这种要求, **FastAPI**提供了`jsonable_encoder()`函数。 +For that, **FastAPI** provides a `jsonable_encoder()` function. -## 使用`jsonable_encoder` +## Using the `jsonable_encoder` -让我们假设你有一个数据库名为`fake_db`,它只能接收与JSON兼容的数据。 +Let's imagine that you have a database `fake_db` that only receives JSON compatible data. -例如,它不接收`datetime`这类的对象,因为这些对象与JSON不兼容。 +For example, it doesn't receive `datetime` objects, as those are not compatible with JSON. -因此,`datetime`对象必须将转换为包含ISO格式化的`str`类型对象。 +So, a `datetime` object would have to be converted to a `str` containing the data in ISO format. -同样,这个数据库也不会接收Pydantic模型(带有属性的对象),而只接收`dict`。 +The same way, this database wouldn't receive a Pydantic model (an object with attributes), only a `dict`. -对此你可以使用`jsonable_encoder`。 +You can use `jsonable_encoder` for that. -它接收一个对象,比如Pydantic模型,并会返回一个JSON兼容的版本: +It receives an object, like a Pydantic model, and returns a JSON compatible version: === "Python 3.10+" @@ -32,11 +32,11 @@ {!> ../../../docs_src/encoder/tutorial001.py!} ``` -在这个例子中,它将Pydantic模型转换为`dict`,并将`datetime`转换为`str`。 +In this example, it would convert the Pydantic model to a `dict`, and the `datetime` to a `str`. -调用它的结果后就可以使用Python标准编码中的`json.dumps()`。 +The result of calling it is something that can be encoded with the Python standard `json.dumps()`. -这个操作不会返回一个包含JSON格式(作为字符串)数据的庞大的`str`。它将返回一个Python标准数据结构(例如`dict`),其值和子值都与JSON兼容。 +It doesn't return a large `str` containing the data in JSON format (as a string). It returns a Python standard data structure (e.g. a `dict`) with values and sub-values that are all compatible with JSON. !!! note - `jsonable_encoder`实际上是FastAPI内部用来转换数据的。但是它在许多其他场景中也很有用。 + `jsonable_encoder` is actually used by **FastAPI** internally to convert data. But it is useful in many other scenarios. From 5e77094a1c861a9fdf841b0bdaa2c8443c139abe Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:37 +0800 Subject: [PATCH 077/163] New translations extra-data-types.md (Chinese Simplified) --- docs/zh/docs/tutorial/extra-data-types.md | 144 ++++++++++++++++------ 1 file changed, 104 insertions(+), 40 deletions(-) diff --git a/docs/zh/docs/tutorial/extra-data-types.md b/docs/zh/docs/tutorial/extra-data-types.md index ac3e076545f48..7d6ffbc780cb0 100644 --- a/docs/zh/docs/tutorial/extra-data-types.md +++ b/docs/zh/docs/tutorial/extra-data-types.md @@ -1,66 +1,130 @@ -# 额外数据类型 +# Extra Data Types -到目前为止,您一直在使用常见的数据类型,如: +Up to now, you have been using common data types, like: * `int` * `float` * `str` * `bool` -但是您也可以使用更复杂的数据类型。 +But you can also use more complex data types. -您仍然会拥有现在已经看到的相同的特性: +And you will still have the same features as seen up to now: -* 很棒的编辑器支持。 -* 传入请求的数据转换。 -* 响应数据转换。 -* 数据验证。 -* 自动补全和文档。 +* Great editor support. +* Data conversion from incoming requests. +* Data conversion for response data. +* Data validation. +* Automatic annotation and documentation. -## 其他数据类型 +## Other data types -下面是一些你可以使用的其他数据类型: +Here are some of the additional data types you can use: * `UUID`: - * 一种标准的 "通用唯一标识符" ,在许多数据库和系统中用作ID。 - * 在请求和响应中将以 `str` 表示。 + * A standard "Universally Unique Identifier", common as an ID in many databases and systems. + * In requests and responses will be represented as a `str`. * `datetime.datetime`: - * 一个 Python `datetime.datetime`. - * 在请求和响应中将表示为 ISO 8601 格式的 `str` ,比如: `2008-09-15T15:53:00+05:00`. + * A Python `datetime.datetime`. + * In requests and responses will be represented as a `str` in ISO 8601 format, like: `2008-09-15T15:53:00+05:00`. * `datetime.date`: * Python `datetime.date`. - * 在请求和响应中将表示为 ISO 8601 格式的 `str` ,比如: `2008-09-15`. + * In requests and responses will be represented as a `str` in ISO 8601 format, like: `2008-09-15`. * `datetime.time`: - * 一个 Python `datetime.time`. - * 在请求和响应中将表示为 ISO 8601 格式的 `str` ,比如: `14:23:55.003`. + * A Python `datetime.time`. + * In requests and responses will be represented as a `str` in ISO 8601 format, like: `14:23:55.003`. * `datetime.timedelta`: - * 一个 Python `datetime.timedelta`. - * 在请求和响应中将表示为 `float` 代表总秒数。 - * Pydantic 也允许将其表示为 "ISO 8601 时间差异编码", 查看文档了解更多信息。 + * A Python `datetime.timedelta`. + * In requests and responses will be represented as a `float` of total seconds. + * Pydantic also allows representing it as a "ISO 8601 time diff encoding", see the docs for more info. * `frozenset`: - * 在请求和响应中,作为 `set` 对待: - * 在请求中,列表将被读取,消除重复,并将其转换为一个 `set`。 - * 在响应中 `set` 将被转换为 `list` 。 - * 产生的模式将指定那些 `set` 的值是唯一的 (使用 JSON 模式的 `uniqueItems`)。 + * In requests and responses, treated the same as a `set`: + * In requests, a list will be read, eliminating duplicates and converting it to a `set`. + * In responses, the `set` will be converted to a `list`. + * The generated schema will specify that the `set` values are unique (using JSON Schema's `uniqueItems`). * `bytes`: - * 标准的 Python `bytes`。 - * 在请求和相应中被当作 `str` 处理。 - * 生成的模式将指定这个 `str` 是 `binary` "格式"。 + * Standard Python `bytes`. + * In requests and responses will be treated as `str`. + * The generated schema will specify that it's a `str` with `binary` "format". * `Decimal`: - * 标准的 Python `Decimal`。 - * 在请求和相应中被当做 `float` 一样处理。 -* 您可以在这里检查所有有效的pydantic数据类型: Pydantic data types. + * Standard Python `Decimal`. + * In requests and responses, handled the same as a `float`. +* You can check all the valid pydantic data types here: Pydantic data types. -## 例子 +## Example -下面是一个*路径操作*的示例,其中的参数使用了上面的一些类型。 +Here's an example *path operation* with parameters using some of the above types. -```Python hl_lines="1 3 12-16" -{!../../../docs_src/extra_data_types/tutorial001.py!} -``` +=== "Python 3.10+" -注意,函数内的参数有原生的数据类型,你可以,例如,执行正常的日期操作,如: + ```Python hl_lines="1 3 12-16" + {!> ../../../docs_src/extra_data_types/tutorial001_an_py310.py!} + ``` -```Python hl_lines="18-19" -{!../../../docs_src/extra_data_types/tutorial001.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="1 3 12-16" + {!> ../../../docs_src/extra_data_types/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="1 3 13-17" + {!> ../../../docs_src/extra_data_types/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="1 2 11-15" + {!> ../../../docs_src/extra_data_types/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="1 2 12-16" + {!> ../../../docs_src/extra_data_types/tutorial001.py!} + ``` + +Note that the parameters inside the function have their natural data type, and you can, for example, perform normal date manipulations, like: + +=== "Python 3.10+" + + ```Python hl_lines="18-19" + {!> ../../../docs_src/extra_data_types/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="18-19" + {!> ../../../docs_src/extra_data_types/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="19-20" + {!> ../../../docs_src/extra_data_types/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="17-18" + {!> ../../../docs_src/extra_data_types/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="18-19" + {!> ../../../docs_src/extra_data_types/tutorial001.py!} + ``` From b0a0a0c49009fb67ebd08ae5d04dec48991335e9 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:38 +0800 Subject: [PATCH 078/163] New translations extra-models.md (Chinese Simplified) --- docs/zh/docs/tutorial/extra-models.md | 193 ++++++++++++++++---------- 1 file changed, 123 insertions(+), 70 deletions(-) diff --git a/docs/zh/docs/tutorial/extra-models.md b/docs/zh/docs/tutorial/extra-models.md index 1fbe77be8de70..e91e879e41672 100644 --- a/docs/zh/docs/tutorial/extra-models.md +++ b/docs/zh/docs/tutorial/extra-models.md @@ -1,55 +1,63 @@ -# 额外的模型 +# Extra Models -我们从前面的示例继续,拥有多个相关的模型是很常见的。 +Continuing with the previous example, it will be common to have more than one related model. -对用户模型来说尤其如此,因为: +This is especially the case for user models, because: -* **输入模型**需要拥有密码属性。 -* **输出模型**不应该包含密码。 -* **数据库模型**很可能需要保存密码的哈希值。 +* The **input model** needs to be able to have a password. +* The **output model** should not have a password. +* The **database model** would probably need to have a hashed password. !!! danger - 永远不要存储用户的明文密码。始终存储一个可以用于验证的「安全哈希值」。 + Never store user's plaintext passwords. Always store a "secure hash" that you can then verify. - 如果你尚未了解该知识,你可以在[安全章节](security/simple-oauth2.md#password-hashing){.internal-link target=_blank}中学习何为「密码哈希值」。 + If you don't know, you will learn what a "password hash" is in the [security chapters](security/simple-oauth2.md#password-hashing){.internal-link target=_blank}. -## 多个模型 +## Multiple models -下面是应该如何根据它们的密码字段以及使用位置去定义模型的大概思路: +Here's a general idea of how the models could look like with their password fields and the places where they are used: -```Python hl_lines="9 11 16 22 24 29-30 33-35 40-41" -{!../../../docs_src/extra_models/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="7 9 14 20 22 27-28 31-33 38-39" + {!> ../../../docs_src/extra_models/tutorial001_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9 11 16 22 24 29-30 33-35 40-41" + {!> ../../../docs_src/extra_models/tutorial001.py!} + ``` -### 关于 `**user_in.dict()` +### About `**user_in.dict()` -#### Pydantic 的 `.dict()` +#### Pydantic's `.dict()` -`user_in` 是一个 `UserIn` 类的 Pydantic 模型. +`user_in` is a Pydantic model of class `UserIn`. -Pydantic 模型具有 `.dict()` 方法,该方法返回一个拥有模型数据的 `dict`。 +Pydantic models have a `.dict()` method that returns a `dict` with the model's data. -因此,如果我们像下面这样创建一个 Pydantic 对象 `user_in`: +So, if we create a Pydantic object `user_in` like: ```Python user_in = UserIn(username="john", password="secret", email="john.doe@example.com") ``` -然后我们调用: +and then we call: ```Python user_dict = user_in.dict() ``` -现在我们有了一个数据位于变量 `user_dict` 中的 `dict`(它是一个 `dict` 而不是 Pydantic 模型对象)。 +we now have a `dict` with the data in the variable `user_dict` (it's a `dict` instead of a Pydantic model object). -如果我们调用: +And if we call: ```Python print(user_dict) ``` -我们将获得一个这样的 Python `dict`: +we would get a Python `dict` with: ```Python { @@ -60,17 +68,17 @@ print(user_dict) } ``` -#### 解包 `dict` +#### Unwrapping a `dict` -如果我们将 `user_dict` 这样的 `dict` 以 `**user_dict` 形式传递给一个函数(或类),Python将对其进行「解包」。它会将 `user_dict` 的键和值作为关键字参数直接传递。 +If we take a `dict` like `user_dict` and pass it to a function (or class) with `**user_dict`, Python will "unwrap" it. It will pass the keys and values of the `user_dict` directly as key-value arguments. -因此,从上面的 `user_dict` 继续,编写: +So, continuing with the `user_dict` from above, writing: ```Python UserInDB(**user_dict) ``` -会产生类似于以下的结果: +Would result in something equivalent to: ```Python UserInDB( @@ -81,7 +89,7 @@ UserInDB( ) ``` -或者更确切地,直接使用 `user_dict` 来表示将来可能包含的任何内容: +Or more exactly, using `user_dict` directly, with whatever contents it might have in the future: ```Python UserInDB( @@ -92,34 +100,34 @@ UserInDB( ) ``` -#### 来自于其他模型内容的 Pydantic 模型 +#### A Pydantic model from the contents of another -如上例所示,我们从 `user_in.dict()` 中获得了 `user_dict`,此代码: +As in the example above we got `user_dict` from `user_in.dict()`, this code: ```Python user_dict = user_in.dict() UserInDB(**user_dict) ``` -等同于: +would be equivalent to: ```Python UserInDB(**user_in.dict()) ``` -...因为 `user_in.dict()` 是一个 `dict`,然后我们通过以`**`开头传递给 `UserInDB` 来使 Python「解包」它。 +...because `user_in.dict()` is a `dict`, and then we make Python "unwrap" it by passing it to `UserInDB` prepended with `**`. -这样,我们获得了一个来自于其他 Pydantic 模型中的数据的 Pydantic 模型。 +So, we get a Pydantic model from the data in another Pydantic model. -#### 解包 `dict` 和额外关键字 +#### Unwrapping a `dict` and extra keywords -然后添加额外的关键字参数 `hashed_password=hashed_password`,例如: +And then adding the extra keyword argument `hashed_password=hashed_password`, like in: ```Python UserInDB(**user_in.dict(), hashed_password=hashed_password) ``` -...最终的结果如下: +...ends up being like: ```Python UserInDB( @@ -132,68 +140,113 @@ UserInDB( ``` !!! warning - 辅助性的额外函数只是为了演示可能的数据流,但它们显然不能提供任何真正的安全性。 + The supporting additional functions are just to demo a possible flow of the data, but they of course are not providing any real security. -## 减少重复 +## Reduce duplication -减少代码重复是 **FastAPI** 的核心思想之一。 +Reducing code duplication is one of the core ideas in **FastAPI**. -因为代码重复会增加出现 bug、安全性问题、代码失步问题(当你在一个位置更新了代码但没有在其他位置更新)等的可能性。 +As code duplication increments the chances of bugs, security issues, code desynchronization issues (when you update in one place but not in the others), etc. -上面的这些模型都共享了大量数据,并拥有重复的属性名称和类型。 +And these models are all sharing a lot of the data and duplicating attribute names and types. -我们可以做得更好。 +We could do better. -我们可以声明一个 `UserBase` 模型作为其他模型的基类。然后我们可以创建继承该模型属性(类型声明,校验等)的子类。 +We can declare a `UserBase` model that serves as a base for our other models. And then we can make subclasses of that model that inherit its attributes (type declarations, validation, etc). -所有的数据转换、校验、文档生成等仍将正常运行。 +All the data conversion, validation, documentation, etc. will still work as normally. -这样,我们可以仅声明模型之间的差异部分(具有明文的 `password`、具有 `hashed_password` 以及不包括密码)。 +That way, we can declare just the differences between the models (with plaintext `password`, with `hashed_password` and without password): -```Python hl_lines="9 15-16 19-20 23-24" -{!../../../docs_src/extra_models/tutorial002.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="7 13-14 17-18 21-22" + {!> ../../../docs_src/extra_models/tutorial002_py310.py!} + ``` + +=== "Python 3.6+" -## `Union` 或者 `anyOf` + ```Python hl_lines="9 15-16 19-20 23-24" + {!> ../../../docs_src/extra_models/tutorial002.py!} + ``` -你可以将一个响应声明为两种类型的 `Union`,这意味着该响应将是两种类型中的任何一种。 +## `Union` or `anyOf` -这将在 OpenAPI 中使用 `anyOf` 进行定义。 +You can declare a response to be the `Union` of two types, that means, that the response would be any of the two. -为此,请使用标准的 Python 类型提示 `typing.Union`: +It will be defined in OpenAPI with `anyOf`. +To do that, use the standard Python type hint `typing.Union`: !!! note - 定义一个 `Union` 类型时,首先包括最详细的类型,然后是不太详细的类型。在下面的示例中,更详细的 `PlaneItem` 位于 `Union[PlaneItem,CarItem]` 中的 `CarItem` 之前。 + When defining a `Union`, include the most specific type first, followed by the less specific type. In the example below, the more specific `PlaneItem` comes before `CarItem` in `Union[PlaneItem, CarItem]`. -```Python hl_lines="1 14-15 18-20 33" -{!../../../docs_src/extra_models/tutorial003.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="1 14-15 18-20 33" + {!> ../../../docs_src/extra_models/tutorial003_py310.py!} + ``` + +=== "Python 3.6+" -## 模型列表 + ```Python hl_lines="1 14-15 18-20 33" + {!> ../../../docs_src/extra_models/tutorial003.py!} + ``` -你可以用同样的方式声明由对象列表构成的响应。 +### `Union` in Python 3.10 -为此,请使用标准的 Python `typing.List`: +In this example we pass `Union[PlaneItem, CarItem]` as the value of the argument `response_model`. -```Python hl_lines="1 20" -{!../../../docs_src/extra_models/tutorial004.py!} +Because we are passing it as a **value to an argument** instead of putting it in a **type annotation**, we have to use `Union` even in Python 3.10. + +If it was in a type annotation we could have used the vertical bar, as: + +```Python +some_variable: PlaneItem | CarItem ``` -## 任意 `dict` 构成的响应 +But if we put that in `response_model=PlaneItem | CarItem` we would get an error, because Python would try to perform an **invalid operation** between `PlaneItem` and `CarItem` instead of interpreting that as a type annotation. -你还可以使用一个任意的普通 `dict` 声明响应,仅声明键和值的类型,而不使用 Pydantic 模型。 +## List of models -如果你事先不知道有效的字段/属性名称(对于 Pydantic 模型是必需的),这将很有用。 +The same way, you can declare responses of lists of objects. -在这种情况下,你可以使用 `typing.Dict`: +For that, use the standard Python `typing.List` (or just `list` in Python 3.9 and above): -```Python hl_lines="1 8" -{!../../../docs_src/extra_models/tutorial005.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="18" + {!> ../../../docs_src/extra_models/tutorial004_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="1 20" + {!> ../../../docs_src/extra_models/tutorial004.py!} + ``` + +## Response with arbitrary `dict` + +You can also declare a response using a plain arbitrary `dict`, declaring just the type of the keys and values, without using a Pydantic model. + +This is useful if you don't know the valid field/attribute names (that would be needed for a Pydantic model) beforehand. + +In this case, you can use `typing.Dict` (or just `dict` in Python 3.9 and above): + +=== "Python 3.9+" + + ```Python hl_lines="6" + {!> ../../../docs_src/extra_models/tutorial005_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="1 8" + {!> ../../../docs_src/extra_models/tutorial005.py!} + ``` -## 总结 +## Recap -使用多个 Pydantic 模型,并针对不同场景自由地继承。 +Use multiple Pydantic models and inherit freely for each case. -如果一个实体必须能够具有不同的「状态」,你无需为每个状态的实体定义单独的数据模型。以用户「实体」为例,其状态有包含 `password`、包含 `password_hash` 以及不含密码。 +You don't need to have a single data model per entity if that entity must be able to have different "states". As the case with the user "entity" with a state including `password`, `password_hash` and no password. From 4f73128a714b1d00a3943283d8994a9091bf62ee Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:39 +0800 Subject: [PATCH 079/163] New translations first-steps.md (Chinese Simplified) --- docs/zh/docs/tutorial/first-steps.md | 225 +++++++++++++-------------- 1 file changed, 111 insertions(+), 114 deletions(-) diff --git a/docs/zh/docs/tutorial/first-steps.md b/docs/zh/docs/tutorial/first-steps.md index 30fae99cf8fc2..1368b89c76081 100644 --- a/docs/zh/docs/tutorial/first-steps.md +++ b/docs/zh/docs/tutorial/first-steps.md @@ -1,14 +1,14 @@ -# 第一步 +# First Steps -最简单的 FastAPI 文件可能像下面这样: +The simplest FastAPI file could look like this: ```Python {!../../../docs_src/first_steps/tutorial001.py!} ``` -将其复制到 `main.py` 文件中。 +Copy that to a file `main.py`. -运行实时服务器: +Run the live server:
@@ -25,83 +25,81 @@ $ uvicorn main:app --reload
!!! note - `uvicorn main:app` 命令含义如下: + The command `uvicorn main:app` refers to: - * `main`:`main.py` 文件(一个 Python「模块」)。 - * `app`:在 `main.py` 文件中通过 `app = FastAPI()` 创建的对象。 - * `--reload`:让服务器在更新代码后重新启动。仅在开发时使用该选项。 + * `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 use for development. - -在输出中,会有一行信息像下面这样: +In the output, there's a line with something like: ```hl_lines="4" INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ``` +That line shows the URL where your app is being served, in your local machine. -该行显示了你的应用在本机所提供服务的 URL 地址。 - -### 查看 +### Check it -打开浏览器访问 http://127.0.0.1:8000。 +Open your browser at http://127.0.0.1:8000. -你将看到如下的 JSON 响应: +You will see the JSON response as: ```JSON {"message": "Hello World"} ``` -### 交互式 API 文档 +### Interactive API docs -跳转到 http://127.0.0.1:8000/docs。 +Now go to http://127.0.0.1:8000/docs. -你将会看到自动生成的交互式 API 文档(由 Swagger UI 提供): +You will see the automatic interactive API documentation (provided by Swagger UI): ![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) -### 可选的 API 文档 +### Alternative API docs -前往 http://127.0.0.1:8000/redoc。 +And now, go to http://127.0.0.1:8000/redoc. -你将会看到可选的自动生成文档 (由 ReDoc 提供): +You will see the alternative automatic documentation (provided by ReDoc): ![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) ### OpenAPI -**FastAPI** 使用定义 API 的 **OpenAPI** 标准将你的所有 API 转换成「模式」。 +**FastAPI** generates a "schema" with all your API using the **OpenAPI** standard for defining APIs. -#### 「模式」 +#### "Schema" -「模式」是对事物的一种定义或描述。它并非具体的实现代码,而只是抽象的描述。 +A "schema" is a definition or description of something. Not the code that implements it, but just an abstract description. -#### API「模式」 +#### API "schema" -在这种场景下,OpenAPI 是一种规定如何定义 API 模式的规范。 +In this case, OpenAPI is a specification that dictates how to define a schema of your API. -定义的 OpenAPI 模式将包括你的 API 路径,以及它们可能使用的参数等等。 +This schema definition includes your API paths, the possible parameters they take, etc. -#### 数据「模式」 +#### Data "schema" -「模式」这个术语也可能指的是某些数据比如 JSON 的结构。 +The term "schema" might also refer to the shape of some data, like a JSON content. -在这种情况下,它可以表示 JSON 的属性及其具有的数据类型,等等。 +In that case, it would mean the JSON attributes, and data types they have, etc. -#### OpenAPI 和 JSON Schema +#### OpenAPI and JSON Schema -OpenAPI 为你的 API 定义 API 模式。该模式中包含了你的 API 发送和接收的数据的定义(或称为「模式」),这些定义通过 JSON 数据模式标准 **JSON Schema** 所生成。 +OpenAPI defines an API schema for your API. And that schema includes definitions (or "schemas") of the data sent and received by your API using **JSON Schema**, the standard for JSON data schemas. -#### 查看 `openapi.json` +#### Check the `openapi.json` -如果你对原始的 OpenAPI 模式长什么样子感到好奇,其实它只是一个自动生成的包含了所有 API 描述的 JSON。 +If you are curious about how the raw OpenAPI schema looks like, FastAPI automatically generates a JSON (schema) with the descriptions of all your API. -你可以直接在:http://127.0.0.1:8000/openapi.json 看到它。 +You can see it directly at: http://127.0.0.1:8000/openapi.json. -它将显示以如下内容开头的 JSON: +It will show a JSON starting with something like: ```JSON { - "openapi": "3.0.2", + "openapi": "3.1.0", "info": { "title": "FastAPI", "version": "0.1.0" @@ -120,40 +118,40 @@ OpenAPI 为你的 API 定义 API 模式。该模式中包含了你的 API 发送 ... ``` -#### OpenAPI 的用途 +#### What is OpenAPI for -驱动 FastAPI 内置的 2 个交互式文档系统的正是 OpenAPI 模式。 +The OpenAPI schema is what powers the two interactive documentation systems included. -并且还有数十种替代方案,它们全部都基于 OpenAPI。你可以轻松地将这些替代方案中的任何一种添加到使用 **FastAPI** 构建的应用程序中。 +And there are dozens of alternatives, all based on OpenAPI. You could easily add any of those alternatives to your application built with **FastAPI**. -你还可以使用它自动生成与你的 API 进行通信的客户端代码。例如 web 前端,移动端或物联网嵌入程序。 +You could also use it to generate code automatically, for clients that communicate with your API. For example, frontend, mobile or IoT applications. -## 分步概括 +## Recap, step by step -### 步骤 1:导入 `FastAPI` +### Step 1: import `FastAPI` ```Python hl_lines="1" {!../../../docs_src/first_steps/tutorial001.py!} ``` -`FastAPI` 是一个为你的 API 提供了所有功能的 Python 类。 +`FastAPI` is a Python class that provides all the functionality for your API. -!!! note "技术细节" - `FastAPI` 是直接从 `Starlette` 继承的类。 +!!! note "Technical Details" + `FastAPI` is a class that inherits directly from `Starlette`. - 你可以通过 `FastAPI` 使用所有的 Starlette 的功能。 + You can use all the Starlette functionality with `FastAPI` too. -### 步骤 2:创建一个 `FastAPI`「实例」 +### Step 2: create a `FastAPI` "instance" ```Python hl_lines="3" {!../../../docs_src/first_steps/tutorial001.py!} ``` -这里的变量 `app` 会是 `FastAPI` 类的一个「实例」。 +Here the `app` variable will be an "instance" of the class `FastAPI`. -这个实例将是创建你所有 API 的主要交互对象。 +This will be the main point of interaction to create all your API. -这个 `app` 同样在如下命令中被 `uvicorn` 所引用: +This `app` is the same one referred by `uvicorn` in the command:
@@ -165,13 +163,13 @@ $ uvicorn main:app --reload
-如果你像下面这样创建应用: +If you create your app like: ```Python hl_lines="3" {!../../../docs_src/first_steps/tutorial002.py!} ``` -将代码放入 `main.py` 文件中,然后你可以像下面这样运行 `uvicorn`: +And put it in a file `main.py`, then you would call `uvicorn` like:
@@ -183,93 +181,92 @@ $ uvicorn main:my_awesome_api --reload
-### 步骤 3:创建一个*路径操作* +### Step 3: create a *path operation* -#### 路径 +#### Path -这里的「路径」指的是 URL 中从第一个 `/` 起的后半部分。 +"Path" here refers to the last part of the URL starting from the first `/`. -所以,在一个这样的 URL 中: +So, in a URL like: ``` https://example.com/items/foo ``` -...路径会是: +...the path would be: ``` /items/foo ``` !!! info - 「路径」也通常被称为「端点」或「路由」。 + A "path" is also commonly called an "endpoint" or a "route". -开发 API 时,「路径」是用来分离「关注点」和「资源」的主要手段。 +While building an API, the "path" is the main way to separate "concerns" and "resources". -#### 操作 +#### Operation -这里的「操作」指的是一种 HTTP「方法」。 +"Operation" here refers to one of the HTTP "methods". -下列之一: +One of: * `POST` * `GET` * `PUT` * `DELETE` -...以及更少见的几种: +...and the more exotic ones: * `OPTIONS` * `HEAD` * `PATCH` * `TRACE` -在 HTTP 协议中,你可以使用以上的其中一种(或多种)「方法」与每个路径进行通信。 +In the HTTP protocol, you can communicate to each path using one (or more) of these "methods". --- -在开发 API 时,你通常使用特定的 HTTP 方法去执行特定的行为。 +When building APIs, you normally use these specific HTTP methods to perform a specific action. -通常使用: +Normally you use: -* `POST`:创建数据。 -* `GET`:读取数据。 -* `PUT`:更新数据。 -* `DELETE`:删除数据。 +* `POST`: to create data. +* `GET`: to read data. +* `PUT`: to update data. +* `DELETE`: to delete data. -因此,在 OpenAPI 中,每一个 HTTP 方法都被称为「操作」。 +So, in OpenAPI, each of the HTTP methods is called an "operation". -我们也打算称呼它们为「操作」。 +We are going to call them "**operations**" too. -#### 定义一个*路径操作装饰器* +#### Define a *path operation decorator* ```Python hl_lines="6" {!../../../docs_src/first_steps/tutorial001.py!} ``` -`@app.get("/")` 告诉 **FastAPI** 在它下方的函数负责处理如下访问请求: - -* 请求路径为 `/` -* 使用 get 操作 - -!!! info "`@decorator` Info" - `@something` 语法在 Python 中被称为「装饰器」。 +The `@app.get("/")` tells **FastAPI** that the function right below is in charge of handling requests that go to: - 像一顶漂亮的装饰帽一样,将它放在一个函数的上方(我猜测这个术语的命名就是这么来的)。 +* the path `/` +* using a get operation - 装饰器接收位于其下方的函数并且用它完成一些工作。 +!!! info "`@decorator` Info" That `@something` syntax in Python is called a "decorator". - 在我们的例子中,这个装饰器告诉 **FastAPI** 位于其下方的函数对应着**路径** `/` 加上 `get` **操作**。 + You put it on top of a function. Like a pretty decorative hat (I guess that's where the term came from). + + A "decorator" takes the function below and does something with it. + + In our case, this decorator tells **FastAPI** that the function below corresponds to the **path** `/` with an **operation** `get`. + + It is the "**path operation decorator**". - 它是一个「**路径操作装饰器**」。 - -你也可以使用其他的操作: +You can also use the other operations: * `@app.post()` * `@app.put()` * `@app.delete()` -以及更少见的: +And the more exotic ones: * `@app.options()` * `@app.head()` @@ -277,59 +274,59 @@ https://example.com/items/foo * `@app.trace()` !!! tip - 您可以随意使用任何一个操作(HTTP方法)。 - - **FastAPI** 没有强制要求操作有任何特定的含义。 - - 此处提供的信息仅作为指导,而不是要求。 + You are free to use each operation (HTTP method) as you wish. - 比如,当使用 GraphQL 时通常你所有的动作都通过 `post` 一种方法执行。 + **FastAPI** doesn't enforce any specific meaning. + + The information here is presented as a guideline, not a requirement. + + For example, when using GraphQL you normally perform all the actions using only `POST` operations. -### 步骤 4:定义**路径操作函数** +### Step 4: define the **path operation function** -这是我们的「**路径操作函数**」: +This is our "**path operation function**": -* **路径**:是 `/`。 -* **操作**:是 `get`。 -* **函数**:是位于「装饰器」下方的函数(位于 `@app.get("/")` 下方)。 +* **path**: is `/`. +* **operation**: is `get`. +* **function**: is the function below the "decorator" (below `@app.get("/")`). ```Python hl_lines="7" {!../../../docs_src/first_steps/tutorial001.py!} ``` -这是一个 Python 函数。 +This is a Python function. -每当 **FastAPI** 接收一个使用 `GET` 方法访问 URL「`/`」的请求时这个函数会被调用。 +It will be called by **FastAPI** whenever it receives a request to the URL "`/`" using a `GET` operation. -在这个例子中,它是一个 `async` 函数。 +In this case, it is an `async` function. --- -你也可以将其定义为常规函数而不使用 `async def`: +You could also define it as a normal function instead of `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}。 + If you don't know the difference, check the [Async: *"In a hurry?"*](../async.md#in-a-hurry){.internal-link target=_blank}. -### 步骤 5:返回内容 +### Step 5: return the content ```Python hl_lines="8" {!../../../docs_src/first_steps/tutorial001.py!} ``` -你可以返回一个 `dict`、`list`,像 `str`、`int` 一样的单个值,等等。 +You can return a `dict`, `list`, singular values as `str`, `int`, etc. -你还可以返回 Pydantic 模型(稍后你将了解更多)。 +You can also return Pydantic models (you'll see more about that later). -还有许多其他将会自动转换为 JSON 的对象和模型(包括 ORM 对象等)。尝试下使用你最喜欢的一种,它很有可能已经被支持。 +There are many other objects and models that will be automatically converted to JSON (including ORMs, etc). Try using your favorite ones, it's highly probable that they are already supported. -## 总结 +## Recap -* 导入 `FastAPI`。 -* 创建一个 `app` 实例。 -* 编写一个**路径操作装饰器**(如 `@app.get("/")`)。 -* 编写一个**路径操作函数**(如上面的 `def root(): ...`)。 -* 运行开发服务器(如 `uvicorn main:app --reload`)。 +* Import `FastAPI`. +* Create an `app` instance. +* Write a **path operation decorator** (like `@app.get("/")`). +* Write a **path operation function** (like `def root(): ...` above). +* Run the development server (like `uvicorn main:app --reload`). From 7b4c777ae8a0ff1b40dd21353adcae6dd801a20c Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:40 +0800 Subject: [PATCH 080/163] New translations handling-errors.md (Chinese Simplified) --- docs/zh/docs/tutorial/handling-errors.md | 200 ++++++++++------------- 1 file changed, 86 insertions(+), 114 deletions(-) diff --git a/docs/zh/docs/tutorial/handling-errors.md b/docs/zh/docs/tutorial/handling-errors.md index 9b066bc2cee65..b150201e797d3 100644 --- a/docs/zh/docs/tutorial/handling-errors.md +++ b/docs/zh/docs/tutorial/handling-errors.md @@ -1,151 +1,139 @@ -# 处理错误 +# Handling Errors -某些情况下,需要向客户端返回错误提示。 +There are many situations in where you need to notify an error to a client that is using your API. -这里所谓的客户端包括前端浏览器、其他应用程序、物联网设备等。 +This client could be a browser with a frontend, a code from someone else, an IoT device, etc. -需要向客户端返回错误提示的场景主要如下: +You could need to tell the client that: -- 客户端没有执行操作的权限 -- 客户端没有访问资源的权限 -- 客户端要访问的项目不存在 -- 等等 ... +* The client doesn't have enough privileges for that operation. +* The client doesn't have access to that resource. +* The item the client was trying to access doesn't exist. +* etc. -遇到这些情况时,通常要返回 **4XX**(400 至 499)**HTTP 状态码**。 +In these cases, you would normally return an **HTTP status code** in the range of **400** (from 400 to 499). -**4XX** 状态码与表示请求成功的 **2XX**(200 至 299) HTTP 状态码类似。 +This is similar to the 200 HTTP status codes (from 200 to 299). Those "200" status codes mean that somehow there was a "success" in the request. -只不过,**4XX** 状态码表示客户端发生的错误。 +The status codes in the 400 range mean that there was an error from the client. -大家都知道**「404 Not Found」**错误,还有调侃这个错误的笑话吧? +Remember all those **"404 Not Found"** errors (and jokes)? -## 使用 `HTTPException` +## Use `HTTPException` -向客户端返回 HTTP 错误响应,可以使用 `HTTPException`。 +To return HTTP responses with errors to the client you use `HTTPException`. -### 导入 `HTTPException` +### Import `HTTPException` ```Python hl_lines="1" {!../../../docs_src/handling_errors/tutorial001.py!} - ``` -### 触发 `HTTPException` +### Raise an `HTTPException` in your code -`HTTPException` 是额外包含了和 API 有关数据的常规 Python 异常。 +`HTTPException` is a normal Python exception with additional data relevant for APIs. -因为是 Python 异常,所以不能 `return`,只能 `raise`。 +Because it's a Python exception, you don't `return` it, you `raise` it. -如在调用*路径操作函数*里的工具函数时,触发了 `HTTPException`,FastAPI 就不再继续执行*路径操作函数*中的后续代码,而是立即终止请求,并把 `HTTPException` 的 HTTP 错误发送至客户端。 +This also means that if you are inside a utility function that you are calling inside of your *path operation function*, and you raise the `HTTPException` from inside of that utility function, it won't run the rest of the code in the *path operation function*, it will terminate that request right away and send the HTTP error from the `HTTPException` to the client. -在介绍依赖项与安全的章节中,您可以了解更多用 `raise` 异常代替 `return` 值的优势。 +The benefit of raising an exception over `return`ing a value will be more evident in the section about Dependencies and Security. -本例中,客户端用 `ID` 请求的 `item` 不存在时,触发状态码为 `404` 的异常: +In this example, when the client requests an item by an ID that doesn't exist, raise an exception with a status code of `404`: ```Python hl_lines="11" {!../../../docs_src/handling_errors/tutorial001.py!} - ``` -### 响应结果 +### The resulting response -请求为 `http://example.com/items/foo`(`item_id` 为 `「foo」`)时,客户端会接收到 HTTP 状态码 - 200 及如下 JSON 响应结果: +If the client requests `http://example.com/items/foo` (an `item_id` `"foo"`), that client will receive an HTTP status code of 200, and a JSON response of: ```JSON { "item": "The Foo Wrestlers" } - ``` -但如果客户端请求 `http://example.com/items/bar`(`item_id` `「bar」` 不存在时),则会接收到 HTTP 状态码 - 404(「未找到」错误)及如下 JSON 响应结果: +But if the client requests `http://example.com/items/bar` (a non-existent `item_id` `"bar"`), that client will receive an HTTP status code of 404 (the "not found" error), and a JSON response of: ```JSON { "detail": "Item not found" } - ``` -!!! tip "提示" - - 触发 `HTTPException` 时,可以用参数 `detail` 传递任何能转换为 JSON 的值,不仅限于 `str`。 - - 还支持传递 `dict`、`list` 等数据结构。 - - **FastAPI** 能自动处理这些数据,并将之转换为 JSON。 +!!! tip + When raising an `HTTPException`, you can pass any value that can be converted to JSON as the parameter `detail`, not only `str`. + You could pass a `dict`, a `list`, etc. + + They are handled automatically by **FastAPI** and converted to JSON. -## 添加自定义响应头 +## Add custom headers -有些场景下要为 HTTP 错误添加自定义响应头。例如,出于某些方面的安全需要。 +There are some situations in where it's useful to be able to add custom headers to the HTTP error. For example, for some types of security. -一般情况下可能不会需要在代码中直接使用响应头。 +You probably won't need to use it directly in your code. -但对于某些高级应用场景,还是需要添加自定义响应头: +But in case you needed it for an advanced scenario, you can add custom headers: ```Python hl_lines="14" {!../../../docs_src/handling_errors/tutorial002.py!} - ``` -## 安装自定义异常处理器 +## Install custom exception handlers -添加自定义处理器,要使用 [Starlette 的异常工具](https://www.starlette.io/exceptions/)。 +You can add custom exception handlers with the same exception utilities from Starlette. -假设要触发的自定义异常叫作 `UnicornException`。 +Let's say you have a custom exception `UnicornException` that you (or a library you use) might `raise`. -且需要 FastAPI 实现全局处理该异常。 +And you want to handle this exception globally with FastAPI. -此时,可以用 `@app.exception_handler()` 添加自定义异常控制器: +You could add a custom exception handler with `@app.exception_handler()`: ```Python hl_lines="5-7 13-18 24" {!../../../docs_src/handling_errors/tutorial003.py!} - ``` -请求 `/unicorns/yolo` 时,路径操作会触发 `UnicornException`。 +Here, if you request `/unicorns/yolo`, the *path operation* will `raise` a `UnicornException`. -但该异常将会被 `unicorn_exception_handler` 处理。 +But it will be handled by the `unicorn_exception_handler`. -接收到的错误信息清晰明了,HTTP 状态码为 `418`,JSON 内容如下: +So, you will receive a clean error, with an HTTP status code of `418` and a JSON content of: ```JSON {"message": "Oops! yolo did something. There goes a rainbow..."} - ``` -!!! note "技术细节" - - `from starlette.requests import Request` 和 `from starlette.responses import JSONResponse` 也可以用于导入 `Request` 和 `JSONResponse`。 +!!! note "Technical Details" + You could also use `from starlette.requests import Request` and `from starlette.responses import JSONResponse`. - **FastAPI** 提供了与 `starlette.responses` 相同的 `fastapi.responses` 作为快捷方式,但大部分响应操作都可以直接从 Starlette 导入。同理,`Request` 也是如此。 + **FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. The same with `Request`. +## Override the default exception handlers -## 覆盖默认异常处理器 +**FastAPI** has some default exception handlers. -**FastAPI** 自带了一些默认异常处理器。 +These handlers are in charge of returning the default JSON responses when you `raise` an `HTTPException` and when the request has invalid data. -触发 `HTTPException` 或请求无效数据时,这些处理器返回默认的 JSON 响应结果。 +You can override these exception handlers with your own. -不过,也可以使用自定义处理器覆盖默认异常处理器。 +### Override request validation exceptions -### 覆盖请求验证异常 +When a request contains invalid data, **FastAPI** internally raises a `RequestValidationError`. -请求中包含无效数据时,**FastAPI** 内部会触发 `RequestValidationError`。 +And it also includes a default exception handler for it. -该异常也内置了默认异常处理器。 +To override it, import the `RequestValidationError` and use it with `@app.exception_handler(RequestValidationError)` to decorate the exception handler. -覆盖默认异常处理器时需要导入 `RequestValidationError`,并用 `@app.excption_handler(RequestValidationError)` 装饰异常处理器。 - -这样,异常处理器就可以接收 `Request` 与异常。 +The exception handler will receive a `Request` and the exception. ```Python hl_lines="2 14-16" {!../../../docs_src/handling_errors/tutorial004.py!} - ``` -访问 `/items/foo`,可以看到以下内容替换了默认 JSON 错误信息: +Now, if you go to `/items/foo`, instead of getting the default JSON error with: ```JSON { @@ -160,75 +148,66 @@ } ] } - ``` -以下是文本格式的错误信息: +you will get a text version, with: ``` 1 validation error path -> item_id value is not a valid integer (type=type_error.integer) - ``` -### `RequestValidationError` vs `ValidationError` - -!!! warning "警告" +#### `RequestValidationError` vs `ValidationError` - 如果您觉得现在还用不到以下技术细节,可以先跳过下面的内容。 +!!! warning + These are technical details that you might skip if it's not important for you now. +`RequestValidationError` is a sub-class of Pydantic's `ValidationError`. -`RequestValidationError` 是 Pydantic 的 `ValidationError` 的子类。 +**FastAPI** uses it so that, if you use a Pydantic model in `response_model`, and your data has an error, you will see the error in your log. -**FastAPI** 调用的就是 `RequestValidationError` 类,因此,如果在 `response_model` 中使用 Pydantic 模型,且数据有错误时,在日志中就会看到这个错误。 +But the client/user will not see it. Instead, the client will receive an "Internal Server Error" with a HTTP status code `500`. -但客户端或用户看不到这个错误。反之,客户端接收到的是 HTTP 状态码为 `500` 的「内部服务器错误」。 +It should be this way because if you have a Pydantic `ValidationError` in your *response* or anywhere in your code (not in the client's *request*), it's actually a bug in your code. -这是因为在*响应*或代码(不是在客户端的请求里)中出现的 Pydantic `ValidationError` 是代码的 bug。 +And while you fix it, your clients/users shouldn't have access to internal information about the error, as that could expose a security vulnerability. -修复错误时,客户端或用户不能访问错误的内部信息,否则会造成安全隐患。 +### Override the `HTTPException` error handler -### 覆盖 `HTTPException` 错误处理器 +The same way, you can override the `HTTPException` handler. -同理,也可以覆盖 `HTTPException` 处理器。 - -例如,只为错误返回纯文本响应,而不是返回 JSON 格式的内容: +For example, you could want to return a plain text response instead of JSON for these errors: ```Python hl_lines="3-4 9-11 22" {!../../../docs_src/handling_errors/tutorial004.py!} - ``` -!!! note "技术细节" +!!! note "Technical Details" + You could also use `from starlette.responses import PlainTextResponse`. - 还可以使用 `from starlette.responses import PlainTextResponse`。 + **FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. - **FastAPI** 提供了与 `starlette.responses` 相同的 `fastapi.responses` 作为快捷方式,但大部分响应都可以直接从 Starlette 导入。 +### Use the `RequestValidationError` body +The `RequestValidationError` contains the `body` it received with invalid data. -### 使用 `RequestValidationError` 的请求体 - -`RequestValidationError` 包含其接收到的无效数据请求的 `body` 。 - -开发时,可以用这个请求体生成日志、调试错误,并返回给用户。 +You could use it while developing your app to log the body and debug it, return it to the user, etc. ```Python hl_lines="14" {!../../../docs_src/handling_errors/tutorial005.py!} - ``` -现在试着发送一个无效的 `item`,例如: +Now try sending an invalid item like: ```JSON { "title": "towel", "size": "XL" } - ``` -收到的响应包含 `body` 信息,并说明数据是无效的: +You will receive a response telling you that the data is invalid containing the received body: ```JSON hl_lines="12-15" { @@ -247,43 +226,36 @@ path -> item_id "size": "XL" } } - ``` -### FastAPI `HTTPException` vs Starlette `HTTPException` +#### FastAPI's `HTTPException` vs Starlette's `HTTPException` -**FastAPI** 也提供了自有的 `HTTPException`。 +**FastAPI** has its own `HTTPException`. -**FastAPI** 的 `HTTPException` 继承自 Starlette 的 `HTTPException` 错误类。 +And **FastAPI**'s `HTTPException` error class inherits from Starlette's `HTTPException` error class. -它们之间的唯一区别是,**FastAPI** 的 `HTTPException` 可以在响应中添加响应头。 +The only difference, is that **FastAPI**'s `HTTPException` allows you to add headers to be included in the response. -OAuth 2.0 等安全工具需要在内部调用这些响应头。 +This is needed/used internally for OAuth 2.0 and some security utilities. -因此你可以继续像平常一样在代码中触发 **FastAPI** 的 `HTTPException` 。 +So, you can keep raising **FastAPI**'s `HTTPException` as normally in your code. -但注册异常处理器时,应该注册到来自 Starlette 的 `HTTPException`。 +But when you register an exception handler, you should register it for Starlette's `HTTPException`. -这样做是为了,当 Starlette 的内部代码、扩展或插件触发 Starlette `HTTPException` 时,处理程序能够捕获、并处理此异常。 +This way, if any part of Starlette's internal code, or a Starlette extension or plug-in, raises a Starlette `HTTPException`, your handler will be able to catch and handle it. -注意,本例代码中同时使用了这两个 `HTTPException`,此时,要把 Starlette 的 `HTTPException` 命名为 `StarletteHTTPException`: +In this example, to be able to have both `HTTPException`s in the same code, Starlette's exceptions is renamed to `StarletteHTTPException`: ```Python from starlette.exceptions import HTTPException as StarletteHTTPException - ``` -### 复用 **FastAPI** 异常处理器 +### Re-use **FastAPI**'s exception handlers -FastAPI 支持先对异常进行某些处理,然后再使用 **FastAPI** 中处理该异常的默认异常处理器。 - -从 `fastapi.exception_handlers` 中导入要复用的默认异常处理器: +If you want to use the exception along with the same default exception handlers from **FastAPI**, You can import and re-use the default exception handlers from `fastapi.exception_handlers`: ```Python hl_lines="2-5 15 21" {!../../../docs_src/handling_errors/tutorial006.py!} - ``` -虽然,本例只是输出了夸大其词的错误信息。 - -但也足以说明,可以在处理异常之后再复用默认的异常处理器。 +In this example you are just `print`ing the error with a very expressive message, but you get the idea. You can use the exception and then just re-use the default exception handlers. From d51695fc93eeca8030e65ed4397b9b76fde5b42c Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:41 +0800 Subject: [PATCH 081/163] New translations header-params.md (Chinese Simplified) --- docs/zh/docs/tutorial/header-params.md | 220 ++++++++++++++++++++----- 1 file changed, 178 insertions(+), 42 deletions(-) diff --git a/docs/zh/docs/tutorial/header-params.md b/docs/zh/docs/tutorial/header-params.md index c4b1c38ceecc8..9e928cdc6b482 100644 --- a/docs/zh/docs/tutorial/header-params.md +++ b/docs/zh/docs/tutorial/header-params.md @@ -1,79 +1,215 @@ -# Header 参数 +# Header Parameters -你可以使用定义 `Query`, `Path` 和 `Cookie` 参数一样的方法定义 Header 参数。 +You can define Header parameters the same way you define `Query`, `Path` and `Cookie` parameters. -## 导入 `Header` +## Import `Header` -首先导入 `Header`: +First import `Header`: -```Python hl_lines="3" -{!../../../docs_src/header_params/tutorial001.py!} -``` +=== "Python 3.10+" -## 声明 `Header` 参数 + ```Python hl_lines="3" + {!> ../../../docs_src/header_params/tutorial001_an_py310.py!} + ``` -然后使用和`Path`, `Query` and `Cookie` 一样的结构定义 header 参数 +=== "Python 3.9+" -第一个值是默认值,你可以传递所有的额外验证或注释参数: + ```Python hl_lines="3" + {!> ../../../docs_src/header_params/tutorial001_an_py39.py!} + ``` -```Python hl_lines="9" -{!../../../docs_src/header_params/tutorial001.py!} -``` +=== "Python 3.6+" + + ```Python hl_lines="3" + {!> ../../../docs_src/header_params/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="1" + {!> ../../../docs_src/header_params/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="3" + {!> ../../../docs_src/header_params/tutorial001.py!} + ``` + +## Declare `Header` parameters + +Then declare the header parameters using the same structure as with `Path`, `Query` and `Cookie`. + +The first value is the default value, you can pass all the extra validation or annotation parameters: + +=== "Python 3.10+" -!!! note "技术细节" - `Header` 是 `Path`, `Query` 和 `Cookie` 的兄弟类型。它也继承自通用的 `Param` 类. + ```Python hl_lines="9" + {!> ../../../docs_src/header_params/tutorial001_an_py310.py!} + ``` - 但是请记得,当你从`fastapi`导入 `Query`, `Path`, `Header`, 或其他时,实际上导入的是返回特定类型的函数。 +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/header_params/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="10" + {!> ../../../docs_src/header_params/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7" + {!> ../../../docs_src/header_params/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="9" + {!> ../../../docs_src/header_params/tutorial001.py!} + ``` + +!!! note "Technical Details" + `Header` is a "sister" class of `Path`, `Query` and `Cookie`. It also inherits from the same common `Param` class. + + But remember that when you import `Query`, `Path`, `Header`, and others from `fastapi`, those are actually functions that return special classes. !!! info - 为了声明headers, 你需要使用`Header`, 因为否则参数将被解释为查询参数。 + To declare headers, you need to use `Header`, because otherwise the parameters would be interpreted as query parameters. -## 自动转换 +## Automatic conversion -`Header` 在 `Path`, `Query` 和 `Cookie` 提供的功能之上有一点额外的功能。 +`Header` has a little extra functionality on top of what `Path`, `Query` and `Cookie` provide. -大多数标准的headers用 "连字符" 分隔,也称为 "减号" (`-`)。 +Most of the standard headers are separated by a "hyphen" character, also known as the "minus symbol" (`-`). -但是像 `user-agent` 这样的变量在Python中是无效的。 +But a variable like `user-agent` is invalid in Python. -因此, 默认情况下, `Header` 将把参数名称的字符从下划线 (`_`) 转换为连字符 (`-`) 来提取并记录 headers. +So, by default, `Header` will convert the parameter names characters from underscore (`_`) to hyphen (`-`) to extract and document the headers. -同时,HTTP headers 是大小写不敏感的,因此,因此可以使用标准Python样式(也称为 "snake_case")声明它们。 +Also, HTTP headers are case-insensitive, so, you can declare them with standard Python style (also known as "snake_case"). -因此,您可以像通常在Python代码中那样使用 `user_agent` ,而不需要将首字母大写为 `User_Agent` 或类似的东西。 +So, you can use `user_agent` as you normally would in Python code, instead of needing to capitalize the first letters as `User_Agent` or something similar. -如果出于某些原因,你需要禁用下划线到连字符的自动转换,设置`Header`的参数 `convert_underscores` 为 `False`: +If for some reason you need to disable automatic conversion of underscores to hyphens, set the parameter `convert_underscores` of `Header` to `False`: -```Python hl_lines="10" -{!../../../docs_src/header_params/tutorial002.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="10" + {!> ../../../docs_src/header_params/tutorial002_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="11" + {!> ../../../docs_src/header_params/tutorial002_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="12" + {!> ../../../docs_src/header_params/tutorial002_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="8" + {!> ../../../docs_src/header_params/tutorial002_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="10" + {!> ../../../docs_src/header_params/tutorial002.py!} + ``` !!! warning - 在设置 `convert_underscores` 为 `False` 之前,请记住,一些HTTP代理和服务器不允许使用带有下划线的headers。 + Before setting `convert_underscores` to `False`, bear in mind that some HTTP proxies and servers disallow the usage of headers with underscores. +## Duplicate headers -## 重复的 headers +It is possible to receive duplicate headers. That means, the same header with multiple values. -有可能收到重复的headers。这意味着,相同的header具有多个值。 +You can define those cases using a list in the type declaration. -您可以在类型声明中使用一个list来定义这些情况。 +You will receive all the values from the duplicate header as a Python `list`. -你可以通过一个Python `list` 的形式获得重复header的所有值。 +For example, to declare a header of `X-Token` that can appear more than once, you can write: -比如, 为了声明一个 `X-Token` header 可以出现多次,你可以这样写: +=== "Python 3.10+" -```Python hl_lines="9" -{!../../../docs_src/header_params/tutorial003.py!} -``` + ```Python hl_lines="9" + {!> ../../../docs_src/header_params/tutorial003_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/header_params/tutorial003_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="10" + {!> ../../../docs_src/header_params/tutorial003_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7" + {!> ../../../docs_src/header_params/tutorial003_py310.py!} + ``` + +=== "Python 3.9+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="9" + {!> ../../../docs_src/header_params/tutorial003_py39.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="9" + {!> ../../../docs_src/header_params/tutorial003.py!} + ``` -如果你与*路径操作*通信时发送两个HTTP headers,就像: +If you communicate with that *path operation* sending two HTTP headers like: ``` X-Token: foo X-Token: bar ``` -响应会是: +The response would be like: ```JSON { @@ -84,8 +220,8 @@ X-Token: bar } ``` -## 回顾 +## Recap -使用 `Header` 来声明 header , 使用和 `Query`, `Path` 与 `Cookie` 相同的模式。 +Declare headers with `Header`, using the same common pattern as `Query`, `Path` and `Cookie`. -不用担心变量中的下划线,**FastAPI** 会负责转换它们。 +And don't worry about underscores in your variables, **FastAPI** will take care of converting them. From 69088834ef4ea150e1e29894ef8de9584f1854fb Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:42 +0800 Subject: [PATCH 082/163] New translations index.md (Chinese Simplified) --- docs/zh/docs/tutorial/index.md | 48 ++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/docs/zh/docs/tutorial/index.md b/docs/zh/docs/tutorial/index.md index 6180d3de399ae..d592f4744650a 100644 --- a/docs/zh/docs/tutorial/index.md +++ b/docs/zh/docs/tutorial/index.md @@ -1,18 +1,18 @@ -# 教程 - 用户指南 +# Tutorial - User Guide -本教程将一步步向你展示如何使用 **FastAPI** 的绝大部分特性。 +This tutorial shows you how to use **FastAPI** with most of its features, step by step. -各个章节的内容循序渐进,但是又围绕着单独的主题,所以你可以直接跳转到某个章节以解决你的特定需求。 +Each section gradually builds on the previous ones, but it's structured to separate topics, so that you can go directly to any specific one to solve your specific API needs. -本教程同样可以作为将来的参考手册。 +It is also built to work as a future reference. -你可以随时回到本教程并查阅你需要的内容。 +So you can come back and see exactly what you need. -## 运行代码 +## Run the code -所有代码片段都可以复制后直接使用(它们实际上是经过测试的 Python 文件)。 +All the code blocks can be copied and used directly (they are actually tested Python files). -要运行任何示例,请将代码复制到 `main.py` 文件中,然后使用以下命令启动 `uvicorn`: +To run any of the examples, copy the code to a file `main.py`, and start `uvicorn` with:
@@ -28,17 +28,17 @@ $ uvicorn main:app --reload
-强烈建议你在本地编写或复制代码,对其进行编辑并运行。 +It is **HIGHLY encouraged** that you write or copy the code, edit it and run it locally. -在编辑器中使用 FastAPI 会真正地展现出它的优势:只需要编写很少的代码,所有的类型检查,代码补全等等。 +Using it in your editor is what really shows you the benefits of FastAPI, seeing how little code you have to write, all the type checks, autocompletion, etc. --- -## 安装 FastAPI +## Install FastAPI -第一个步骤是安装 FastAPI。 +The first step is to install FastAPI. -为了使用本教程,你可能需要安装所有的可选依赖及对应功能: +For the tutorial, you might want to install it with all the optional dependencies and features:
@@ -50,31 +50,33 @@ $ pip install "fastapi[all]"
-......以上安装还包括了 `uvicorn`,你可以将其用作运行代码的服务器。 +...that also includes `uvicorn`, that you can use as the server that runs your code. !!! note - 你也可以分开来安装。 + You can also install it part by part. - 假如你想将应用程序部署到生产环境,你可能要执行以下操作: + This is what you would probably do once you want to deploy your application to production: ``` pip install fastapi ``` - 并且安装`uvicorn`来作为服务器: + + Also install `uvicorn` to work as the server: ``` pip install "uvicorn[standard]" ``` - 然后对你想使用的每个可选依赖项也执行相同的操作。 -## 进阶用户指南 + And the same for each of the optional dependencies that you want to use. + +## Advanced User Guide -在本**教程-用户指南**之后,你可以阅读**进阶用户指南**。 +There is also an **Advanced User Guide** that you can read later after this **Tutorial - User guide**. -**进阶用户指南**以本教程为基础,使用相同的概念,并教授一些额外的特性。 +The **Advanced User Guide**, builds on this, uses the same concepts, and teaches you some extra features. -但是你应该先阅读**教程-用户指南**(即你现在正在阅读的内容)。 +But you should first read the **Tutorial - User Guide** (what you are reading right now). -教程经过精心设计,使你可以仅通过**教程-用户指南**来开发一个完整的应用程序,然后根据你的需要,使用**进阶用户指南**中的一些其他概念,以不同的方式来扩展它。 +It's designed so that you can build a complete application with just the **Tutorial - User Guide**, and then extend it in different ways, depending on your needs, using some of the additional ideas from the **Advanced User Guide**. From f24c22fdcf22b52f04ed42bb6961c2b0c1b0ba96 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:43 +0800 Subject: [PATCH 083/163] New translations metadata.md (Chinese Simplified) --- docs/zh/docs/tutorial/metadata.md | 228 ++++++++++++++++-------------- 1 file changed, 123 insertions(+), 105 deletions(-) diff --git a/docs/zh/docs/tutorial/metadata.md b/docs/zh/docs/tutorial/metadata.md index 3e669bc72fa24..e0722c2e3a9a2 100644 --- a/docs/zh/docs/tutorial/metadata.md +++ b/docs/zh/docs/tutorial/metadata.md @@ -1,105 +1,123 @@ -# 元数据和文档 URL - -你可以在 **FastAPI** 应用中自定义几个元数据配置。 - -## 标题、描述和版本 - -你可以设定: - -* **Title**:在 OpenAPI 和自动 API 文档用户界面中作为 API 的标题/名称使用。 -* **Description**:在 OpenAPI 和自动 API 文档用户界面中用作 API 的描述。 -* **Version**:API 版本,例如 `v2` 或者 `2.5.0`。 - * 如果你之前的应用程序版本也使用 OpenAPI 会很有用。 - -使用 `title`、`description` 和 `version` 来设置它们: - -```Python hl_lines="4-6" -{!../../../docs_src/metadata/tutorial001.py!} -``` - -通过这样设置,自动 API 文档看起来会像: - - - -## 标签元数据 - -你也可以使用参数 `openapi_tags`,为用于分组路径操作的不同标签添加额外的元数据。 - -它接受一个列表,这个列表包含每个标签对应的一个字典。 - -每个字典可以包含: - -* `name`(**必要**):一个 `str`,它与*路径操作*和 `APIRouter` 中使用的 `tags` 参数有相同的标签名。 -* `description`:一个用于简短描述标签的 `str`。它支持 Markdown 并且会在文档用户界面中显示。 -* `externalDocs`:一个描述外部文档的 `dict`: - * `description`:用于简短描述外部文档的 `str`。 - * `url`(**必要**):外部文档的 URL `str`。 - -### 创建标签元数据 - -让我们在带有标签的示例中为 `users` 和 `items` 试一下。 - -创建标签元数据并把它传递给 `openapi_tags` 参数: - -```Python hl_lines="3-16 18" -{!../../../docs_src/metadata/tutorial004.py!} -``` - -注意你可以在描述内使用 Markdown,例如「login」会显示为粗体(**login**)以及「fancy」会显示为斜体(_fancy_)。 - -!!! 提示 - 不必为你使用的所有标签都添加元数据。 - -### 使用你的标签 - -将 `tags` 参数和*路径操作*(以及 `APIRouter`)一起使用,将其分配给不同的标签: - -```Python hl_lines="21 26" -{!../../../docs_src/metadata/tutorial004.py!} -``` - -!!! 信息 - 阅读更多关于标签的信息[路径操作配置](../path-operation-configuration/#tags){.internal-link target=_blank}。 - -### 查看文档 - -如果你现在查看文档,它们会显示所有附加的元数据: - - - -### 标签顺序 - -每个标签元数据字典的顺序也定义了在文档用户界面显示的顺序。 - -例如按照字母顺序,即使 `users` 排在 `items` 之后,它也会显示在前面,因为我们将它的元数据添加为列表内的第一个字典。 - -## OpenAPI URL - -默认情况下,OpenAPI 模式服务于 `/openapi.json`。 - -但是你可以通过参数 `openapi_url` 对其进行配置。 - -例如,将其设置为服务于 `/api/v1/openapi.json`: - -```Python hl_lines="3" -{!../../../docs_src/metadata/tutorial002.py!} -``` - -如果你想完全禁用 OpenAPI 模式,可以将其设置为 `openapi_url=None`,这样也会禁用使用它的文档用户界面。 - -## 文档 URLs - -你可以配置两个文档用户界面,包括: - -* **Swagger UI**:服务于 `/docs`。 - * 可以使用参数 `docs_url` 设置它的 URL。 - * 可以通过设置 `docs_url=None` 禁用它。 -* ReDoc:服务于 `/redoc`。 - * 可以使用参数 `redoc_url` 设置它的 URL。 - * 可以通过设置 `redoc_url=None` 禁用它。 - -例如,设置 Swagger UI 服务于 `/documentation` 并禁用 ReDoc: - -```Python hl_lines="3" -{!../../../docs_src/metadata/tutorial003.py!} -``` +# Metadata and Docs URLs + +You can customize several metadata configurations in your **FastAPI** application. + +## Metadata for API + +You can set the following fields that are used in the OpenAPI specification and the automatic API docs UIs: + +| Parameter | Type | Description | +| ------------------ | -------- | --------------------------------------------------------------------------------------------------------- | +| `title` | `str` | The title of the API. | +| `summary` | `str` | A short summary of the API. Available since OpenAPI 3.1.0, FastAPI 0.99.0. | +| `description` | `str` | A short description of the API. It can use Markdown. | +| `version` | `string` | The version of the API. This is the version of your own application, not of OpenAPI. For example `2.5.0`. | +| `terms_of_service` | `str` | A URL to the Terms of Service for the API. If provided, this has to be a URL. | +| `contact` | `dict` | The contact information for the exposed API. It can contain several fields.
contact fields
ParameterTypeDescription
namestrThe identifying name of the contact person/organization.
urlstrThe URL pointing to the contact information. MUST be in the format of a URL.
emailstrThe email address of the contact person/organization. MUST be in the format of an email address.
| +| `license_info` | `dict` | The license information for the exposed API. It can contain several fields.
license_info fields
ParameterTypeDescription
namestrREQUIRED (if a license_info is set). The license name used for the API.
identifierstrAn SPDX license expression for the API. The identifier field is mutually exclusive of the url field. Available since OpenAPI 3.1.0, FastAPI 0.99.0.
urlstrA URL to the license used for the API. MUST be in the format of a URL.
| + +You can set them as follows: + +```Python hl_lines="3-16 19-32" +{!../../../docs_src/metadata/tutorial001.py!} +``` + +!!! tip + You can write Markdown in the `description` field and it will be rendered in the output. + +With this configuration, the automatic API docs would look like: + + + +## License identifier + +Since OpenAPI 3.1.0 and FastAPI 0.99.0, you can also set the `license_info` with an `identifier` instead of a `url`. + +For example: + +```Python hl_lines="31" +{!../../../docs_src/metadata/tutorial001_1.py!} +``` + +## Metadata for tags + +You can also add additional metadata for the different tags used to group your path operations with the parameter `openapi_tags`. + +It takes a list containing one dictionary for each tag. + +Each dictionary can contain: + +* `name` (**required**): a `str` with the same tag name you use in the `tags` parameter in your *path operations* and `APIRouter`s. +* `description`: a `str` with a short description for the tag. It can have Markdown and will be shown in the docs UI. +* `externalDocs`: a `dict` describing external documentation with: + * `description`: a `str` with a short description for the external docs. + * `url` (**required**): a `str` with the URL for the external documentation. + +### Create metadata for tags + +Let's try that in an example with tags for `users` and `items`. + +Create metadata for your tags and pass it to the `openapi_tags` parameter: + +```Python hl_lines="3-16 18" +{!../../../docs_src/metadata/tutorial004.py!} +``` + +Notice that you can use Markdown inside of the descriptions, for example "login" will be shown in bold (**login**) and "fancy" will be shown in italics (_fancy_). + +!!! tip + You don't have to add metadata for all the tags that you use. + +### Use your tags + +Use the `tags` parameter with your *path operations* (and `APIRouter`s) to assign them to different tags: + +```Python hl_lines="21 26" +{!../../../docs_src/metadata/tutorial004.py!} +``` + +!!! info + Read more about tags in [Path Operation Configuration](../path-operation-configuration/#tags){.internal-link target=_blank}. + +### Check the docs + +Now, if you check the docs, they will show all the additional metadata: + + + +### Order of tags + +The order of each tag metadata dictionary also defines the order shown in the docs UI. + +For example, even though `users` would go after `items` in alphabetical order, it is shown before them, because we added their metadata as the first dictionary in the list. + +## OpenAPI URL + +By default, the OpenAPI schema is served at `/openapi.json`. + +But you can configure it with the parameter `openapi_url`. + +For example, to set it to be served at `/api/v1/openapi.json`: + +```Python hl_lines="3" +{!../../../docs_src/metadata/tutorial002.py!} +``` + +If you want to disable the OpenAPI schema completely you can set `openapi_url=None`, that will also disable the documentation user interfaces that use it. + +## Docs URLs + +You can configure the two documentation user interfaces included: + +* **Swagger UI**: served at `/docs`. + * You can set its URL with the parameter `docs_url`. + * You can disable it by setting `docs_url=None`. +* **ReDoc**: served at `/redoc`. + * You can set its URL with the parameter `redoc_url`. + * You can disable it by setting `redoc_url=None`. + +For example, to set Swagger UI to be served at `/documentation` and disable ReDoc: + +```Python hl_lines="3" +{!../../../docs_src/metadata/tutorial003.py!} +``` From 1bf9c82b4a30ff45bc3575996253af217a4a74fe Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:44 +0800 Subject: [PATCH 084/163] New translations middleware.md (Chinese Simplified) --- docs/zh/docs/tutorial/middleware.md | 64 ++++++++++++++--------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/docs/zh/docs/tutorial/middleware.md b/docs/zh/docs/tutorial/middleware.md index c9a7e7725a120..3c6868fe4de73 100644 --- a/docs/zh/docs/tutorial/middleware.md +++ b/docs/zh/docs/tutorial/middleware.md @@ -1,61 +1,61 @@ -# 中间件 +# Middleware -你可以向 **FastAPI** 应用添加中间件. +You can add middleware to **FastAPI** applications. -"中间件"是一个函数,它在每个**请求**被特定的*路径操作*处理之前,以及在每个**响应**返回之前工作. +A "middleware" is a function that works with every **request** before it is processed by any specific *path operation*. And also with every **response** before returning it. -* 它接收你的应用程序的每一个**请求**. -* 然后它可以对这个**请求**做一些事情或者执行任何需要的代码. -* 然后它将**请求**传递给应用程序的其他部分 (通过某种*路径操作*). -* 然后它获取应用程序生产的**响应** (通过某种*路径操作*). -* 它可以对该**响应**做些什么或者执行任何需要的代码. -* 然后它返回这个 **响应**. +* It takes each **request** that comes to your application. +* It can then do something to that **request** or run any needed code. +* Then it passes the **request** to be processed by the rest of the application (by some *path operation*). +* It then takes the **response** generated by the application (by some *path operation*). +* It can do something to that **response** or run any needed code. +* Then it returns the **response**. -!!! note "技术细节" - 如果你使用了 `yield` 关键字依赖, 依赖中的退出代码将在执行中间件*后*执行. +!!! note "Technical Details" + If you have dependencies with `yield`, the exit code will run *after* the middleware. - 如果有任何后台任务(稍后记录), 它们将在执行中间件*后*运行. + If there were any background tasks (documented later), they will run *after* all the middleware. -## 创建中间件 +## Create a middleware -要创建中间件你可以在函数的顶部使用装饰器 `@app.middleware("http")`. +To create a middleware you use the decorator `@app.middleware("http")` on top of a function. -中间件参数接收如下参数: +The middleware function receives: -* `request`. -* 一个函数 `call_next` 它将接收 `request` 作为参数. - * 这个函数将 `request` 传递给相应的 *路径操作*. - * 然后它将返回由相应的*路径操作*生成的 `response`. -* 然后你可以在返回 `response` 前进一步修改它. +* The `request`. +* A function `call_next` that will receive the `request` as a parameter. + * This function will pass the `request` to the corresponding *path operation*. + * Then it returns the `response` generated by the corresponding *path operation*. +* You can then modify further the `response` before returning it. ```Python hl_lines="8-9 11 14" {!../../../docs_src/middleware/tutorial001.py!} ``` !!! tip - 请记住可以 用'X-' 前缀添加专有自定义请求头. + Have in mind that custom proprietary headers can be added using the 'X-' prefix. - 但是如果你想让浏览器中的客户端看到你的自定义请求头, 你需要把它们加到 CORS 配置 ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) 的 `expose_headers` 参数中,在 Starlette's CORS docs文档中. + But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your CORS configurations ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) using the parameter `expose_headers` documented in Starlette's CORS docs. -!!! note "技术细节" - 你也可以使用 `from starlette.requests import Request`. +!!! note "Technical Details" + You could also use `from starlette.requests import Request`. - **FastAPI** 为了开发者方便提供了该对象. 但其实它直接来自于 Starlette. + **FastAPI** provides it as a convenience for you, the developer. But it comes directly from Starlette. -### 在 `response` 的前和后 +### Before and after the `response` -在任何*路径操作*收到`request`前,可以添加要和请求一起运行的代码. +You can add code to be run with the `request`, before any *path operation* receives it. -也可以在*响应*生成但是返回之前添加代码. +And also after the `response` is generated, before returning it. -例如你可以添加自定义请求头 `X-Process-Time` 包含以秒为单位的接收请求和生成响应的时间: +For example, you could add a custom header `X-Process-Time` containing the time in seconds that it took to process the request and generate a response: ```Python hl_lines="10 12-13" {!../../../docs_src/middleware/tutorial001.py!} ``` -## 其他中间件 +## Other middlewares -你可以稍后在 [Advanced User Guide: Advanced Middleware](../advanced/middleware.md){.internal-link target=_blank}阅读更多关于中间件的教程. +You can later read more about other middlewares in the [Advanced User Guide: Advanced Middleware](../advanced/middleware.md){.internal-link target=_blank}. -你将在下一节中学习如何使用中间件处理 CORS . +You will read about how to handle CORS with a middleware in the next section. From 12374d8da47ccdd1de2ccbd447c44544f4267b54 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:44 +0800 Subject: [PATCH 085/163] New translations path-operation-configuration.md (Chinese Simplified) --- .../tutorial/path-operation-configuration.md | 184 +++++++++++++----- 1 file changed, 131 insertions(+), 53 deletions(-) diff --git a/docs/zh/docs/tutorial/path-operation-configuration.md b/docs/zh/docs/tutorial/path-operation-configuration.md index f79b0e692d8cd..ee3b5cd0ad9f6 100644 --- a/docs/zh/docs/tutorial/path-operation-configuration.md +++ b/docs/zh/docs/tutorial/path-operation-configuration.md @@ -1,101 +1,179 @@ -# 路径操作配置 +# Path Operation Configuration -*路径操作装饰器*支持多种配置参数。 +There are several parameters that you can pass to your *path operation decorator* to configure it. -!!! warning "警告" +!!! warning + Notice that these parameters are passed directly to the *path operation decorator*, not to your *path operation function*. - 注意:以下参数应直接传递给**路径操作装饰器**,不能传递给*路径操作函数*。 +## Response Status Code -## `status_code` 状态码 +You can define the (HTTP) `status_code` to be used in the response of your *path operation*. -`status_code` 用于定义*路径操作*响应中的 HTTP 状态码。 +You can pass directly the `int` code, like `404`. -可以直接传递 `int` 代码, 比如 `404`。 +But if you don't remember what each number code is for, you can use the shortcut constants in `status`: -如果记不住数字码的涵义,也可以用 `status` 的快捷常量: +=== "Python 3.10+" -```Python hl_lines="3 17" -{!../../../docs_src/path_operation_configuration/tutorial001.py!} -``` + ```Python hl_lines="1 15" + {!> ../../../docs_src/path_operation_configuration/tutorial001_py310.py!} + ``` -状态码在响应中使用,并会被添加到 OpenAPI 概图。 +=== "Python 3.9+" -!!! note "技术细节" + ```Python hl_lines="3 17" + {!> ../../../docs_src/path_operation_configuration/tutorial001_py39.py!} + ``` - 也可以使用 `from starlette import status` 导入状态码。 +=== "Python 3.6+" - **FastAPI** 的`fastapi.status` 和 `starlette.status` 一样,只是快捷方式。实际上,`fastapi.status` 直接继承自 Starlette。 + ```Python hl_lines="3 17" + {!> ../../../docs_src/path_operation_configuration/tutorial001.py!} + ``` -## `tags` 参数 +That status code will be used in the response and will be added to the OpenAPI schema. -`tags` 参数的值是由 `str` 组成的 `list` (一般只有一个 `str` ),`tags` 用于为*路径操作*添加标签: +!!! note "Technical Details" + You could also use `from starlette import status`. -```Python hl_lines="17 22 27" -{!../../../docs_src/path_operation_configuration/tutorial002.py!} -``` + **FastAPI** provides the same `starlette.status` as `fastapi.status` just as a convenience for you, the developer. But it comes directly from Starlette. -OpenAPI 概图会自动添加标签,供 API 文档接口使用: +## Tags - +You can add tags to your *path operation*, pass the parameter `tags` with a `list` of `str` (commonly just one `str`): -## `summary` 和 `description` 参数 +=== "Python 3.10+" -路径装饰器还支持 `summary` 和 `description` 这两个参数: + ```Python hl_lines="15 20 25" + {!> ../../../docs_src/path_operation_configuration/tutorial002_py310.py!} + ``` -```Python hl_lines="20-21" -{!../../../docs_src/path_operation_configuration/tutorial003.py!} -``` +=== "Python 3.9+" -## 文档字符串(`docstring`) + ```Python hl_lines="17 22 27" + {!> ../../../docs_src/path_operation_configuration/tutorial002_py39.py!} + ``` -描述内容比较长且占用多行时,可以在函数的 docstring 中声明*路径操作*的描述,**FastAPI** 支持从文档字符串中读取描述内容。 +=== "Python 3.6+" -文档字符串支持 Markdown,能正确解析和显示 Markdown 的内容,但要注意文档字符串的缩进。 + ```Python hl_lines="17 22 27" + {!> ../../../docs_src/path_operation_configuration/tutorial002.py!} + ``` -```Python hl_lines="19-27" -{!../../../docs_src/path_operation_configuration/tutorial004.py!} -``` +They will be added to the OpenAPI schema and used by the automatic documentation interfaces: -下图为 Markdown 文本在 API 文档中的显示效果: + - +### Tags with Enums -## 响应描述 +If you have a big application, you might end up accumulating **several tags**, and you would want to make sure you always use the **same tag** for related *path operations*. -`response_description` 参数用于定义响应的描述说明: +In these cases, it could make sense to store the tags in an `Enum`. -```Python hl_lines="21" -{!../../../docs_src/path_operation_configuration/tutorial005.py!} +**FastAPI** supports that the same way as with plain strings: + +```Python hl_lines="1 8-10 13 18" +{!../../../docs_src/path_operation_configuration/tutorial002b.py!} ``` -!!! info "说明" +## Summary and description + +You can add a `summary` and `description`: + +=== "Python 3.10+" + + ```Python hl_lines="18-19" + {!> ../../../docs_src/path_operation_configuration/tutorial003_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="20-21" + {!> ../../../docs_src/path_operation_configuration/tutorial003_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="20-21" + {!> ../../../docs_src/path_operation_configuration/tutorial003.py!} + ``` + +## Description from docstring + +As descriptions tend to be long and cover multiple lines, you can declare the *path operation* description in the function docstring and **FastAPI** will read it from there. + +You can write Markdown in the docstring, it will be interpreted and displayed correctly (taking into account docstring indentation). + +=== "Python 3.10+" + + ```Python hl_lines="17-25" + {!> ../../../docs_src/path_operation_configuration/tutorial004_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="19-27" + {!> ../../../docs_src/path_operation_configuration/tutorial004_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="19-27" + {!> ../../../docs_src/path_operation_configuration/tutorial004.py!} + ``` + +It will be used in the interactive docs: + + + +## Response description + +You can specify the response description with the parameter `response_description`: + +=== "Python 3.10+" + + ```Python hl_lines="19" + {!> ../../../docs_src/path_operation_configuration/tutorial005_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="21" + {!> ../../../docs_src/path_operation_configuration/tutorial005_py39.py!} + ``` + +=== "Python 3.6+" - 注意,`response_description` 只用于描述响应,`description` 一般则用于描述*路径操作*。 + ```Python hl_lines="21" + {!> ../../../docs_src/path_operation_configuration/tutorial005.py!} + ``` -!!! check "检查" +!!! info + Notice that `response_description` refers specifically to the response, the `description` refers to the *path operation* in general. - OpenAPI 规定每个*路径操作*都要有响应描述。 +!!! check + OpenAPI specifies that each *path operation* requires a response description. - 如果没有定义响应描述,**FastAPI** 则自动生成内容为 "Successful response" 的响应描述。 + So, if you don't provide one, **FastAPI** will automatically generate one of "Successful response". - + -## 弃用*路径操作* +## Deprecate a *path operation* -`deprecated` 参数可以把*路径操作*标记为弃用,无需直接删除: +If you need to mark a *path operation* as deprecated, but without removing it, pass the parameter `deprecated`: ```Python hl_lines="16" {!../../../docs_src/path_operation_configuration/tutorial006.py!} ``` -API 文档会把该路径操作标记为弃用: +It will be clearly marked as deprecated in the interactive docs: - + -下图显示了正常*路径操作*与弃用*路径操作* 的区别: +Check how deprecated and non-deprecated *path operations* look like: - + -## 小结 +## Recap -通过传递参数给*路径操作装饰器* ,即可轻松地配置*路径操作*、添加元数据。 +You can configure and add metadata for your *path operations* easily by passing parameters to the *path operation decorators*. From feaf33d2ed9e92f6c02ba45a45c6e62259f8d7d4 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:45 +0800 Subject: [PATCH 086/163] New translations path-params-numeric-validations.md (Chinese Simplified) --- .../path-params-numeric-validations.md | 308 ++++++++++++++---- 1 file changed, 240 insertions(+), 68 deletions(-) diff --git a/docs/zh/docs/tutorial/path-params-numeric-validations.md b/docs/zh/docs/tutorial/path-params-numeric-validations.md index 13512a08edf06..fd211224826c3 100644 --- a/docs/zh/docs/tutorial/path-params-numeric-validations.md +++ b/docs/zh/docs/tutorial/path-params-numeric-validations.md @@ -1,122 +1,294 @@ -# 路径参数和数值校验 +# Path Parameters and Numeric Validations -与使用 `Query` 为查询参数声明更多的校验和元数据的方式相同,你也可以使用 `Path` 为路径参数声明相同类型的校验和元数据。 +In the same way that you can declare more validations and metadata for query parameters with `Query`, you can declare the same type of validations and metadata for path parameters with `Path`. -## 导入 Path +## Import Path -首先,从 `fastapi` 导入 `Path`: +First, import `Path` from `fastapi`, and import `Annotated`: -```Python hl_lines="1" -{!../../../docs_src/path_params_numeric_validations/tutorial001.py!} -``` +=== "Python 3.10+" -## 声明元数据 + ```Python hl_lines="1 3" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py!} + ``` -你可以声明与 `Query` 相同的所有参数。 +=== "Python 3.9+" -例如,要声明路径参数 `item_id`的 `title` 元数据值,你可以输入: + ```Python hl_lines="1 3" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py39.py!} + ``` -```Python hl_lines="8" -{!../../../docs_src/path_params_numeric_validations/tutorial001.py!} -``` +=== "Python 3.6+" + + ```Python hl_lines="3-4" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="1" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="3" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!} + ``` + +!!! info + FastAPI added support for `Annotated` (and started recommending it) in version 0.95.0. + + If you have an older version, you would get errors when trying to use `Annotated`. + + Make sure you [Upgrade the FastAPI version](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} to at least 0.95.1 before using `Annotated`. + +## Declare metadata + +You can declare all the same parameters as for `Query`. + +For example, to declare a `title` metadata value for the path parameter `item_id` you can type: + +=== "Python 3.10+" + + ```Python hl_lines="10" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="10" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="11" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="8" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="10" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!} + ``` !!! note - 路径参数总是必需的,因为它必须是路径的一部分。 + A path parameter is always required as it has to be part of the path. - 所以,你应该在声明时使用 `...` 将其标记为必需参数。 + So, you should declare it with `...` to mark it as required. + + Nevertheless, even if you declared it with `None` or set a default value, it would not affect anything, it would still be always required. - 然而,即使你使用 `None` 声明路径参数或设置一个其他默认值也不会有任何影响,它依然会是必需参数。 +## Order the parameters as you need -## 按需对参数排序 +!!! tip + This is probably not as important or necessary if you use `Annotated`. -假设你想要声明一个必需的 `str` 类型查询参数 `q`。 +Let's say that you want to declare the query parameter `q` as a required `str`. -而且你不需要为该参数声明任何其他内容,所以实际上你并不需要使用 `Query`。 +And you don't need to declare anything else for that parameter, so you don't really need to use `Query`. -但是你仍然需要使用 `Path` 来声明路径参数 `item_id`。 +But you still need to use `Path` for the `item_id` path parameter. And you don't want to use `Annotated` for some reason. -如果你将带有「默认值」的参数放在没有「默认值」的参数之前,Python 将会报错。 +Python will complain if you put a value with a "default" before a value that doesn't have a "default". -但是你可以对其重新排序,并将不带默认值的值(查询参数 `q`)放到最前面。 +But you can re-order them, and have the value without a default (the query parameter `q`) first. -对 **FastAPI** 来说这无关紧要。它将通过参数的名称、类型和默认值声明(`Query`、`Path` 等)来检测参数,而不在乎参数的顺序。 +It doesn't matter for **FastAPI**. It will detect the parameters by their names, types and default declarations (`Query`, `Path`, etc), it doesn't care about the order. -因此,你可以将函数声明为: +So, you can declare your function as: -```Python hl_lines="7" -{!../../../docs_src/path_params_numeric_validations/tutorial002.py!} -``` +=== "Python 3.6 non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7" + {!> ../../../docs_src/path_params_numeric_validations/tutorial002.py!} + ``` + +But have in mind that if you use `Annotated`, you won't have this problem, it won't matter as you're not using the function parameter default values for `Query()` or `Path()`. + +=== "Python 3.9+" -## 按需对参数排序的技巧 + ```Python hl_lines="10" + {!> ../../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py!} + ``` -如果你想不使用 `Query` 声明没有默认值的查询参数 `q`,同时使用 `Path` 声明路径参数 `item_id`,并使它们的顺序与上面不同,Python 对此有一些特殊的语法。 +=== "Python 3.6+" -传递 `*` 作为函数的第一个参数。 + ```Python hl_lines="9" + {!> ../../../docs_src/path_params_numeric_validations/tutorial002_an.py!} + ``` -Python 不会对该 `*` 做任何事情,但是它将知道之后的所有参数都应作为关键字参数(键值对),也被称为 kwargs,来调用。即使它们没有默认值。 +## Order the parameters as you need, tricks + +!!! tip + This is probably not as important or necessary if you use `Annotated`. + +Here's a **small trick** that can be handy, but you won't need it often. + +If you want to: + +* declare the `q` query parameter without a `Query` nor any default value +* declare the path parameter `item_id` using `Path` +* have them in a different order +* not use `Annotated` + +...Python has a little special syntax for that. + +Pass `*`, as the first parameter of the function. + +Python won't do anything with that `*`, but it will know that all the following parameters should be called as keyword arguments (key-value pairs), also known as kwargs. Even if they don't have a default value. ```Python hl_lines="7" {!../../../docs_src/path_params_numeric_validations/tutorial003.py!} ``` -## 数值校验:大于等于 +### Better with `Annotated` -使用 `Query` 和 `Path`(以及你将在后面看到的其他类)可以声明字符串约束,但也可以声明数值约束。 +Have in mind that if you use `Annotated`, as you are not using function parameter default values, you won't have this problem, and you probably won't need to use `*`. -像下面这样,添加 `ge=1` 后,`item_id` 将必须是一个大于(`g`reater than)或等于(`e`qual)`1` 的整数。 +=== "Python 3.9+" -```Python hl_lines="8" -{!../../../docs_src/path_params_numeric_validations/tutorial004.py!} -``` + ```Python hl_lines="10" + {!> ../../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py!} + ``` -## 数值校验:大于和小于等于 +=== "Python 3.6+" -同样的规则适用于: + ```Python hl_lines="9" + {!> ../../../docs_src/path_params_numeric_validations/tutorial003_an.py!} + ``` -* `gt`:大于(`g`reater `t`han) -* `le`:小于等于(`l`ess than or `e`qual) +## Number validations: greater than or equal -```Python hl_lines="9" -{!../../../docs_src/path_params_numeric_validations/tutorial005.py!} -``` +With `Query` and `Path` (and others you'll see later) you can declare number constraints. -## 数值校验:浮点数、大于和小于 +Here, with `ge=1`, `item_id` will need to be an integer number "`g`reater than or `e`qual" to `1`. -数值校验同样适用于 `float` 值。 +=== "Python 3.9+" -能够声明 gt 而不仅仅是 ge 在这个前提下变得重要起来。例如,你可以要求一个值必须大于 `0`,即使它小于 `1`。 + ```Python hl_lines="10" + {!> ../../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py!} + ``` -因此,`0.5` 将是有效值。但是 `0.0`或 `0` 不是。 +=== "Python 3.6+" -对于 lt 也是一样的。 + ```Python hl_lines="9" + {!> ../../../docs_src/path_params_numeric_validations/tutorial004_an.py!} + ``` -```Python hl_lines="11" -{!../../../docs_src/path_params_numeric_validations/tutorial006.py!} -``` +=== "Python 3.6+ non-Annotated" -## 总结 + !!! tip + Prefer to use the `Annotated` version if possible. -你能够以与 [查询参数和字符串校验](query-params-str-validations.md){.internal-link target=_blank} 相同的方式使用 `Query`、`Path`(以及其他你还没见过的类)声明元数据和字符串校验。 + ```Python hl_lines="8" + {!> ../../../docs_src/path_params_numeric_validations/tutorial004.py!} + ``` -而且你还可以声明数值校验: +## Number validations: greater than and less than or equal -* `gt`:大于(`g`reater `t`han) -* `ge`:大于等于(`g`reater than or `e`qual) -* `lt`:小于(`l`ess `t`han) -* `le`:小于等于(`l`ess than or `e`qual) +The same applies for: -!!! info - `Query`、`Path` 以及你后面会看到的其他类继承自一个共同的 `Param` 类(不需要直接使用它)。 +* `gt`: `g`reater `t`han +* `le`: `l`ess than or `e`qual + +=== "Python 3.9+" + + ```Python hl_lines="10" + {!> ../../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9" + {!> ../../../docs_src/path_params_numeric_validations/tutorial005_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. - 而且它们都共享相同的所有你已看到并用于添加额外校验和元数据的参数。 + ```Python hl_lines="9" + {!> ../../../docs_src/path_params_numeric_validations/tutorial005.py!} + ``` -!!! note "技术细节" - 当你从 `fastapi` 导入 `Query`、`Path` 和其他同类对象时,它们实际上是函数。 +## Number validations: floats, greater than and less than - 当被调用时,它们返回同名类的实例。 +Number validations also work for `float` values. + +Here's where it becomes important to be able to declare gt and not just ge. As with it you can require, for example, that a value must be greater than `0`, even if it is less than `1`. + +So, `0.5` would be a valid value. But `0.0` or `0` would not. + +And the same for lt. + +=== "Python 3.9+" + + ```Python hl_lines="13" + {!> ../../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="12" + {!> ../../../docs_src/path_params_numeric_validations/tutorial006_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="11" + {!> ../../../docs_src/path_params_numeric_validations/tutorial006.py!} + ``` + +## Recap + +With `Query`, `Path` (and others you haven't seen yet) you can declare metadata and string validations in the same ways as with [Query Parameters and String Validations](query-params-str-validations.md){.internal-link target=_blank}. + +And you can also declare numeric validations: + +* `gt`: `g`reater `t`han +* `ge`: `g`reater than or `e`qual +* `lt`: `l`ess `t`han +* `le`: `l`ess than or `e`qual + +!!! info + `Query`, `Path`, and other classes you will see later are subclasses of a common `Param` class. - 如此,你导入 `Query` 这个函数。当你调用它时,它将返回一个同样命名为 `Query` 的类的实例。 + All of them share the same parameters for additional validation and metadata you have seen. - 因为使用了这些函数(而不是直接使用类),所以你的编辑器不会标记有关其类型的错误。 +!!! note "Technical Details" + When you import `Query`, `Path` and others from `fastapi`, they are actually functions. - 这样,你可以使用常规的编辑器和编码工具,而不必添加自定义配置来忽略这些错误。 + That when called, return instances of classes of the same name. + + So, you import `Query`, which is a function. And when you call it, it returns an instance of a class also named `Query`. + + These functions are there (instead of just using the classes directly) so that your editor doesn't mark errors about their types. + + That way you can use your normal editor and coding tools without having to add custom configurations to disregard those errors. From 43e63593c8d93f00472e498671a59b10251891f5 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:46 +0800 Subject: [PATCH 087/163] New translations path-params.md (Chinese Simplified) --- docs/zh/docs/tutorial/path-params.md | 190 +++++++++++++++------------ 1 file changed, 104 insertions(+), 86 deletions(-) diff --git a/docs/zh/docs/tutorial/path-params.md b/docs/zh/docs/tutorial/path-params.md index 1b428d6627112..6b14a69a6f553 100644 --- a/docs/zh/docs/tutorial/path-params.md +++ b/docs/zh/docs/tutorial/path-params.md @@ -1,48 +1,48 @@ -# 路径参数 +# Path Parameters -你可以使用与 Python 格式化字符串相同的语法来声明路径"参数"或"变量": +You can declare path "parameters" or "variables" with the same syntax used by Python format strings: ```Python hl_lines="6-7" {!../../../docs_src/path_params/tutorial001.py!} ``` -路径参数 `item_id` 的值将作为参数 `item_id` 传递给你的函数。 +The value of the path parameter `item_id` will be passed to your function as the argument `item_id`. -所以,如果你运行示例并访问 http://127.0.0.1:8000/items/foo,将会看到如下响应: +So, if you run this example and go to http://127.0.0.1:8000/items/foo, you will see a response of: ```JSON {"item_id":"foo"} ``` -## 有类型的路径参数 +## Path parameters with types -你可以使用标准的 Python 类型标注为函数中的路径参数声明类型。 +You can declare the type of a path parameter in the function, using standard Python type annotations: ```Python hl_lines="7" {!../../../docs_src/path_params/tutorial002.py!} ``` -在这个例子中,`item_id` 被声明为 `int` 类型。 +In this case, `item_id` is declared to be an `int`. !!! check - 这将为你的函数提供编辑器支持,包括错误检查、代码补全等等。 + This will give you editor support inside of your function, with error checks, completion, etc. -## 数据转换 +## Data conversion -如果你运行示例并打开浏览器访问 http://127.0.0.1:8000/items/3,将得到如下响应: +If you run this example and open your browser at http://127.0.0.1:8000/items/3, you will see a response of: ```JSON {"item_id":3} ``` !!! check - 注意函数接收(并返回)的值为 3,是一个 Python `int` 值,而不是字符串 `"3"`。 + Notice that the value your function received (and returned) is `3`, as a Python `int`, not a string `"3"`. - 所以,**FastAPI** 通过上面的类型声明提供了对请求的自动"解析"。 + So, with that type declaration, **FastAPI** gives you automatic request "parsing". -## 数据校验 +## Data validation -但如果你通过浏览器访问 http://127.0.0.1:8000/items/foo,你会看到一个清晰可读的 HTTP 错误: +But if you go to the browser at http://127.0.0.1:8000/items/foo, you will see a nice HTTP error of: ```JSON { @@ -59,176 +59,194 @@ } ``` -因为路径参数 `item_id` 传入的值为 `"foo"`,它不是一个 `int`。 +because the path parameter `item_id` had a value of `"foo"`, which is not an `int`. -如果你提供的是 `float` 而非整数也会出现同样的错误,比如: http://127.0.0.1:8000/items/4.2 +The same error would appear if you provided a `float` instead of an `int`, as in: http://127.0.0.1:8000/items/4.2 !!! check - 所以,通过同样的 Python 类型声明,**FastAPI** 提供了数据校验功能。 + So, with the same Python type declaration, **FastAPI** gives you data validation. - 注意上面的错误同样清楚地指出了校验未通过的具体原因。 + Notice that the error also clearly states exactly the point where the validation didn't pass. + + This is incredibly helpful while developing and debugging code that interacts with your API. - 在开发和调试与你的 API 进行交互的代码时,这非常有用。 +## Documentation -## 文档 +And when you open your browser at http://127.0.0.1:8000/docs, you will see an automatic, interactive, API documentation like: -当你打开浏览器访问 http://127.0.0.1:8000/docs,你将看到自动生成的交互式 API 文档: - - + !!! check - 再一次,还是通过相同的 Python 类型声明,**FastAPI** 为你提供了自动生成的交互式文档(集成 Swagger UI)。 + Again, just with that same Python type declaration, **FastAPI** gives you automatic, interactive documentation (integrating Swagger UI). - 注意这里的路径参数被声明为一个整数。 + Notice that the path parameter is declared to be an integer. -## 基于标准的好处:可选文档 +## Standards-based benefits, alternative documentation -由于生成的 API 模式来自于 OpenAPI 标准,所以有很多工具与其兼容。 +And because the generated schema is from the OpenAPI standard, there are many compatible tools. -正因如此,**FastAPI** 内置了一个可选的 API 文档(使用 Redoc): +Because of this, **FastAPI** itself provides an alternative API documentation (using ReDoc), which you can access at http://127.0.0.1:8000/redoc: - + -同样的,还有很多其他兼容的工具,包括适用于多种语言的代码生成工具。 +The same way, there are many compatible tools. Including code generation tools for many languages. ## Pydantic -所有的数据校验都由 Pydantic 在幕后完成,所以你可以从它所有的优点中受益。并且你知道它在这方面非常胜任。 +All the data validation is performed under the hood by Pydantic, so you get all the benefits from it. And you know you are in good hands. + +You can use the same type declarations with `str`, `float`, `bool` and many other complex data types. -你可以使用同样的类型声明来声明 `str`、`float`、`bool` 以及许多其他的复合数据类型。 +Several of these are explored in the next chapters of the tutorial. -本教程的下一章节将探讨其中的一些内容。 +## Order matters -## 顺序很重要 +When creating *path operations*, you can find situations where you have a fixed path. -在创建*路径操作*时,你会发现有些情况下路径是固定的。 +Like `/users/me`, let's say that it's to get data about the current user. -比如 `/users/me`,我们假设它用来获取关于当前用户的数据. +And then you can also have a path `/users/{user_id}` to get data about a specific user by some user ID. -然后,你还可以使用路径 `/users/{user_id}` 来通过用户 ID 获取关于特定用户的数据。 +Because *path operations* are evaluated in order, you need to make sure that the path for `/users/me` is declared before the one for `/users/{user_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` 参数。 +Otherwise, the path for `/users/{user_id}` would match also for `/users/me`, "thinking" that it's receiving a parameter `user_id` with a value of `"me"`. + +Similarly, you cannot redefine a path operation: + +```Python hl_lines="6 11" +{!../../../docs_src/path_params/tutorial003b.py!} +``` + +The first one will always be used since the path matches first. -## 预设值 +## Predefined values -如果你有一个接收路径参数的路径操作,但你希望预先设定可能的有效参数值,则可以使用标准的 Python `Enum` 类型。 +If you have a *path operation* that receives a *path parameter*, but you want the possible valid *path parameter* values to be predefined, you can use a standard Python `Enum`. -### 创建一个 `Enum` 类 +### Create an `Enum` class -导入 `Enum` 并创建一个继承自 `str` 和 `Enum` 的子类。 +Import `Enum` and create a sub-class that inherits from `str` and from `Enum`. -通过从 `str` 继承,API 文档将能够知道这些值必须为 `string` 类型并且能够正确地展示出来。 +By inheriting from `str` the API docs will be able to know that the values must be of type `string` and will be able to render correctly. -然后创建具有固定值的类属性,这些固定值将是可用的有效值: +Then create class attributes with fixed values, which will be the available valid values: ```Python hl_lines="1 6-9" {!../../../docs_src/path_params/tutorial005.py!} ``` !!! info - 枚举(或 enums)从 3.4 版本起在 Python 中可用。 + Enumerations (or enums) are available in Python since version 3.4. !!! tip - 如果你想知道,"AlexNet"、"ResNet" 和 "LeNet" 只是机器学习中的模型名称。 + If you are wondering, "AlexNet", "ResNet", and "LeNet" are just names of Machine Learning models. -### 声明*路径参数* +### Declare a *path parameter* -然后使用你定义的枚举类(`ModelName`)创建一个带有类型标注的*路径参数*: +Then create a *path parameter* with a type annotation using the enum class you created (`ModelName`): ```Python hl_lines="16" {!../../../docs_src/path_params/tutorial005.py!} ``` -### 查看文档 +### Check the docs -因为已经指定了*路径参数*的可用值,所以交互式文档可以恰当地展示它们: +Because the available values for the *path parameter* are predefined, the interactive docs can show them nicely: - + -### 使用 Python *枚举类型* +### Working with Python *enumerations* -*路径参数*的值将是一个*枚举成员*。 +The value of the *path parameter* will be an *enumeration member*. -#### 比较*枚举成员* +#### Compare *enumeration members* -你可以将它与你创建的枚举类 `ModelName` 中的*枚举成员*进行比较: +You can compare it with the *enumeration member* in your created enum `ModelName`: ```Python hl_lines="17" {!../../../docs_src/path_params/tutorial005.py!} ``` -#### 获取*枚举值* +#### Get the *enumeration value* -你可以使用 `model_name.value` 或通常来说 `your_enum_member.value` 来获取实际的值(在这个例子中为 `str`): +You can get the actual value (a `str` in this case) using `model_name.value`, or in general, `your_enum_member.value`: -```Python hl_lines="19" +```Python hl_lines="20" {!../../../docs_src/path_params/tutorial005.py!} ``` !!! tip - 你也可以通过 `ModelName.lenet.value` 来获取值 `"lenet"`。 + You could also access the value `"lenet"` with `ModelName.lenet.value`. -#### 返回*枚举成员* +#### Return *enumeration members* -你可以从*路径操作*中返回*枚举成员*,即使嵌套在 JSON 结构中(例如一个 `dict` 中)。 +You can return *enum members* from your *path operation*, even nested in a JSON body (e.g. a `dict`). -在返回给客户端之前,它们将被转换为对应的值: +They will be converted to their corresponding values (strings in this case) before returning them to the client: -```Python hl_lines="18-21" +```Python hl_lines="18 21 23" {!../../../docs_src/path_params/tutorial005.py!} ``` -## 包含路径的路径参数 +In your client you will get a JSON response like: + +```JSON +{ + "model_name": "alexnet", + "message": "Deep Learning FTW!" +} +``` + +## Path parameters containing paths -假设你有一个*路径操作*,它的路径为 `/files/{file_path}`。 +Let's say you have a *path operation* with a path `/files/{file_path}`. -但是你需要 `file_path` 自身也包含*路径*,比如 `home/johndoe/myfile.txt`。 +But you need `file_path` itself to contain a *path*, like `home/johndoe/myfile.txt`. -因此,该文件的URL将类似于这样:`/files/home/johndoe/myfile.txt`。 +So, the URL for that file would be something like: `/files/home/johndoe/myfile.txt`. -### OpenAPI 支持 +### OpenAPI support -OpenAPI 不支持任何方式去声明*路径参数*以在其内部包含*路径*,因为这可能会导致难以测试和定义的情况出现。 +OpenAPI doesn't support a way to declare a *path parameter* to contain a *path* inside, as that could lead to scenarios that are difficult to test and define. -不过,你仍然可以通过 Starlette 的一个内部工具在 **FastAPI** 中实现它。 +Nevertheless, you can still do it in **FastAPI**, using one of the internal tools from Starlette. -而且文档依旧可以使用,但是不会添加任何该参数应包含路径的说明。 +And the docs would still work, although not adding any documentation telling that the parameter should contain a path. -### 路径转换器 +### Path convertor -你可以使用直接来自 Starlette 的选项来声明一个包含*路径*的*路径参数*: +Using an option directly from Starlette you can declare a *path parameter* containing a *path* using a URL like: ``` /files/{file_path:path} ``` -在这种情况下,参数的名称为 `file_path`,结尾部分的 `:path` 说明该参数应匹配任意的*路径*。 +In this case, the name of the parameter is `file_path`, and the last part, `:path`, tells it that the parameter should match any *path*. -因此,你可以这样使用它: +So, you can use it with: ```Python hl_lines="6" {!../../../docs_src/path_params/tutorial004.py!} ``` !!! tip - 你可能会需要参数包含 `/home/johndoe/myfile.txt`,以斜杠(`/`)开头。 + You could need the parameter to contain `/home/johndoe/myfile.txt`, with a leading slash (`/`). - 在这种情况下,URL 将会是 `/files//home/johndoe/myfile.txt`,在`files` 和 `home` 之间有一个双斜杠(`//`)。 + In that case, the URL would be: `/files//home/johndoe/myfile.txt`, with a double slash (`//`) between `files` and `home`. -## 总结 +## Recap -使用 **FastAPI**,通过简短、直观和标准的 Python 类型声明,你将获得: +With **FastAPI**, by using short, intuitive and standard Python type declarations, you get: -* 编辑器支持:错误检查,代码补全等 -* 数据 "解析" -* 数据校验 -* API 标注和自动生成的文档 +* Editor support: error checks, autocompletion, etc. +* Data "parsing" +* Data validation +* API annotation and automatic documentation -而且你只需要声明一次即可。 +And you only have to declare them once. -这可能是 **FastAPI** 与其他框架相比主要的明显优势(除了原始性能以外)。 +That's probably the main visible advantage of **FastAPI** compared to alternative frameworks (apart from the raw performance). From af859b4fb4ed14ba5ef55fd631a7b31b22bbda5b Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:47 +0800 Subject: [PATCH 088/163] New translations query-params-str-validations.md (Chinese Simplified) --- .../tutorial/query-params-str-validations.md | 913 +++++++++++++++--- 1 file changed, 770 insertions(+), 143 deletions(-) diff --git a/docs/zh/docs/tutorial/query-params-str-validations.md b/docs/zh/docs/tutorial/query-params-str-validations.md index 070074839f634..6905626cc983f 100644 --- a/docs/zh/docs/tutorial/query-params-str-validations.md +++ b/docs/zh/docs/tutorial/query-params-str-validations.md @@ -1,182 +1,598 @@ -# 查询参数和字符串校验 +# Query Parameters and String Validations -**FastAPI** 允许你为参数声明额外的信息和校验。 +**FastAPI** allows you to declare additional information and validation for your parameters. -让我们以下面的应用程序为例: +Let's take this application as example: -```Python hl_lines="7" -{!../../../docs_src/query_params_str_validations/tutorial001.py!} -``` +=== "Python 3.10+" -查询参数 `q` 的类型为 `str`,默认值为 `None`,因此它是可选的。 + ```Python hl_lines="7" + {!> ../../../docs_src/query_params_str_validations/tutorial001_py310.py!} + ``` -## 额外的校验 +=== "Python 3.6+" -我们打算添加约束条件:即使 `q` 是可选的,但只要提供了该参数,则该参数值**不能超过50个字符的长度**。 + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial001.py!} + ``` -### 导入 `Query` +The query parameter `q` is of type `Union[str, None]` (or `str | None` in Python 3.10), that means that it's of type `str` but could also be `None`, and indeed, the default value is `None`, so FastAPI will know it's not required. -为此,首先从 `fastapi` 导入 `Query`: +!!! note + FastAPI will know that the value of `q` is not required because of the default value `= None`. -```Python hl_lines="1" -{!../../../docs_src/query_params_str_validations/tutorial002.py!} -``` + The `Union` in `Union[str, None]` will allow your editor to give you better support and detect errors. -## 使用 `Query` 作为默认值 +## Additional validation -现在,将 `Query` 用作查询参数的默认值,并将它的 `max_length` 参数设置为 50: +We are going to enforce that even though `q` is optional, whenever it is provided, **its length doesn't exceed 50 characters**. -```Python hl_lines="9" -{!../../../docs_src/query_params_str_validations/tutorial002.py!} -``` +### Import `Query` and `Annotated` + +To achieve that, first import: + +* `Query` from `fastapi` +* `Annotated` from `typing` (or from `typing_extensions` in Python below 3.9) + +=== "Python 3.10+" + + In Python 3.9 or above, `Annotated` is part of the standard library, so you can import it from `typing`. + + ```Python hl_lines="1 3" + {!> ../../../docs_src/query_params_str_validations/tutorial002_an_py310.py!} + ``` + +=== "Python 3.6+" + + In versions of Python below Python 3.9 you import `Annotated` from `typing_extensions`. + + It will already be installed with FastAPI. + + ```Python hl_lines="3-4" + {!> ../../../docs_src/query_params_str_validations/tutorial002_an.py!} + ``` + +!!! info + FastAPI added support for `Annotated` (and started recommending it) in version 0.95.0. + + If you have an older version, you would get errors when trying to use `Annotated`. + + Make sure you [Upgrade the FastAPI version](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} to at least 0.95.1 before using `Annotated`. + +## Use `Annotated` in the type for the `q` parameter + +Remember I told you before that `Annotated` can be used to add metadata to your parameters in the [Python Types Intro](../python-types.md#type-hints-with-metadata-annotations){.internal-link target=_blank}? + +Now it's the time to use it with FastAPI. 🚀 + +We had this type annotation: + +=== "Python 3.10+" + + ```Python + q: str | None = None + ``` + +=== "Python 3.6+" + + ```Python + q: Union[str, None] = None + ``` + +What we will do is wrap that with `Annotated`, so it becomes: + +=== "Python 3.10+" + + ```Python + q: Annotated[str | None] = None + ``` + +=== "Python 3.6+" + + ```Python + q: Annotated[Union[str, None]] = None + ``` + +Both of those versions mean the same thing, `q` is a parameter that can be a `str` or `None`, and by default, it is `None`. + +Now let's jump to the fun stuff. 🎉 + +## Add `Query` to `Annotated` in the `q` parameter + +Now that we have this `Annotated` where we can put more metadata, add `Query` to it, and set the parameter `max_length` to 50: -由于我们必须用 `Query(default=None)` 替换默认值 `None`,`Query` 的第一个参数同样也是用于定义默认值。 +=== "Python 3.10+" -所以: + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial002_an_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="10" + {!> ../../../docs_src/query_params_str_validations/tutorial002_an.py!} + ``` + +Notice that the default value is still `None`, so the parameter is still optional. + +But now, having `Query(max_length=50)` inside of `Annotated`, we are telling FastAPI that we want it to extract this value from the query parameters (this would have been the default anyway 🤷) and that we want to have **additional validation** for this value (that's why we do this, to get the additional validation). 😎 + +FastAPI will now: + +* **Validate** the data making sure that the max length is 50 characters +* Show a **clear error** for the client when the data is not valid +* **Document** the parameter in the OpenAPI schema *path operation* (so it will show up in the **automatic docs UI**) + +## Alternative (old) `Query` as the default value + +Previous versions of FastAPI (before 0.95.0) required you to use `Query` as the default value of your parameter, instead of putting it in `Annotated`, there's a high chance that you will see code using it around, so I'll explain it to you. + +!!! tip + For new code and whenever possible, use `Annotated` as explained above. There are multiple advantages (explained below) and no disadvantages. 🍰 + +This is how you would use `Query()` as the default value of your function parameter, setting the parameter `max_length` to 50: + +=== "Python 3.10+" + + ```Python hl_lines="7" + {!> ../../../docs_src/query_params_str_validations/tutorial002_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial002.py!} + ``` + +As in this case (without using `Annotated`) we have to replace the default value `None` in the function with `Query()`, we now need to set the default value with the parameter `Query(default=None)`, it serves the same purpose of defining that default value (at least for FastAPI). + +So: ```Python q: Union[str, None] = Query(default=None) ``` -...使得参数可选,等同于: +...makes the parameter optional, with a default value of `None`, the same as: + +```Python +q: Union[str, None] = None +``` + +And in Python 3.10 and above: + +```Python +q: str | None = Query(default=None) +``` + +...makes the parameter optional, with a default value of `None`, the same as: ```Python -q: str = None +q: str | None = None ``` -但是 `Query` 显式地将其声明为查询参数。 +But it declares it explicitly as being a query parameter. + +!!! info + Have in mind that the most important part to make a parameter optional is the part: + + ```Python + = None + ``` + + + or the: + + ```Python + = Query(default=None) + ``` -然后,我们可以将更多的参数传递给 `Query`。在本例中,适用于字符串的 `max_length` 参数: + + as it will use that `None` as the default value, and that way make the parameter **not required**. + + The `Union[str, None]` part allows your editor to provide better support, but it is not what tells FastAPI that this parameter is not required. + +Then, we can pass more parameters to `Query`. In this case, the `max_length` parameter that applies to strings: ```Python q: Union[str, None] = Query(default=None, max_length=50) ``` -将会校验数据,在数据无效时展示清晰的错误信息,并在 OpenAPI 模式的*路径操作*中记录该参​​数。 +This will validate the data, show a clear error when the data is not valid, and document the parameter in the OpenAPI schema *path operation*. + +### `Query` as the default value or in `Annotated` + +Have in mind that when using `Query` inside of `Annotated` you cannot use the `default` parameter for `Query`. -## 添加更多校验 +Instead use the actual default value of the function parameter. Otherwise, it would be inconsistent. -你还可以添加 `min_length` 参数: +For example, this is not allowed: -```Python hl_lines="10" -{!../../../docs_src/query_params_str_validations/tutorial003.py!} +```Python +q: Annotated[str, Query(default="rick")] = "morty" ``` -## 添加正则表达式 +...because it's not clear if the default value should be `"rick"` or `"morty"`. + +So, you would use (preferably): + +```Python +q: Annotated[str, Query()] = "rick" +``` -你可以定义一个参数值必须匹配的正则表达式: +...or in older code bases you will find: -```Python hl_lines="11" -{!../../../docs_src/query_params_str_validations/tutorial004.py!} +```Python +q: str = Query(default="rick") ``` -这个指定的正则表达式通过以下规则检查接收到的参数值: +### Advantages of `Annotated` -* `^`:以该符号之后的字符开头,符号之前没有字符。 -* `fixedquery`: 值精确地等于 `fixedquery`。 -* `$`: 到此结束,在 `fixedquery` 之后没有更多字符。 +**Using `Annotated` is recommended** instead of the default value in function parameters, it is **better** for multiple reasons. 🤓 -如果你对所有的这些**「正则表达式」**概念感到迷茫,请不要担心。对于许多人来说这都是一个困难的主题。你仍然可以在无需正则表达式的情况下做很多事情。 +The **default** value of the **function parameter** is the **actual default** value, that's more intuitive with Python in general. 😌 -但是,一旦你需要用到并去学习它们时,请了解你已经可以在 **FastAPI** 中直接使用它们。 +You could **call** that same function in **other places** without FastAPI, and it would **work as expected**. If there's a **required** parameter (without a default value), your **editor** will let you know with an error, **Python** will also complain if you run it without passing the required parameter. -## 默认值 +When you don't use `Annotated` and instead use the **(old) default value style**, if you call that function without FastAPI in **other place**, you have to **remember** to pass the arguments to the function for it to work correctly, otherwise the values will be different from what you expect (e.g. `QueryInfo` or something similar instead of `str`). And your editor won't complain, and Python won't complain running that function, only when the operations inside error out. -你可以向 `Query` 的第一个参数传入 `None` 用作查询参数的默认值,以同样的方式你也可以传递其他默认值。 +Because `Annotated` can have more than one metadata annotation, you could now even use the same function with other tools, like Typer. 🚀 -假设你想要声明查询参数 `q`,使其 `min_length` 为 `3`,并且默认值为 `fixedquery`: +## Add more validations -```Python hl_lines="7" -{!../../../docs_src/query_params_str_validations/tutorial005.py!} -``` +You can also add a parameter `min_length`: + +=== "Python 3.10+" + + ```Python hl_lines="10" + {!> ../../../docs_src/query_params_str_validations/tutorial003_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="10" + {!> ../../../docs_src/query_params_str_validations/tutorial003_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="11" + {!> ../../../docs_src/query_params_str_validations/tutorial003_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7" + {!> ../../../docs_src/query_params_str_validations/tutorial003_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="10" + {!> ../../../docs_src/query_params_str_validations/tutorial003.py!} + ``` + +## Add regular expressions + +You can define a regular expression `pattern` that the parameter should match: + +=== "Python 3.10+" + + ```Python hl_lines="11" + {!> ../../../docs_src/query_params_str_validations/tutorial004_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="11" + {!> ../../../docs_src/query_params_str_validations/tutorial004_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="12" + {!> ../../../docs_src/query_params_str_validations/tutorial004_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial004_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="11" + {!> ../../../docs_src/query_params_str_validations/tutorial004.py!} + ``` + +This specific regular expression pattern checks that the received parameter value: + +* `^`: starts with the following characters, doesn't have characters before. +* `fixedquery`: has the exact value `fixedquery`. +* `$`: ends there, doesn't have any more characters after `fixedquery`. + +If you feel lost with all these **"regular expression"** ideas, don't worry. They are a hard topic for many people. You can still do a lot of stuff without needing regular expressions yet. + +But whenever you need them and go and learn them, know that you can already use them directly in **FastAPI**. + +### Pydantic v1 `regex` instead of `pattern` + +Before Pydantic version 2 and before FastAPI 0.100.0, the parameter was called `regex` instead of `pattern`, but it's now deprecated. + +You could still see some code using it: + +=== "Python 3.10+ Pydantic v1" + + ```Python hl_lines="11" + {!> ../../../docs_src/query_params_str_validations/tutorial004_an_py310_regex.py!} + ``` + +But know that this is deprecated and it should be updated to use the new parameter `pattern`. 🤓 + +## Default values + +You can, of course, use default values other than `None`. + +Let's say that you want to declare the `q` query parameter to have a `min_length` of `3`, and to have a default value of `"fixedquery"`: + +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial005_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="8" + {!> ../../../docs_src/query_params_str_validations/tutorial005_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7" + {!> ../../../docs_src/query_params_str_validations/tutorial005.py!} + ``` !!! note - 具有默认值还会使该参数成为可选参数。 + Having a default value of any type, including `None`, makes the parameter optional (not required). -## 声明为必需参数 +## Make it required -当我们不需要声明额外的校验或元数据时,只需不声明默认值就可以使 `q` 参数成为必需参数,例如: +When we don't need to declare more validations or metadata, we can make the `q` query parameter required just by not declaring a default value, like: ```Python q: str ``` -代替: +instead of: ```Python q: Union[str, None] = None ``` -但是现在我们正在用 `Query` 声明它,例如: +But we are now declaring it with `Query`, for example like: -```Python -q: Union[str, None] = Query(default=None, min_length=3) -``` +=== "Annotated" -因此,当你在使用 `Query` 且需要声明一个值是必需的时,只需不声明默认参数: + ```Python + q: Annotated[Union[str, None], Query(min_length=3)] = None + ``` -```Python hl_lines="7" -{!../../../docs_src/query_params_str_validations/tutorial006.py!} -``` +=== "non-Annotated" -### 使用省略号(`...`)声明必需参数 + ```Python + q: Union[str, None] = Query(default=None, min_length=3) + ``` -有另一种方法可以显式的声明一个值是必需的,即将默认参数的默认值设为 `...` : +So, when you need to declare a value as required while using `Query`, you can simply not declare a default value: -```Python hl_lines="7" -{!../../../docs_src/query_params_str_validations/tutorial006b.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial006_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="8" + {!> ../../../docs_src/query_params_str_validations/tutorial006_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7" + {!> ../../../docs_src/query_params_str_validations/tutorial006.py!} + ``` + + + !!! tip + Notice that, even though in this case the `Query()` is used as the function parameter default value, we don't pass the `default=None` to `Query()`. + + Still, probably better to use the `Annotated` version. 😉 + +### Required with Ellipsis (`...`) + +There's an alternative way to explicitly declare that a value is required. You can set the default to the literal value `...`: + +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial006b_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="8" + {!> ../../../docs_src/query_params_str_validations/tutorial006b_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7" + {!> ../../../docs_src/query_params_str_validations/tutorial006b.py!} + ``` !!! info - 如果你之前没见过 `...` 这种用法:它是一个特殊的单独值,它是 Python 的一部分并且被称为「省略号」。 - Pydantic 和 FastAPI 使用它来显式的声明需要一个值。 + If you hadn't seen that `...` before: it is a special single value, it is part of Python and is called "Ellipsis". -这将使 **FastAPI** 知道此查询参数是必需的。 + It is used by Pydantic and FastAPI to explicitly declare that a value is required. -### 使用`None`声明必需参数 +This will let **FastAPI** know that this parameter is required. -你可以声明一个参数可以接收`None`值,但它仍然是必需的。这将强制客户端发送一个值,即使该值是`None`。 +### Required with `None` -为此,你可以声明`None`是一个有效的类型,并仍然使用`default=...`: +You can declare that a parameter can accept `None`, but that it's still required. This would force clients to send a value, even if the value is `None`. -```Python hl_lines="9" -{!../../../docs_src/query_params_str_validations/tutorial006c.py!} -``` +To do that, you can declare that `None` is a valid type but still use `...` as the default: + +=== "Python 3.10+" + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial006c_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial006c_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="10" + {!> ../../../docs_src/query_params_str_validations/tutorial006c_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7" + {!> ../../../docs_src/query_params_str_validations/tutorial006c_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial006c.py!} + ``` !!! tip - Pydantic 是 FastAPI 中所有数据验证和序列化的核心,当你在没有设默认值的情况下使用 `Optional` 或 `Union[Something, None]` 时,它具有特殊行为,你可以在 Pydantic 文档中阅读有关必需可选字段的更多信息。 + Pydantic, which is what powers all the data validation and serialization in FastAPI, has a special behavior when you use `Optional` or `Union[Something, None]` without a default value, you can read more about it in the Pydantic docs about Required Optional fields. -### 使用Pydantic中的`Required`代替省略号(`...`) +### Use Pydantic's `Required` instead of Ellipsis (`...`) -如果你觉得使用 `...` 不舒服,你也可以从 Pydantic 导入并使用 `Required`: +If you feel uncomfortable using `...`, you can also import and use `Required` from Pydantic: -```Python hl_lines="2 8" -{!../../../docs_src/query_params_str_validations/tutorial006d.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="4 10" + {!> ../../../docs_src/query_params_str_validations/tutorial006d_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="2 9" + {!> ../../../docs_src/query_params_str_validations/tutorial006d_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="2 8" + {!> ../../../docs_src/query_params_str_validations/tutorial006d.py!} + ``` !!! tip - 请记住,在大多数情况下,当你需要某些东西时,可以简单地省略 `default` 参数,因此你通常不必使用 `...` 或 `Required` + Remember that in most of the cases, when something is required, you can simply omit the default, so you normally don't have to use `...` nor `Required`. +## Query parameter list / multiple values -## 查询参数列表 / 多个值 +When you define a query parameter explicitly with `Query` you can also declare it to receive a list of values, or said in other way, to receive multiple values. -当你使用 `Query` 显式地定义查询参数时,你还可以声明它去接收一组值,或换句话来说,接收多个值。 +For example, to declare a query parameter `q` that can appear multiple times in the URL, you can write: -例如,要声明一个可在 URL 中出现多次的查询参数 `q`,你可以这样写: +=== "Python 3.10+" -```Python hl_lines="9" -{!../../../docs_src/query_params_str_validations/tutorial011.py!} -``` + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial011_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial011_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="10" + {!> ../../../docs_src/query_params_str_validations/tutorial011_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7" + {!> ../../../docs_src/query_params_str_validations/tutorial011_py310.py!} + ``` + +=== "Python 3.9+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial011_py39.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial011.py!} + ``` -然后,输入如下网址: +Then, with a URL like: ``` http://localhost:8000/items/?q=foo&q=bar ``` -你会在*路径操作函数*的*函数参数* `q` 中以一个 Python `list` 的形式接收到*查询参数* `q` 的多个值(`foo` 和 `bar`)。 +you would receive the multiple `q` *query parameters'* values (`foo` and `bar`) in a Python `list` inside your *path operation function*, in the *function parameter* `q`. -因此,该 URL 的响应将会是: +So, the response to that URL would be: ```JSON { @@ -188,27 +604,53 @@ http://localhost:8000/items/?q=foo&q=bar ``` !!! tip - 要声明类型为 `list` 的查询参数,如上例所示,你需要显式地使用 `Query`,否则该参数将被解释为请求体。 + To declare a query parameter with a type of `list`, like in the example above, you need to explicitly use `Query`, otherwise it would be interpreted as a request body. -交互式 API 文档将会相应地进行更新,以允许使用多个值: +The interactive API docs will update accordingly, to allow multiple values: - + -### 具有默认值的查询参数列表 / 多个值 +### Query parameter list / multiple values with defaults -你还可以定义在没有任何给定值时的默认 `list` 值: +And you can also define a default `list` of values if none are provided: -```Python hl_lines="9" -{!../../../docs_src/query_params_str_validations/tutorial012.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial012_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="10" + {!> ../../../docs_src/query_params_str_validations/tutorial012_an.py!} + ``` + +=== "Python 3.9+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7" + {!> ../../../docs_src/query_params_str_validations/tutorial012_py39.py!} + ``` + +=== "Python 3.6+ non-Annotated" -如果你访问: + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial012.py!} + ``` + +If you go to: ``` http://localhost:8000/items/ ``` -`q` 的默认值将为:`["foo", "bar"]`,你的响应会是: +the default of `q` will be: `["foo", "bar"]` and your response will be: ```JSON { @@ -219,97 +661,282 @@ http://localhost:8000/items/ } ``` -#### 使用 `list` +#### Using `list` -你也可以直接使用 `list` 代替 `List [str]`: +You can also use `list` directly instead of `List[str]` (or `list[str]` in Python 3.9+): -```Python hl_lines="7" -{!../../../docs_src/query_params_str_validations/tutorial013.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial013_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="8" + {!> ../../../docs_src/query_params_str_validations/tutorial013_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7" + {!> ../../../docs_src/query_params_str_validations/tutorial013.py!} + ``` !!! note - 请记住,在这种情况下 FastAPI 将不会检查列表的内容。 + Have in mind that in this case, FastAPI won't check the contents of the list. - 例如,`List[int]` 将检查(并记录到文档)列表的内容必须是整数。但是单独的 `list` 不会。 + For example, `List[int]` would check (and document) that the contents of the list are integers. But `list` alone wouldn't. -## 声明更多元数据 +## Declare more metadata -你可以添加更多有关该参数的信息。 +You can add more information about the parameter. -这些信息将包含在生成的 OpenAPI 模式中,并由文档用户界面和外部工具所使用。 +That information will be included in the generated OpenAPI and used by the documentation user interfaces and external tools. !!! note - 请记住,不同的工具对 OpenAPI 的支持程度可能不同。 + Have in mind that different tools might have different levels of OpenAPI support. - 其中一些可能不会展示所有已声明的额外信息,尽管在大多数情况下,缺少的这部分功能已经计划进行开发。 + Some of them might not show all the extra information declared yet, although in most of the cases, the missing feature is already planned for development. -你可以添加 `title`: +You can add a `title`: -```Python hl_lines="10" -{!../../../docs_src/query_params_str_validations/tutorial007.py!} -``` +=== "Python 3.10+" -以及 `description`: + ```Python hl_lines="10" + {!> ../../../docs_src/query_params_str_validations/tutorial007_an_py310.py!} + ``` -```Python hl_lines="13" -{!../../../docs_src/query_params_str_validations/tutorial008.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="10" + {!> ../../../docs_src/query_params_str_validations/tutorial007_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="11" + {!> ../../../docs_src/query_params_str_validations/tutorial007_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="8" + {!> ../../../docs_src/query_params_str_validations/tutorial007_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="10" + {!> ../../../docs_src/query_params_str_validations/tutorial007.py!} + ``` + +And a `description`: + +=== "Python 3.10+" + + ```Python hl_lines="14" + {!> ../../../docs_src/query_params_str_validations/tutorial008_an_py310.py!} + ``` + +=== "Python 3.9+" -## 别名参数 + ```Python hl_lines="14" + {!> ../../../docs_src/query_params_str_validations/tutorial008_an_py39.py!} + ``` -假设你想要查询参数为 `item-query`。 +=== "Python 3.6+" -像下面这样: + ```Python hl_lines="15" + {!> ../../../docs_src/query_params_str_validations/tutorial008_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="12" + {!> ../../../docs_src/query_params_str_validations/tutorial008_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="13" + {!> ../../../docs_src/query_params_str_validations/tutorial008.py!} + ``` + +## Alias parameters + +Imagine that you want the parameter to be `item-query`. + +Like in: ``` http://127.0.0.1:8000/items/?item-query=foobaritems ``` -但是 `item-query` 不是一个有效的 Python 变量名称。 +But `item-query` is not a valid Python variable name. -最接近的有效名称是 `item_query`。 +The closest would be `item_query`. -但是你仍然要求它在 URL 中必须是 `item-query`... +But you still need it to be exactly `item-query`... -这时你可以用 `alias` 参数声明一个别名,该别名将用于在 URL 中查找查询参数值: +Then you can declare an `alias`, and that alias is what will be used to find the parameter value: -```Python hl_lines="9" -{!../../../docs_src/query_params_str_validations/tutorial009.py!} -``` +=== "Python 3.10+" -## 弃用参数 + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial009_an_py310.py!} + ``` -现在假设你不再喜欢此参数。 +=== "Python 3.9+" -你不得不将其保留一段时间,因为有些客户端正在使用它,但你希望文档清楚地将其展示为已弃用。 + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial009_an_py39.py!} + ``` -那么将参数 `deprecated=True` 传入 `Query`: +=== "Python 3.6+" -```Python hl_lines="18" -{!../../../docs_src/query_params_str_validations/tutorial010.py!} -``` + ```Python hl_lines="10" + {!> ../../../docs_src/query_params_str_validations/tutorial009_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7" + {!> ../../../docs_src/query_params_str_validations/tutorial009_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial009.py!} + ``` + +## Deprecating parameters + +Now let's say you don't like this parameter anymore. + +You have to leave it there a while because there are clients using it, but you want the docs to clearly show it as deprecated. + +Then pass the parameter `deprecated=True` to `Query`: + +=== "Python 3.10+" + + ```Python hl_lines="19" + {!> ../../../docs_src/query_params_str_validations/tutorial010_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="19" + {!> ../../../docs_src/query_params_str_validations/tutorial010_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="20" + {!> ../../../docs_src/query_params_str_validations/tutorial010_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="17" + {!> ../../../docs_src/query_params_str_validations/tutorial010_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="18" + {!> ../../../docs_src/query_params_str_validations/tutorial010.py!} + ``` + +The docs will show it like this: + + + +## Exclude from OpenAPI + +To exclude a query parameter from the generated OpenAPI schema (and thus, from the automatic documentation systems), set the parameter `include_in_schema` of `Query` to `False`: + +=== "Python 3.10+" + + ```Python hl_lines="10" + {!> ../../../docs_src/query_params_str_validations/tutorial014_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="10" + {!> ../../../docs_src/query_params_str_validations/tutorial014_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="11" + {!> ../../../docs_src/query_params_str_validations/tutorial014_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="8" + {!> ../../../docs_src/query_params_str_validations/tutorial014_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" -文档将会像下面这样展示它: + !!! tip + Prefer to use the `Annotated` version if possible. - + ```Python hl_lines="10" + {!> ../../../docs_src/query_params_str_validations/tutorial014.py!} + ``` -## 总结 +## Recap -你可以为查询参数声明额外的校验和元数据。 +You can declare additional validations and metadata for your parameters. -通用的校验和元数据: +Generic validations and metadata: * `alias` * `title` * `description` * `deprecated` -特定于字符串的校验: +Validations specific for strings: * `min_length` * `max_length` * `regex` -在这些示例中,你了解了如何声明对 `str` 值的校验。 +In these examples you saw how to declare validations for `str` values. -请参阅下一章节,以了解如何声明对其他类型例如数值的校验。 +See the next chapters to see how to declare validations for other types, like numbers. From c5a54911773277b6e5bbf1b214abf0b182f882b9 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:48 +0800 Subject: [PATCH 089/163] New translations query-params.md (Chinese Simplified) --- docs/zh/docs/tutorial/query-params.md | 164 +++++++++++++++----------- 1 file changed, 98 insertions(+), 66 deletions(-) diff --git a/docs/zh/docs/tutorial/query-params.md b/docs/zh/docs/tutorial/query-params.md index b1668a2d2523f..0b74b10f81c8c 100644 --- a/docs/zh/docs/tutorial/query-params.md +++ b/docs/zh/docs/tutorial/query-params.md @@ -1,151 +1,175 @@ -# 查询参数 +# Query Parameters -声明不属于路径参数的其他函数参数时,它们将被自动解释为"查询字符串"参数 +When you declare other function parameters that are not part of the path parameters, they are automatically interpreted as "query" parameters. ```Python hl_lines="9" {!../../../docs_src/query_params/tutorial001.py!} ``` -查询字符串是键值对的集合,这些键值对位于 URL 的 `?` 之后,并以 `&` 符号分隔。 +The query is the set of key-value pairs that go after the `?` in a URL, separated by `&` characters. -例如,在以下 url 中: +For example, in the URL: ``` http://127.0.0.1:8000/items/?skip=0&limit=10 ``` -...查询参数为: +...the query parameters are: -* `skip`:对应的值为 `0` -* `limit`:对应的值为 `10` +* `skip`: with a value of `0` +* `limit`: with a value of `10` -由于它们是 URL 的一部分,因此它们的"原始值"是字符串。 +As they are part of the URL, they are "naturally" strings. -但是,当你为它们声明了 Python 类型(在上面的示例中为 `int`)时,它们将转换为该类型并针对该类型进行校验。 +But when you declare them with Python types (in the example above, as `int`), they are converted to that type and validated against it. -应用于路径参数的所有相同过程也适用于查询参数: +All the same process that applied for path parameters also applies for query parameters: -* (很明显的)编辑器支持 -* 数据"解析" -* 数据校验 -* 自动生成文档 +* Editor support (obviously) +* Data "parsing" +* Data validation +* Automatic documentation -## 默认值 +## Defaults -由于查询参数不是路径的固定部分,因此它们可以是可选的,并且可以有默认值。 +As query parameters are not a fixed part of a path, they can be optional and can have default values. -在上面的示例中,它们具有 `skip=0` 和 `limit=10` 的默认值。 +In the example above they have default values of `skip=0` and `limit=10`. -因此,访问 URL: +So, going to the URL: ``` http://127.0.0.1:8000/items/ ``` -将与访问以下地址相同: +would be the same as going to: ``` http://127.0.0.1:8000/items/?skip=0&limit=10 ``` -但是,如果你访问的是: +But if you go to, for example: ``` http://127.0.0.1:8000/items/?skip=20 ``` -函数中的参数值将会是: +The parameter values in your function will be: -* `skip=20`:在 URL 中设定的值 -* `limit=10`:使用默认值 +* `skip=20`: because you set it in the URL +* `limit=10`: because that was the default value -## 可选参数 +## Optional parameters -通过同样的方式,你可以将它们的默认值设置为 `None` 来声明可选查询参数: +The same way, you can declare optional query parameters, by setting their default to `None`: -```Python hl_lines="7" -{!../../../docs_src/query_params/tutorial002.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="7" + {!> ../../../docs_src/query_params/tutorial002_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params/tutorial002.py!} + ``` -在这个例子中,函数参数 `q` 将是可选的,并且默认值为 `None`。 +In this case, the function parameter `q` will be optional, and will be `None` by default. !!! check - 还要注意的是,**FastAPI** 足够聪明,能够分辨出参数 `item_id` 是路径参数而 `q` 不是,因此 `q` 是一个查询参数。 + Also notice that **FastAPI** is smart enough to notice that the path parameter `item_id` is a path parameter and `q` is not, so, it's a query parameter. -## 查询参数类型转换 +## Query parameter type conversion -你还可以声明 `bool` 类型,它们将被自动转换: +You can also declare `bool` types, and they will be converted: -```Python hl_lines="7" -{!../../../docs_src/query_params/tutorial003.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="7" + {!> ../../../docs_src/query_params/tutorial003_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params/tutorial003.py!} + ``` -这个例子中,如果你访问: +In this case, if you go to: ``` http://127.0.0.1:8000/items/foo?short=1 ``` -或 +or ``` http://127.0.0.1:8000/items/foo?short=True ``` -或 +or ``` http://127.0.0.1:8000/items/foo?short=true ``` -或 +or ``` http://127.0.0.1:8000/items/foo?short=on ``` -或 +or ``` http://127.0.0.1:8000/items/foo?short=yes ``` -或任何其他的变体形式(大写,首字母大写等等),你的函数接收的 `short` 参数都会是布尔值 `True`。对于值为 `False` 的情况也是一样的。 +or any other case variation (uppercase, first letter in uppercase, etc), your function will see the parameter `short` with a `bool` value of `True`. Otherwise as `False`. -## 多个路径和查询参数 +## Multiple path and query parameters -你可以同时声明多个路径参数和查询参数,**FastAPI** 能够识别它们。 +You can declare multiple path parameters and query parameters at the same time, **FastAPI** knows which is which. -而且你不需要以任何特定的顺序来声明。 +And you don't have to declare them in any specific order. -它们将通过名称被检测到: +They will be detected by name: -```Python hl_lines="6 8" -{!../../../docs_src/query_params/tutorial004.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="6 8" + {!> ../../../docs_src/query_params/tutorial004_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="8 10" + {!> ../../../docs_src/query_params/tutorial004.py!} + ``` -## 必需查询参数 +## Required query parameters -当你为非路径参数声明了默认值时(目前而言,我们所知道的仅有查询参数),则该参数不是必需的。 +When you declare a default value for non-path parameters (for now, we have only seen query parameters), then it is not required. -如果你不想添加一个特定的值,而只是想使该参数成为可选的,则将默认值设置为 `None`。 +If you don't want to add a specific value but just make it optional, set the default as `None`. -但当你想让一个查询参数成为必需的,不声明任何默认值就可以: +But when you want to make a query parameter required, you can just not declare any default value: ```Python hl_lines="6-7" {!../../../docs_src/query_params/tutorial005.py!} ``` -这里的查询参数 `needy` 是类型为 `str` 的必需查询参数。 +Here the query parameter `needy` is a required query parameter of type `str`. -如果你在浏览器中打开一个像下面的 URL: +If you open in your browser a URL like: ``` http://127.0.0.1:8000/items/foo-item ``` -...因为没有添加必需的参数 `needy`,你将看到类似以下的错误: +...without adding the required parameter `needy`, you will see an error like: ```JSON { @@ -162,13 +186,13 @@ http://127.0.0.1:8000/items/foo-item } ``` -由于 `needy` 是必需参数,因此你需要在 URL 中设置它的值: +As `needy` is a required parameter, you would need to set it in the URL: ``` http://127.0.0.1:8000/items/foo-item?needy=sooooneedy ``` -...这样就正常了: +...this would work: ```JSON { @@ -177,17 +201,25 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy } ``` -当然,你也可以定义一些参数为必需的,一些具有默认值,而某些则完全是可选的: +And of course, you can define some parameters as required, some as having a default value, and some entirely optional: -```Python hl_lines="7" -{!../../../docs_src/query_params/tutorial006.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="8" + {!> ../../../docs_src/query_params/tutorial006_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="10" + {!> ../../../docs_src/query_params/tutorial006.py!} + ``` -在这个例子中,有3个查询参数: +In this case, there are 3 query parameters: -* `needy`,一个必需的 `str` 类型参数。 -* `skip`,一个默认值为 `0` 的 `int` 类型参数。 -* `limit`,一个可选的 `int` 类型参数。 +* `needy`, a required `str`. +* `skip`, an `int` with a default value of `0`. +* `limit`, an optional `int`. !!! tip - 你还可以像在 [路径参数](path-params.md#predefined-values){.internal-link target=_blank} 中那样使用 `Enum`。 + You could also use `Enum`s the same way as with [Path Parameters](path-params.md#predefined-values){.internal-link target=_blank}. From fc9dcfe6991d421f8415db9a3d85228d97dd644f Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:49 +0800 Subject: [PATCH 090/163] New translations request-files.md (Chinese Simplified) --- docs/zh/docs/tutorial/request-files.md | 303 +++++++++++++++++-------- 1 file changed, 211 insertions(+), 92 deletions(-) diff --git a/docs/zh/docs/tutorial/request-files.md b/docs/zh/docs/tutorial/request-files.md index 03474907ee94b..d2d649924f859 100644 --- a/docs/zh/docs/tutorial/request-files.md +++ b/docs/zh/docs/tutorial/request-files.md @@ -1,194 +1,313 @@ -# 请求文件 +# Request Files -`File` 用于定义客户端的上传文件。 +You can define files to be uploaded by the client using `File`. -!!! info "说明" +!!! info + To receive uploaded files, first install `python-multipart`. - 因为上传文件以「表单数据」形式发送。 + E.g. `pip install python-multipart`. + + This is because uploaded files are sent as "form data". - 所以接收上传文件,要预先安装 `python-multipart`。 +## Import `File` - 例如: `pip install python-multipart`。 +Import `File` and `UploadFile` from `fastapi`: -## 导入 `File` +=== "Python 3.9+" -从 `fastapi` 导入 `File` 和 `UploadFile`: + ```Python hl_lines="3" + {!> ../../../docs_src/request_files/tutorial001_an_py39.py!} + ``` -```Python hl_lines="1" -{!../../../docs_src/request_files/tutorial001.py!} -``` +=== "Python 3.6+" + + ```Python hl_lines="1" + {!> ../../../docs_src/request_files/tutorial001_an.py!} + ``` -## 定义 `File` 参数 +=== "Python 3.6+ non-Annotated" -创建文件(`File`)参数的方式与 `Body` 和 `Form` 一样: + !!! tip + Prefer to use the `Annotated` version if possible. -```Python hl_lines="7" -{!../../../docs_src/request_files/tutorial001.py!} -``` + ```Python hl_lines="1" + {!> ../../../docs_src/request_files/tutorial001.py!} + ``` + +## Define `File` Parameters + +Create file parameters the same way you would for `Body` or `Form`: -!!! info "说明" +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/request_files/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" - `File` 是直接继承自 `Form` 的类。 + ```Python hl_lines="8" + {!> ../../../docs_src/request_files/tutorial001_an.py!} + ``` - 注意,从 `fastapi` 导入的 `Query`、`Path`、`File` 等项,实际上是返回特定类的函数。 +=== "Python 3.6+ non-Annotated" -!!! tip "提示" + !!! tip + Prefer to use the `Annotated` version if possible. - 声明文件体必须使用 `File`,否则,FastAPI 会把该参数当作查询参数或请求体(JSON)参数。 + ```Python hl_lines="7" + {!> ../../../docs_src/request_files/tutorial001.py!} + ``` -文件作为「表单数据」上传。 +!!! info + `File` is a class that inherits directly from `Form`. -如果把*路径操作函数*参数的类型声明为 `bytes`,**FastAPI** 将以 `bytes` 形式读取和接收文件内容。 + But remember that when you import `Query`, `Path`, `File` and others from `fastapi`, those are actually functions that return special classes. -这种方式把文件的所有内容都存储在内存里,适用于小型文件。 +!!! tip + To declare File bodies, you need to use `File`, because otherwise the parameters would be interpreted as query parameters or body (JSON) parameters. -不过,很多情况下,`UploadFile` 更好用。 +The files will be uploaded as "form data". -## 含 `UploadFile` 的文件参数 +If you declare the type of your *path operation function* parameter as `bytes`, **FastAPI** will read the file for you and you will receive the contents as `bytes`. -定义文件参数时使用 `UploadFile`: +Have in mind that this means that the whole contents will be stored in memory. This will work well for small files. -```Python hl_lines="12" -{!../../../docs_src/request_files/tutorial001.py!} -``` +But there are several cases in which you might benefit from using `UploadFile`. -`UploadFile` 与 `bytes` 相比有更多优势: +## File Parameters with `UploadFile` -* 使用 `spooled` 文件: - * 存储在内存的文件超出最大上限时,FastAPI 会把文件存入磁盘; -* 这种方式更适于处理图像、视频、二进制文件等大型文件,好处是不会占用所有内存; -* 可获取上传文件的元数据; -* 自带 file-like `async` 接口; -* 暴露的 Python `SpooledTemporaryFile` 对象,可直接传递给其他预期「file-like」对象的库。 +Define a file parameter with a type of `UploadFile`: + +=== "Python 3.9+" + + ```Python hl_lines="14" + {!> ../../../docs_src/request_files/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="13" + {!> ../../../docs_src/request_files/tutorial001_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="12" + {!> ../../../docs_src/request_files/tutorial001.py!} + ``` + +Using `UploadFile` has several advantages over `bytes`: + +* You don't have to use `File()` in the default value of the parameter. +* It uses a "spooled" file: + * A file stored in memory up to a maximum size limit, and after passing this limit it will be stored in disk. +* This means that it will work well for large files like images, videos, large binaries, etc. without consuming all the memory. +* You can get metadata from the uploaded file. +* It has a file-like `async` interface. +* It exposes an actual Python `SpooledTemporaryFile` object that you can pass directly to other libraries that expect a file-like object. ### `UploadFile` -`UploadFile` 的属性如下: +`UploadFile` has the following attributes: -* `filename`:上传文件名字符串(`str`),例如, `myimage.jpg`; -* `content_type`:内容类型(MIME 类型 / 媒体类型)字符串(`str`),例如,`image/jpeg`; -* `file`: `SpooledTemporaryFile`file-like 对象)。其实就是 Python文件,可直接传递给其他预期 `file-like` 对象的函数或支持库。 +* `filename`: A `str` with the original file name that was uploaded (e.g. `myimage.jpg`). +* `content_type`: A `str` with the content type (MIME type / media type) (e.g. `image/jpeg`). +* `file`: A `SpooledTemporaryFile` (a file-like object). This is the actual Python file that you can pass directly to other functions or libraries that expect a "file-like" object. -`UploadFile` 支持以下 `async` 方法,(使用内部 `SpooledTemporaryFile`)可调用相应的文件方法。 +`UploadFile` has the following `async` methods. They all call the corresponding file methods underneath (using the internal `SpooledTemporaryFile`). -* `write(data)`:把 `data` (`str` 或 `bytes`)写入文件; -* `read(size)`:按指定数量的字节或字符(`size` (`int`))读取文件内容; -* `seek(offset)`:移动至文件 `offset` (`int`)字节处的位置; - * 例如,`await myfile.seek(0) ` 移动到文件开头; - * 执行 `await myfile.read()` 后,需再次读取已读取内容时,这种方法特别好用; -* `close()`:关闭文件。 +* `write(data)`: Writes `data` (`str` or `bytes`) to the file. +* `read(size)`: Reads `size` (`int`) bytes/characters of the file. +* `seek(offset)`: Goes to the byte position `offset` (`int`) in the file. + * E.g., `await myfile.seek(0)` would go to the start of the file. + * This is especially useful if you run `await myfile.read()` once and then need to read the contents again. +* `close()`: Closes the file. -因为上述方法都是 `async` 方法,要搭配「await」使用。 +As all these methods are `async` methods, you need to "await" them. -例如,在 `async` *路径操作函数* 内,要用以下方式读取文件内容: +For example, inside of an `async` *path operation function* you can get the contents with: ```Python contents = await myfile.read() ``` -在普通 `def` *路径操作函数* 内,则可以直接访问 `UploadFile.file`,例如: +If you are inside of a normal `def` *path operation function*, you can access the `UploadFile.file` directly, for example: ```Python contents = myfile.file.read() ``` -!!! note "`async` 技术细节" +!!! note "`async` Technical Details" When you use the `async` methods, **FastAPI** runs the file methods in a threadpool and awaits for them. - 使用 `async` 方法时,**FastAPI** 在线程池中执行文件方法,并 `await` 操作完成。 +!!! note "Starlette Technical Details" + **FastAPI**'s `UploadFile` inherits directly from **Starlette**'s `UploadFile`, but adds some necessary parts to make it compatible with **Pydantic** and the other parts of FastAPI. -!!! note "Starlette 技术细节" +## What is "Form Data" - **FastAPI** 的 `UploadFile` 直接继承自 **Starlette** 的 `UploadFile`,但添加了一些必要功能,使之与 **Pydantic** 及 FastAPI 的其它部件兼容。 +The way HTML forms (`
`) sends the data to the server normally uses a "special" encoding for that data, it's different from JSON. -## 什么是 「表单数据」 +**FastAPI** will make sure to read that data from the right place instead of JSON. -与 JSON 不同,HTML 表单(`
`)向服务器发送数据通常使用「特殊」的编码。 +!!! note "Technical Details" + Data from forms is normally encoded using the "media type" `application/x-www-form-urlencoded` when it doesn't include files. -**FastAPI** 要确保从正确的位置读取数据,而不是读取 JSON。 + But when the form includes files, it is encoded as `multipart/form-data`. If you use `File`, **FastAPI** will know it has to get the files from the correct part of the body. + + If you want to read more about these encodings and form fields, head to the MDN web docs for POST. -!!! note "技术细节" +!!! warning + You can declare multiple `File` and `Form` parameters in a *path operation*, but you can't also declare `Body` fields that you expect to receive as JSON, as the request will have the body encoded using `multipart/form-data` instead of `application/json`. - 不包含文件时,表单数据一般用 `application/x-www-form-urlencoded`「媒体类型」编码。 + This is not a limitation of **FastAPI**, it's part of the HTTP protocol. - 但表单包含文件时,编码为 `multipart/form-data`。使用了 `File`,**FastAPI** 就知道要从请求体的正确位置获取文件。 +## Optional File Upload - 编码和表单字段详见 MDN Web 文档的 POST 小节。 +You can make a file optional by using standard type annotations and setting a default value of `None`: -!!! warning "警告" +=== "Python 3.10+" - 可在一个*路径操作*中声明多个 `File` 和 `Form` 参数,但不能同时声明要接收 JSON 的 `Body` 字段。因为此时请求体的编码是 `multipart/form-data`,不是 `application/json`。 + ```Python hl_lines="9 17" + {!> ../../../docs_src/request_files/tutorial001_02_an_py310.py!} + ``` - 这不是 **FastAPI** 的问题,而是 HTTP 协议的规定。 +=== "Python 3.9+" -## 可选文件上传 + ```Python hl_lines="9 17" + {!> ../../../docs_src/request_files/tutorial001_02_an_py39.py!} + ``` -您可以通过使用标准类型注解并将 None 作为默认值的方式将一个文件参数设为可选: +=== "Python 3.6+" -=== "Python 3.9+" + ```Python hl_lines="10 18" + {!> ../../../docs_src/request_files/tutorial001_02_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. - ```Python hl_lines="7 14" + ```Python hl_lines="7 15" {!> ../../../docs_src/request_files/tutorial001_02_py310.py!} ``` -=== "Python 3.6+" +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. ```Python hl_lines="9 17" {!> ../../../docs_src/request_files/tutorial001_02.py!} ``` -## 带有额外元数据的 `UploadFile` +## `UploadFile` with Additional Metadata -您也可以将 `File()` 与 `UploadFile` 一起使用,例如,设置额外的元数据: +You can also use `File()` with `UploadFile`, for example, to set additional metadata: -```Python hl_lines="13" -{!../../../docs_src/request_files/tutorial001_03.py!} -``` +=== "Python 3.9+" -## 多文件上传 + ```Python hl_lines="9 15" + {!> ../../../docs_src/request_files/tutorial001_03_an_py39.py!} + ``` -FastAPI 支持同时上传多个文件。 +=== "Python 3.6+" -可用同一个「表单字段」发送含多个文件的「表单数据」。 + ```Python hl_lines="8 14" + {!> ../../../docs_src/request_files/tutorial001_03_an.py!} + ``` -上传多个文件时,要声明含 `bytes` 或 `UploadFile` 的列表(`List`): +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7 13" + {!> ../../../docs_src/request_files/tutorial001_03.py!} + ``` + +## Multiple File Uploads + +It's possible to upload several files at the same time. + +They would be associated to the same "form field" sent using "form data". + +To use that, declare a list of `bytes` or `UploadFile`: === "Python 3.9+" + ```Python hl_lines="10 15" + {!> ../../../docs_src/request_files/tutorial002_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="11 16" + {!> ../../../docs_src/request_files/tutorial002_an.py!} + ``` + +=== "Python 3.9+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + ```Python hl_lines="8 13" {!> ../../../docs_src/request_files/tutorial002_py39.py!} ``` -=== "Python 3.6+" +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. ```Python hl_lines="10 15" {!> ../../../docs_src/request_files/tutorial002.py!} ``` -接收的也是含 `bytes` 或 `UploadFile` 的列表(`list`)。 +You will receive, as declared, a `list` of `bytes` or `UploadFile`s. +!!! note "Technical Details" + You could also use `from starlette.responses import HTMLResponse`. -!!! note "技术细节" + **FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. - 也可以使用 `from starlette.responses import HTMLResponse`。 +### Multiple File Uploads with Additional Metadata - `fastapi.responses` 其实与 `starlette.responses` 相同,只是为了方便开发者调用。实际上,大多数 **FastAPI** 的响应都直接从 Starlette 调用。 +And the same way as before, you can use `File()` to set additional parameters, even for `UploadFile`: -### 带有额外元数据的多文件上传 +=== "Python 3.9+" -和之前的方式一样, 您可以为 `File()` 设置额外参数, 即使是 `UploadFile`: + ```Python hl_lines="11 18-20" + {!> ../../../docs_src/request_files/tutorial003_an_py39.py!} + ``` -=== "Python 3.9+" +=== "Python 3.6+" + + ```Python hl_lines="12 19-21" + {!> ../../../docs_src/request_files/tutorial003_an.py!} + ``` - ```Python hl_lines="16" +=== "Python 3.9+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="9 16" {!> ../../../docs_src/request_files/tutorial003_py39.py!} ``` -=== "Python 3.6+" +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. - ```Python hl_lines="18" + ```Python hl_lines="11 18" {!> ../../../docs_src/request_files/tutorial003.py!} ``` -## 小结 +## Recap -本节介绍了如何用 `File` 把上传文件声明为(表单数据的)输入参数。 +Use `File`, `bytes`, and `UploadFile` to declare files to be uploaded in the request, sent as form data. From 9d1b1a384995963e63b4b4eb1a593c8bc004386c Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:50 +0800 Subject: [PATCH 091/163] New translations request-forms-and-files.md (Chinese Simplified) --- .../docs/tutorial/request-forms-and-files.md | 74 +++++++++++++------ 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/docs/zh/docs/tutorial/request-forms-and-files.md b/docs/zh/docs/tutorial/request-forms-and-files.md index 70cd70f98650c..1818946c4e087 100644 --- a/docs/zh/docs/tutorial/request-forms-and-files.md +++ b/docs/zh/docs/tutorial/request-forms-and-files.md @@ -1,37 +1,69 @@ -# 请求表单与文件 +# Request Forms and Files -FastAPI 支持同时使用 `File` 和 `Form` 定义文件和表单字段。 +You can define files and form fields at the same time using `File` and `Form`. -!!! info "说明" +!!! info + To receive uploaded files and/or form data, first install `python-multipart`. - 接收上传文件或表单数据,要预先安装 `python-multipart`。 + E.g. `pip install python-multipart`. - 例如,`pip install python-multipart`。 +## Import `File` and `Form` -## 导入 `File` 与 `Form` +=== "Python 3.9+" -```Python hl_lines="1" -{!../../../docs_src/request_forms_and_files/tutorial001.py!} -``` + ```Python hl_lines="3" + {!> ../../../docs_src/request_forms_and_files/tutorial001_an_py39.py!} + ``` -## 定义 `File` 与 `Form` 参数 +=== "Python 3.6+" -创建文件和表单参数的方式与 `Body` 和 `Query` 一样: + ```Python hl_lines="1" + {!> ../../../docs_src/request_forms_and_files/tutorial001_an.py!} + ``` -```Python hl_lines="8" -{!../../../docs_src/request_forms_and_files/tutorial001.py!} -``` +=== "Python 3.6+ non-Annotated" -文件和表单字段作为表单数据上传与接收。 + !!! tip + Prefer to use the `Annotated` version if possible. -声明文件可以使用 `bytes` 或 `UploadFile` 。 + ```Python hl_lines="1" + {!> ../../../docs_src/request_forms_and_files/tutorial001.py!} + ``` -!!! warning "警告" +## Define `File` and `Form` parameters - 可在一个*路径操作*中声明多个 `File` 与 `Form` 参数,但不能同时声明要接收 JSON 的 `Body` 字段。因为此时请求体的编码为 `multipart/form-data`,不是 `application/json`。 +Create file and form parameters the same way you would for `Body` or `Query`: - 这不是 **FastAPI** 的问题,而是 HTTP 协议的规定。 +=== "Python 3.9+" -## 小结 + ```Python hl_lines="10-12" + {!> ../../../docs_src/request_forms_and_files/tutorial001_an_py39.py!} + ``` -在同一个请求中接收数据和文件时,应同时使用 `File` 和 `Form`。 +=== "Python 3.6+" + + ```Python hl_lines="9-11" + {!> ../../../docs_src/request_forms_and_files/tutorial001_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="8" + {!> ../../../docs_src/request_forms_and_files/tutorial001.py!} + ``` + +The files and form fields will be uploaded as form data and you will receive the files and form fields. + +And you can declare some of the files as `bytes` and some as `UploadFile`. + +!!! warning + You can declare multiple `File` and `Form` parameters in a *path operation*, but you can't also declare `Body` fields that you expect to receive as JSON, as the request will have the body encoded using `multipart/form-data` instead of `application/json`. + + This is not a limitation of **FastAPI**, it's part of the HTTP protocol. + +## Recap + +Use `File` and `Form` together when you need to receive data and files in the same request. From 8f7116d3f9905d0c5e308fabbcc4d8b02b97eecf Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:51 +0800 Subject: [PATCH 092/163] New translations request-forms.md (Chinese Simplified) --- docs/zh/docs/tutorial/request-forms.md | 97 +++++++++++++++++--------- 1 file changed, 63 insertions(+), 34 deletions(-) diff --git a/docs/zh/docs/tutorial/request-forms.md b/docs/zh/docs/tutorial/request-forms.md index 6436ffbcdc26e..1a608f33f5a3b 100644 --- a/docs/zh/docs/tutorial/request-forms.md +++ b/docs/zh/docs/tutorial/request-forms.md @@ -1,63 +1,92 @@ -# 表单数据 +# Form Data -接收的不是 JSON,而是表单字段时,要使用 `Form`。 +When you need to receive form fields instead of JSON, you can use `Form`. -!!! info "说明" +!!! info + To use forms, first install `python-multipart`. - 要使用表单,需预先安装 `python-multipart`。 + E.g. `pip install python-multipart`. - 例如,`pip install python-multipart`。 +## Import `Form` -## 导入 `Form` +Import `Form` from `fastapi`: -从 `fastapi` 导入 `Form`: +=== "Python 3.9+" -```Python hl_lines="1" -{!../../../docs_src/request_forms/tutorial001.py!} -``` + ```Python hl_lines="3" + {!> ../../../docs_src/request_forms/tutorial001_an_py39.py!} + ``` -## 定义 `Form` 参数 +=== "Python 3.6+" -创建表单(`Form`)参数的方式与 `Body` 和 `Query` 一样: + ```Python hl_lines="1" + {!> ../../../docs_src/request_forms/tutorial001_an.py!} + ``` -```Python hl_lines="7" -{!../../../docs_src/request_forms/tutorial001.py!} -``` +=== "Python 3.6+ non-Annotated" -例如,OAuth2 规范的 "密码流" 模式规定要通过表单字段发送 `username` 和 `password`。 + !!! tip + Prefer to use the `Annotated` version if possible. -该规范要求字段必须命名为 `username` 和 `password`,并通过表单字段发送,不能用 JSON。 + ```Python hl_lines="1" + {!> ../../../docs_src/request_forms/tutorial001.py!} + ``` -使用 `Form` 可以声明与 `Body` (及 `Query`、`Path`、`Cookie`)相同的元数据和验证。 +## Define `Form` parameters -!!! info "说明" +Create form parameters the same way you would for `Body` or `Query`: - `Form` 是直接继承自 `Body` 的类。 +=== "Python 3.9+" -!!! tip "提示" + ```Python hl_lines="9" + {!> ../../../docs_src/request_forms/tutorial001_an_py39.py!} + ``` - 声明表单体要显式使用 `Form` ,否则,FastAPI 会把该参数当作查询参数或请求体(JSON)参数。 +=== "Python 3.6+" -## 关于 "表单字段" + ```Python hl_lines="8" + {!> ../../../docs_src/request_forms/tutorial001_an.py!} + ``` -与 JSON 不同,HTML 表单(`
`)向服务器发送数据通常使用「特殊」的编码。 +=== "Python 3.6+ non-Annotated" -**FastAPI** 要确保从正确的位置读取数据,而不是读取 JSON。 + !!! tip + Prefer to use the `Annotated` version if possible. -!!! note "技术细节" + ```Python hl_lines="7" + {!> ../../../docs_src/request_forms/tutorial001.py!} + ``` - 表单数据的「媒体类型」编码一般为 `application/x-www-form-urlencoded`。 +For example, in one of the ways the OAuth2 specification can be used (called "password flow") it is required to send a `username` and `password` as form fields. - 但包含文件的表单编码为 `multipart/form-data`。文件处理详见下节。 +The spec requires the fields to be exactly named `username` and `password`, and to be sent as form fields, not JSON. - 编码和表单字段详见 MDN Web 文档的 POST小节。 +With `Form` you can declare the same configurations as with `Body` (and `Query`, `Path`, `Cookie`), including validation, examples, an alias (e.g. `user-name` instead of `username`), etc. -!!! warning "警告" +!!! info + `Form` is a class that inherits directly from `Body`. - 可在一个*路径操作*中声明多个 `Form` 参数,但不能同时声明要接收 JSON 的 `Body` 字段。因为此时请求体的编码是 `application/x-www-form-urlencoded`,不是 `application/json`。 +!!! tip + To declare form bodies, you need to use `Form` explicitly, because without it the parameters would be interpreted as query parameters or body (JSON) parameters. - 这不是 **FastAPI** 的问题,而是 HTTP 协议的规定。 +## About "Form Fields" -## 小结 +The way HTML forms (`
`) sends the data to the server normally uses a "special" encoding for that data, it's different from JSON. -本节介绍了如何使用 `Form` 声明表单数据输入参数。 +**FastAPI** will make sure to read that data from the right place instead of JSON. + +!!! note "Technical Details" + Data from forms is normally encoded using the "media type" `application/x-www-form-urlencoded`. + + But when the form includes files, it is encoded as `multipart/form-data`. You'll read about handling files in the next chapter. + + If you want to read more about these encodings and form fields, head to the MDN web docs for POST. + +!!! warning + You can declare multiple `Form` parameters in a *path operation*, but you can't also declare `Body` fields that you expect to receive as JSON, as the request will have the body encoded using `application/x-www-form-urlencoded` instead of `application/json`. + + This is not a limitation of **FastAPI**, it's part of the HTTP protocol. + +## Recap + +Use `Form` to declare form data input parameters. From 80dfb4521dd71030390e194ab51c2021d50032c5 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:52 +0800 Subject: [PATCH 093/163] New translations response-model.md (Chinese Simplified) --- docs/zh/docs/tutorial/response-model.md | 464 +++++++++++++++++++----- 1 file changed, 368 insertions(+), 96 deletions(-) diff --git a/docs/zh/docs/tutorial/response-model.md b/docs/zh/docs/tutorial/response-model.md index ea3d0666ded6e..9c849bf3a0453 100644 --- a/docs/zh/docs/tutorial/response-model.md +++ b/docs/zh/docs/tutorial/response-model.md @@ -1,118 +1,374 @@ -# 响应模型 +# Response Model - Return Type -你可以在任意的*路径操作*中使用 `response_model` 参数来声明用于响应的模型: +You can declare the type used for the response by annotating the *path operation function* **return type**. + +You can use **type annotations** the same way you would for input data in function **parameters**, you can use Pydantic models, lists, dictionaries, scalar values like integers, booleans, etc. + +=== "Python 3.10+" + + ```Python hl_lines="16 21" + {!> ../../../docs_src/response_model/tutorial001_01_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="18 23" + {!> ../../../docs_src/response_model/tutorial001_01_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="18 23" + {!> ../../../docs_src/response_model/tutorial001_01.py!} + ``` + +FastAPI will use this return type to: + +* **Validate** the returned data. + * If the data is invalid (e.g. you are missing a field), it means that *your* app code is broken, not returning what it should, and it will return a server error instead of returning incorrect data. This way you and your clients can be certain that they will receive the data and the data shape expected. +* Add a **JSON Schema** for the response, in the OpenAPI *path operation*. + * This will be used by the **automatic docs**. + * It will also be used by automatic client code generation tools. + +But most importantly: + +* It will **limit and filter** the output data to what is defined in the return type. + * This is particularly important for **security**, we'll see more of that below. + +## `response_model` Parameter + +There are some cases where you need or want to return some data that is not exactly what the type declares. + +For example, you could want to **return a dictionary** or a database object, but **declare it as a Pydantic model**. This way the Pydantic model would do all the data documentation, validation, etc. for the object that you returned (e.g. a dictionary or database object). + +If you added the return type annotation, tools and editors would complain with a (correct) error telling you that your function is returning a type (e.g. a dict) that is different from what you declared (e.g. a Pydantic model). + +In those cases, you can use the *path operation decorator* parameter `response_model` instead of the return type. + +You can use the `response_model` parameter in any of the *path operations*: * `@app.get()` * `@app.post()` * `@app.put()` * `@app.delete()` -* 等等。 +* etc. -```Python hl_lines="17" -{!../../../docs_src/response_model/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="17 22 24-27" + {!> ../../../docs_src/response_model/tutorial001_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="17 22 24-27" + {!> ../../../docs_src/response_model/tutorial001_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="17 22 24-27" + {!> ../../../docs_src/response_model/tutorial001.py!} + ``` !!! note - 注意,`response_model`是「装饰器」方法(`get`,`post` 等)的一个参数。不像之前的所有参数和请求体,它不属于*路径操作函数*。 + Notice that `response_model` is a parameter of the "decorator" method (`get`, `post`, etc). Not of your *path operation function*, like all the parameters and body. -它接收的类型与你将为 Pydantic 模型属性所声明的类型相同,因此它可以是一个 Pydantic 模型,但也可以是一个由 Pydantic 模型组成的 `list`,例如 `List[Item]`。 +`response_model` receives the same type you would declare for a Pydantic model field, so, it can be a Pydantic model, but it can also be, e.g. a `list` of Pydantic models, like `List[Item]`. -FastAPI 将使用此 `response_model` 来: +FastAPI will use this `response_model` to do all the data documentation, validation, etc. and also to **convert and filter the output data** to its type declaration. -* 将输出数据转换为其声明的类型。 -* 校验数据。 -* 在 OpenAPI 的*路径操作*中为响应添加一个 JSON Schema。 -* 并在自动生成文档系统中使用。 +!!! tip + If you have strict type checks in your editor, mypy, etc, you can declare the function return type as `Any`. -但最重要的是: + That way you tell the editor that you are intentionally returning anything. But FastAPI will still do the data documentation, validation, filtering, etc. with the `response_model`. -* 会将输出数据限制在该模型定义内。下面我们会看到这一点有多重要。 +### `response_model` Priority -!!! note "技术细节" - 响应模型在参数中被声明,而不是作为函数返回类型的注解,这是因为路径函数可能不会真正返回该响应模型,而是返回一个 `dict`、数据库对象或其他模型,然后再使用 `response_model` 来执行字段约束和序列化。 +If you declare both a return type and a `response_model`, the `response_model` will take priority and be used by FastAPI. -## 返回与输入相同的数据 +This way you can add correct type annotations to your functions even when you are returning a type different than the response model, to be used by the editor and tools like mypy. And still you can have FastAPI do the data validation, documentation, etc. using the `response_model`. -现在我们声明一个 `UserIn` 模型,它将包含一个明文密码属性。 +You can also use `response_model=None` to disable creating a response model for that *path operation*, you might need to do it if you are adding type annotations for things that are not valid Pydantic fields, you will see an example of that in one of the sections below. -```Python hl_lines="9 11" -{!../../../docs_src/response_model/tutorial002.py!} -``` +## Return the same input data -我们正在使用此模型声明输入数据,并使用同一模型声明输出数据: +Here we are declaring a `UserIn` model, it will contain a plaintext password: -```Python hl_lines="17-18" -{!../../../docs_src/response_model/tutorial002.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="7 9" + {!> ../../../docs_src/response_model/tutorial002_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9 11" + {!> ../../../docs_src/response_model/tutorial002.py!} + ``` + +!!! info + To use `EmailStr`, first install `email_validator`. + + E.g. `pip install email-validator` + or `pip install pydantic[email]`. + +And we are using this model to declare our input and the same model to declare our output: + +=== "Python 3.10+" -现在,每当浏览器使用一个密码创建用户时,API 都会在响应中返回相同的密码。 + ```Python hl_lines="16" + {!> ../../../docs_src/response_model/tutorial002_py310.py!} + ``` -在这个案例中,这可能不算是问题,因为用户自己正在发送密码。 +=== "Python 3.6+" -但是,如果我们在其他的*路径操作*中使用相同的模型,则可能会将用户的密码发送给每个客户端。 + ```Python hl_lines="18" + {!> ../../../docs_src/response_model/tutorial002.py!} + ``` + +Now, whenever a browser is creating a user with a password, the API will return the same password in the response. + +In this case, it might not be a problem, because it's the same user sending the password. + +But if we use the same model for another *path operation*, we could be sending our user's passwords to every client. !!! danger - 永远不要存储用户的明文密码,也不要在响应中发送密码。 + Never store the plain password of a user or send it in a response like this, unless you know all the caveats and you know what you are doing. -## 添加输出模型 +## Add an output model -相反,我们可以创建一个有明文密码的输入模型和一个没有明文密码的输出模型: +We can instead create an input model with the plaintext password and an output model without it: -```Python hl_lines="9 11 16" -{!../../../docs_src/response_model/tutorial003.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="9 11 16" + {!> ../../../docs_src/response_model/tutorial003_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9 11 16" + {!> ../../../docs_src/response_model/tutorial003.py!} + ``` + +Here, even though our *path operation function* is returning the same input user that contains the password: + +=== "Python 3.10+" + + ```Python hl_lines="24" + {!> ../../../docs_src/response_model/tutorial003_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="24" + {!> ../../../docs_src/response_model/tutorial003.py!} + ``` + +...we declared the `response_model` to be our model `UserOut`, that doesn't include the password: + +=== "Python 3.10+" + + ```Python hl_lines="22" + {!> ../../../docs_src/response_model/tutorial003_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="22" + {!> ../../../docs_src/response_model/tutorial003.py!} + ``` + +So, **FastAPI** will take care of filtering out all the data that is not declared in the output model (using Pydantic). + +### `response_model` or Return Type + +In this case, because the two models are different, if we annotated the function return type as `UserOut`, the editor and tools would complain that we are returning an invalid type, as those are different classes. + +That's why in this example we have to declare it in the `response_model` parameter. + +...but continue reading below to see how to overcome that. + +## Return Type and Data Filtering + +Let's continue from the previous example. We wanted to **annotate the function with one type** but return something that includes **more data**. + +We want FastAPI to keep **filtering** the data using the response model. + +In the previous example, because the classes were different, we had to use the `response_model` parameter. But that also means that we don't get the support from the editor and tools checking the function return type. -这样,即便我们的*路径操作函数*将会返回包含密码的相同输入用户: +But in most of the cases where we need to do something like this, we want the model just to **filter/remove** some of the data as in this example. -```Python hl_lines="24" -{!../../../docs_src/response_model/tutorial003.py!} +And in those cases, we can use classes and inheritance to take advantage of function **type annotations** to get better support in the editor and tools, and still get the FastAPI **data filtering**. + +=== "Python 3.10+" + + ```Python hl_lines="7-10 13-14 18" + {!> ../../../docs_src/response_model/tutorial003_01_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9-13 15-16 20" + {!> ../../../docs_src/response_model/tutorial003_01.py!} + ``` + +With this, we get tooling support, from editors and mypy as this code is correct in terms of types, but we also get the data filtering from FastAPI. + +How does this work? Let's check that out. 🤓 + +### Type Annotations and Tooling + +First let's see how editors, mypy and other tools would see this. + +`BaseUser` has the base fields. Then `UserIn` inherits from `BaseUser` and adds the `password` field, so, it will include all the fields from both models. + +We annotate the function return type as `BaseUser`, but we are actually returning a `UserIn` instance. + +The editor, mypy, and other tools won't complain about this because, in typing terms, `UserIn` is a subclass of `BaseUser`, which means it's a *valid* type when what is expected is anything that is a `BaseUser`. + +### FastAPI Data Filtering + +Now, for FastAPI, it will see the return type and make sure that what you return includes **only** the fields that are declared in the type. + +FastAPI does several things internally with Pydantic to make sure that those same rules of class inheritance are not used for the returned data filtering, otherwise you could end up returning much more data than what you expected. + +This way, you can get the best of both worlds: type annotations with **tooling support** and **data filtering**. + +## See it in the docs + +When you see the automatic docs, you can check that the input model and output model will both have their own JSON Schema: + + + +And both models will be used for the interactive API documentation: + + + +## Other Return Type Annotations + +There might be cases where you return something that is not a valid Pydantic field and you annotate it in the function, only to get the support provided by tooling (the editor, mypy, etc). + +### Return a Response Directly + +The most common case would be [returning a Response directly as explained later in the advanced docs](../advanced/response-directly.md){.internal-link target=_blank}. + +```Python hl_lines="8 10-11" +{!> ../../../docs_src/response_model/tutorial003_02.py!} ``` -...我们已经将 `response_model` 声明为了不包含密码的 `UserOut` 模型: +This simple case is handled automatically by FastAPI because the return type annotation is the class (or a subclass) of `Response`. + +And tools will also be happy because both `RedirectResponse` and `JSONResponse` are subclasses of `Response`, so the type annotation is correct. + +### Annotate a Response Subclass -```Python hl_lines="22" -{!../../../docs_src/response_model/tutorial003.py!} +You can also use a subclass of `Response` in the type annotation: + +```Python hl_lines="8-9" +{!> ../../../docs_src/response_model/tutorial003_03.py!} ``` -因此,**FastAPI** 将会负责过滤掉未在输出模型中声明的所有数据(使用 Pydantic)。 +This will also work because `RedirectResponse` is a subclass of `Response`, and FastAPI will automatically handle this simple case. -## 在文档中查看 +### Invalid Return Type Annotations -当你查看自动化文档时,你可以检查输入模型和输出模型是否都具有自己的 JSON Schema: +But when you return some other arbitrary object that is not a valid Pydantic type (e.g. a database object) and you annotate it like that in the function, FastAPI will try to create a Pydantic response model from that type annotation, and will fail. - +The same would happen if you had something like a union between different types where one or more of them are not valid Pydantic types, for example this would fail 💥: -并且两种模型都将在交互式 API 文档中使用: +=== "Python 3.10+" - + ```Python hl_lines="8" + {!> ../../../docs_src/response_model/tutorial003_04_py310.py!} + ``` -## 响应模型编码参数 +=== "Python 3.6+" -你的响应模型可以具有默认值,例如: + ```Python hl_lines="10" + {!> ../../../docs_src/response_model/tutorial003_04.py!} + ``` -```Python hl_lines="11 13-14" -{!../../../docs_src/response_model/tutorial004.py!} -``` +...this fails because the type annotation is not a Pydantic type and is not just a single `Response` class or subclass, it's a union (any of the two) between a `Response` and a `dict`. -* `description: Union[str, None] = None` 具有默认值 `None`。 -* `tax: float = 10.5` 具有默认值 `10.5`. -* `tags: List[str] = []` 具有一个空列表作为默认值: `[]`. +### Disable Response Model -但如果它们并没有存储实际的值,你可能想从结果中忽略它们的默认值。 +Continuing from the example above, you might not want to have the default data validation, documentation, filtering, etc. that is performed by FastAPI. -举个例子,当你在 NoSQL 数据库中保存了具有许多可选属性的模型,但你又不想发送充满默认值的很长的 JSON 响应。 +But you might want to still keep the return type annotation in the function to get the support from tools like editors and type checkers (e.g. mypy). -### 使用 `response_model_exclude_unset` 参数 +In this case, you can disable the response model generation by setting `response_model=None`: -你可以设置*路径操作装饰器*的 `response_model_exclude_unset=True` 参数: +=== "Python 3.10+" -```Python hl_lines="24" -{!../../../docs_src/response_model/tutorial004.py!} -``` + ```Python hl_lines="7" + {!> ../../../docs_src/response_model/tutorial003_05_py310.py!} + ``` -然后响应中将不会包含那些默认值,而是仅有实际设置的值。 +=== "Python 3.6+" -因此,如果你向*路径操作*发送 ID 为 `foo` 的商品的请求,则响应(不包括默认值)将为: + ```Python hl_lines="9" + {!> ../../../docs_src/response_model/tutorial003_05.py!} + ``` + +This will make FastAPI skip the response model generation and that way you can have any return type annotations you need without it affecting your FastAPI application. 🤓 + +## Response Model encoding parameters + +Your response model could have default values, like: + +=== "Python 3.10+" + + ```Python hl_lines="9 11-12" + {!> ../../../docs_src/response_model/tutorial004_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="11 13-14" + {!> ../../../docs_src/response_model/tutorial004_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="11 13-14" + {!> ../../../docs_src/response_model/tutorial004.py!} + ``` + +* `description: Union[str, None] = None` (or `str | None = None` in Python 3.10) has a default of `None`. +* `tax: float = 10.5` has a default of `10.5`. +* `tags: List[str] = []` as a default of an empty list: `[]`. + +but you might want to omit them from the result if they were not actually stored. + +For example, if you have models with many optional attributes in a NoSQL database, but you don't want to send very long JSON responses full of default values. + +### Use the `response_model_exclude_unset` parameter + +You can set the *path operation decorator* parameter `response_model_exclude_unset=True`: + +=== "Python 3.10+" + + ```Python hl_lines="22" + {!> ../../../docs_src/response_model/tutorial004_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="24" + {!> ../../../docs_src/response_model/tutorial004_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="24" + {!> ../../../docs_src/response_model/tutorial004.py!} + ``` + +and those default values won't be included in the response, only the values actually set. + +So, if you send a request to that *path operation* for the item with ID `foo`, the response (not including default values) will be: ```JSON { @@ -122,19 +378,19 @@ FastAPI 将使用此 `response_model` 来: ``` !!! info - FastAPI 通过 Pydantic 模型的 `.dict()` 配合 该方法的 `exclude_unset` 参数 来实现此功能。 + FastAPI uses Pydantic model's `.dict()` with its `exclude_unset` parameter to achieve this. !!! info - 你还可以使用: + You can also use: * `response_model_exclude_defaults=True` * `response_model_exclude_none=True` - 参考 Pydantic 文档 中对 `exclude_defaults` 和 `exclude_none` 的描述。 + as described in the Pydantic docs for `exclude_defaults` and `exclude_none`. -#### 默认值字段有实际值的数据 +#### Data with values for fields with defaults -但是,如果你的数据在具有默认值的模型字段中有实际的值,例如 ID 为 `bar` 的项: +But if your data has values for the model's fields with default values, like the item with ID `bar`: ```Python hl_lines="3 5" { @@ -145,11 +401,11 @@ FastAPI 将使用此 `response_model` 来: } ``` -这些值将包含在响应中。 +they will be included in the response. -#### 具有与默认值相同值的数据 +#### Data with the same values as the defaults -如果数据具有与默认值相同的值,例如 ID 为 `baz` 的项: +If the data has the same values as the default ones, like the item with ID `baz`: ```Python hl_lines="3 5-6" { @@ -161,49 +417,65 @@ FastAPI 将使用此 `response_model` 来: } ``` -即使 `description`、`tax` 和 `tags` 具有与默认值相同的值,FastAPI 足够聪明 (实际上是 Pydantic 足够聪明) 去认识到这一点,它们的值被显式地所设定(而不是取自默认值)。 +FastAPI is smart enough (actually, Pydantic is smart enough) to realize that, even though `description`, `tax`, and `tags` have the same values as the defaults, they were set explicitly (instead of taken from the defaults). -因此,它们将包含在 JSON 响应中。 +So, they will be included in the JSON response. !!! tip - 请注意默认值可以是任何值,而不仅是`None`。 + Notice that the default values can be anything, not only `None`. - 它们可以是一个列表(`[]`),一个值为 `10.5`的 `float`,等等。 + They can be a list (`[]`), a `float` of `10.5`, etc. -### `response_model_include` 和 `response_model_exclude` +### `response_model_include` and `response_model_exclude` -你还可以使用*路径操作装饰器*的 `response_model_include` 和 `response_model_exclude` 参数。 +You can also use the *path operation decorator* parameters `response_model_include` and `response_model_exclude`. -它们接收一个由属性名称 `str` 组成的 `set` 来包含(忽略其他的)或者排除(包含其他的)这些属性。 +They take a `set` of `str` with the name of the attributes to include (omitting the rest) or to exclude (including the rest). -如果你只有一个 Pydantic 模型,并且想要从输出中移除一些数据,则可以使用这种快捷方法。 +This can be used as a quick shortcut if you have only one Pydantic model and want to remove some data from the output. !!! tip - 但是依然建议你使用上面提到的主意,使用多个类而不是这些参数。 + But it is still recommended to use the ideas above, using multiple classes, instead of these parameters. - 这是因为即使使用 `response_model_include` 或 `response_model_exclude` 来省略某些属性,在应用程序的 OpenAPI 定义(和文档)中生成的 JSON Schema 仍将是完整的模型。 + This is because the JSON Schema generated in your app's OpenAPI (and the docs) will still be the one for the complete model, even if you use `response_model_include` or `response_model_exclude` to omit some attributes. + + This also applies to `response_model_by_alias` that works similarly. - 这也适用于作用类似的 `response_model_by_alias`。 +=== "Python 3.10+" -```Python hl_lines="31 37" -{!../../../docs_src/response_model/tutorial005.py!} -``` + ```Python hl_lines="29 35" + {!> ../../../docs_src/response_model/tutorial005_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="31 37" + {!> ../../../docs_src/response_model/tutorial005.py!} + ``` !!! tip - `{"name", "description"}` 语法创建一个具有这两个值的 `set`。 + The syntax `{"name", "description"}` creates a `set` with those two values. - 等同于 `set(["name", "description"])`。 + It is equivalent to `set(["name", "description"])`. -#### 使用 `list` 而不是 `set` +#### Using `list`s instead of `set`s -如果你忘记使用 `set` 而是使用 `list` 或 `tuple`,FastAPI 仍会将其转换为 `set` 并且正常工作: +If you forget to use a `set` and use a `list` or `tuple` instead, FastAPI will still convert it to a `set` and it will work correctly: -```Python hl_lines="31 37" -{!../../../docs_src/response_model/tutorial006.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="29 35" + {!> ../../../docs_src/response_model/tutorial006_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="31 37" + {!> ../../../docs_src/response_model/tutorial006.py!} + ``` -## 总结 +## Recap -使用*路径操作装饰器*的 `response_model` 参数来定义响应模型,特别是确保私有数据被过滤掉。 +Use the *path operation decorator's* parameter `response_model` to define response models and especially to ensure private data is filtered out. -使用 `response_model_exclude_unset` 来仅返回显式设定的值。 +Use `response_model_exclude_unset` to return only the values explicitly set. From dade6ef2df8bacc5531d9c12ebd368c9fd16e49a Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:53 +0800 Subject: [PATCH 094/163] New translations response-status-code.md (Chinese Simplified) --- docs/zh/docs/tutorial/response-status-code.md | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/docs/zh/docs/tutorial/response-status-code.md b/docs/zh/docs/tutorial/response-status-code.md index 3578319423adc..8e26706e53d62 100644 --- a/docs/zh/docs/tutorial/response-status-code.md +++ b/docs/zh/docs/tutorial/response-status-code.md @@ -1,89 +1,89 @@ -# 响应状态码 +# Response Status Code -与指定响应模型的方式相同,你也可以在以下任意的*路径操作*中使用 `status_code` 参数来声明用于响应的 HTTP 状态码: +The same way you can specify a response model, you can also declare the HTTP status code used for the response with the parameter `status_code` in any of the *path operations*: * `@app.get()` * `@app.post()` * `@app.put()` * `@app.delete()` -* 等等。 +* etc. ```Python hl_lines="6" {!../../../docs_src/response_status_code/tutorial001.py!} ``` !!! note - 注意,`status_code` 是「装饰器」方法(`get`,`post` 等)的一个参数。不像之前的所有参数和请求体,它不属于*路径操作函数*。 + Notice that `status_code` is a parameter of the "decorator" method (`get`, `post`, etc). Not of your *path operation function*, like all the parameters and body. -`status_code` 参数接收一个表示 HTTP 状态码的数字。 +The `status_code` parameter receives a number with the HTTP status code. !!! info - `status_code` 也能够接收一个 `IntEnum` 类型,比如 Python 的 `http.HTTPStatus`。 + `status_code` can alternatively also receive an `IntEnum`, such as Python's `http.HTTPStatus`. -它将会: +It will: -* 在响应中返回该状态码。 -* 在 OpenAPI 模式中(以及在用户界面中)将其记录为: +* Return that status code in the response. +* Document it as such in the OpenAPI schema (and so, in the user interfaces): - + !!! note - 一些响应状态码(请参阅下一部分)表示响应没有响应体。 + Some response codes (see the next section) indicate that the response does not have a body. - FastAPI 知道这一点,并将生成表明没有响应体的 OpenAPI 文档。 + FastAPI knows this, and will produce OpenAPI docs that state there is no response body. -## 关于 HTTP 状态码 +## About HTTP status codes !!! note - 如果你已经了解什么是 HTTP 状态码,请跳到下一部分。 + If you already know what HTTP status codes are, skip to the next section. -在 HTTP 协议中,你将发送 3 位数的数字状态码作为响应的一部分。 +In HTTP, you send a numeric status code of 3 digits as part of the response. -这些状态码有一个识别它们的关联名称,但是重要的还是数字。 +These status codes have a name associated to recognize them, but the important part is the number. -简而言之: +In short: -* `100` 及以上状态码用于「消息」响应。你很少直接使用它们。具有这些状态代码的响应不能带有响应体。 -* **`200`** 及以上状态码用于「成功」响应。这些是你最常使用的。 - * `200` 是默认状态代码,它表示一切「正常」。 - * 另一个例子会是 `201`,「已创建」。它通常在数据库中创建了一条新记录后使用。 - * 一个特殊的例子是 `204`,「无内容」。此响应在没有内容返回给客户端时使用,因此该响应不能包含响应体。 -* **`300`** 及以上状态码用于「重定向」。具有这些状态码的响应可能有或者可能没有响应体,但 `304`「未修改」是个例外,该响应不得含有响应体。 -* **`400`** 及以上状态码用于「客户端错误」响应。这些可能是你第二常使用的类型。 - * 一个例子是 `404`,用于「未找到」响应。 - * 对于来自客户端的一般错误,你可以只使用 `400`。 -* `500` 及以上状态码用于服务器端错误。你几乎永远不会直接使用它们。当你的应用程序代码或服务器中的某些部分出现问题时,它将自动返回这些状态代码之一。 +* `100` and above are for "Information". You rarely use them directly. Responses with these status codes cannot have a body. +* **`200`** and above are for "Successful" responses. These are the ones you would use the most. + * `200` is the default status code, which means everything was "OK". + * Another example would be `201`, "Created". It is commonly used after creating a new record in the database. + * A special case is `204`, "No Content". This response is used when there is no content to return to the client, and so the response must not have a body. +* **`300`** and above are for "Redirection". Responses with these status codes may or may not have a body, except for `304`, "Not Modified", which must not have one. +* **`400`** and above are for "Client error" responses. These are the second type you would probably use the most. + * An example is `404`, for a "Not Found" response. + * For generic errors from the client, you can just use `400`. +* `500` and above are for server errors. You almost never use them directly. When something goes wrong at some part in your application code, or server, it will automatically return one of these status codes. !!! tip - 要了解有关每个状态代码以及适用场景的更多信息,请查看 MDN 关于 HTTP 状态码的文档。 + To know more about each status code and which code is for what, check the MDN documentation about HTTP status codes. -## 记住名称的捷径 +## Shortcut to remember the names -让我们再次看看之前的例子: +Let's see the previous example again: ```Python hl_lines="6" {!../../../docs_src/response_status_code/tutorial001.py!} ``` -`201` 是表示「已创建」的状态码。 +`201` is the status code for "Created". -但是你不必去记住每个代码的含义。 +But you don't have to memorize what each of these codes mean. -你可以使用来自 `fastapi.status` 的便捷变量。 +You can use the convenience variables from `fastapi.status`. ```Python hl_lines="1 6" {!../../../docs_src/response_status_code/tutorial002.py!} ``` -它们只是一种便捷方式,它们具有同样的数字代码,但是这样使用你就可以使用编辑器的自动补全功能来查找它们: +They are just a convenience, they hold the same number, but that way you can use the editor's autocomplete to find them: - + -!!! note "技术细节" - 你也可以使用 `from starlette import status`。 +!!! note "Technical Details" + You could also use `from starlette import status`. - 为了给你(即开发者)提供方便,**FastAPI** 提供了与 `starlette.status` 完全相同的 `fastapi.status`。但它直接来自于 Starlette。 + **FastAPI** provides the same `starlette.status` as `fastapi.status` just as a convenience for you, the developer. But it comes directly from Starlette. -## 更改默认状态码 +## Changing the default -稍后,在[高级用户指南](../advanced/response-change-status-code.md){.internal-link target=_blank}中你将了解如何返回与在此声明的默认状态码不同的状态码。 +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. From 7bcf2c38091ca8a6ce79318f74d37d1f2d9fc7c4 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:54 +0800 Subject: [PATCH 095/163] New translations schema-extra-example.md (Chinese Simplified) --- docs/zh/docs/tutorial/schema-extra-example.md | 263 +++++++++++++++--- 1 file changed, 231 insertions(+), 32 deletions(-) diff --git a/docs/zh/docs/tutorial/schema-extra-example.md b/docs/zh/docs/tutorial/schema-extra-example.md index 8f5fbfe70bbb5..9b24aa3702e80 100644 --- a/docs/zh/docs/tutorial/schema-extra-example.md +++ b/docs/zh/docs/tutorial/schema-extra-example.md @@ -1,58 +1,257 @@ -# 模式的额外信息 - 例子 +# Declare Request Example Data -您可以在JSON模式中定义额外的信息。 +You can declare examples of the data your app can receive. -一个常见的用例是添加一个将在文档中显示的`example`。 +Here are several ways to do it. -有几种方法可以声明额外的 JSON 模式信息。 +## Extra JSON Schema data in Pydantic models -## Pydantic `schema_extra` +You can declare `examples` for a Pydantic model that will be added to the generated JSON Schema. -您可以使用 `Config` 和 `schema_extra` 为Pydantic模型声明一个示例,如Pydantic 文档:定制 Schema 中所述: +=== "Python 3.10+ Pydantic v2" -```Python hl_lines="15-23" -{!../../../docs_src/schema_extra_example/tutorial001.py!} -``` + ```Python hl_lines="13-24" + {!> ../../../docs_src/schema_extra_example/tutorial001_py310.py!} + ``` -这些额外的信息将按原样添加到输出的JSON模式中。 +=== "Python 3.10+ Pydantic v1" -## `Field` 的附加参数 + ```Python hl_lines="13-23" + {!> ../../../docs_src/schema_extra_example/tutorial001_py310_pv1.py!} + ``` -在 `Field`, `Path`, `Query`, `Body` 和其他你之后将会看到的工厂函数,你可以为JSON 模式声明额外信息,你也可以通过给工厂函数传递其他的任意参数来给JSON 模式声明额外信息,比如增加 `example`: +=== "Python 3.6+ Pydantic v2" -```Python hl_lines="4 10-13" -{!../../../docs_src/schema_extra_example/tutorial002.py!} -``` + ```Python hl_lines="15-26" + {!> ../../../docs_src/schema_extra_example/tutorial001.py!} + ``` + +=== "Python 3.6+ Pydantic v1" + + ```Python hl_lines="15-25" + {!> ../../../docs_src/schema_extra_example/tutorial001_pv1.py!} + ``` + +That extra info will be added as-is to the output **JSON Schema** for that model, and it will be used in the API docs. + +=== "Pydantic v2" + + In Pydantic version 2, you would use the attribute `model_config`, that takes a `dict` as described in Pydantic's docs: Model Config. + + You can set `"json_schema_extra"` with a `dict` containing any additonal data you would like to show up in the generated JSON Schema, including `examples`. + +=== "Pydantic v1" + + In Pydantic version 1, you would use an internal class `Config` and `schema_extra`, as described in Pydantic's docs: Schema customization. + + You can set `schema_extra` with a `dict` containing any additonal data you would like to show up in the generated JSON Schema, including `examples`. + +!!! tip + You could use the same technique to extend the JSON Schema and add your own custom extra info. + + For example you could use it to add metadata for a frontend user interface, etc. + +!!! info + OpenAPI 3.1.0 (used since FastAPI 0.99.0) added support for `examples`, which is part of the **JSON Schema** standard. + + Before that, it only supported the keyword `example` with a single example. That is still supported by OpenAPI 3.1.0, but is deprecated and is not part of the JSON Schema standard. So you are encouraged to migrate `example` to `examples`. 🤓 + + You can read more at the end of this page. + +## `Field` additional arguments + +When using `Field()` with Pydantic models, you can also declare additional `examples`: + +=== "Python 3.10+" + + ```Python hl_lines="2 8-11" + {!> ../../../docs_src/schema_extra_example/tutorial002_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="4 10-13" + {!> ../../../docs_src/schema_extra_example/tutorial002.py!} + ``` + +## `examples` in OpenAPI + +When using any of: + +* `Path()` +* `Query()` +* `Header()` +* `Cookie()` +* `Body()` +* `Form()` +* `File()` + +you can also declare a group of `examples` with additional information that will be added to **OpenAPI**. + +### `Body` with `examples` + +Here we pass `examples` containing one example of the data expected in `Body()`: + +=== "Python 3.10+" + + ```Python hl_lines="22-29" + {!> ../../../docs_src/schema_extra_example/tutorial003_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="22-29" + {!> ../../../docs_src/schema_extra_example/tutorial003_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="23-30" + {!> ../../../docs_src/schema_extra_example/tutorial003_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="18-25" + {!> ../../../docs_src/schema_extra_example/tutorial003_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="20-27" + {!> ../../../docs_src/schema_extra_example/tutorial003.py!} + ``` + +### Example in the docs UI + +With any of the methods above it would look like this in the `/docs`: + + + +### `Body` with multiple `examples` + +You can of course also pass multiple `examples`: + +=== "Python 3.10+" + + ```Python hl_lines="23-38" + {!> ../../../docs_src/schema_extra_example/tutorial004_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="23-38" + {!> ../../../docs_src/schema_extra_example/tutorial004_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="24-39" + {!> ../../../docs_src/schema_extra_example/tutorial004_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="19-34" + {!> ../../../docs_src/schema_extra_example/tutorial004_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="21-36" + {!> ../../../docs_src/schema_extra_example/tutorial004.py!} + ``` + +### Examples in the docs UI + +With `examples` added to `Body()` the `/docs` would look like: + + + +## Technical Details + +!!! tip + If you are already using **FastAPI** version **0.99.0 or above**, you can probably **skip** these details. + + They are more relevant for older versions, before OpenAPI 3.1.0 was available. + + You can consider this a brief OpenAPI and JSON Schema **history lesson**. 🤓 !!! warning - 请记住,传递的那些额外参数不会添加任何验证,只会添加注释,用于文档的目的。 + These are very technical details about the standards **JSON Schema** and **OpenAPI**. + + If the ideas above already work for you, that might be enough, and you probably don't need these details, feel free to skip them. + +Before OpenAPI 3.1.0, OpenAPI used an older and modified version of **JSON Schema**. + +JSON Schema didn't have `examples`, so OpenAPI added it's own `example` field to its own modified version. + +OpenAPI also added `example` and `examples` fields to other parts of the specification: + +* `Parameter Object` (in the specification) that was used by FastAPI's: + * `Path()` + * `Query()` + * `Header()` + * `Cookie()` +* `Request Body Object`, in the field `content`, on the `Media Type Object` (in the specification) that was used by FastAPI's: + * `Body()` + * `File()` + * `Form()` + +### OpenAPI's `examples` field + +The shape of this field `examples` from OpenAPI is a `dict` with **multiple examples**, each with extra information that will be added to **OpenAPI** too. + +The keys of the `dict` identify each example, and each value is another `dict`. + +Each specific example `dict` in the `examples` can contain: + +* `summary`: Short description for the example. +* `description`: A long description that can contain Markdown text. +* `value`: This is the actual example shown, e.g. a `dict`. +* `externalValue`: alternative to `value`, a URL pointing to the example. Although this might not be supported by as many tools as `value`. + +This applies to those other parts of the OpenAPI specification apart from JSON Schema. + +### JSON Schema's `examples` field -## `Body` 额外参数 +But then JSON Schema added an `examples` field to a new version of the specification. -你可以通过传递额外信息给 `Field` 同样的方式操作`Path`, `Query`, `Body`等。 +And then the new OpenAPI 3.1.0 was based on the latest version (JSON Schema 2020-12) that included this new field `examples`. -比如,你可以将请求体的一个 `example` 传递给 `Body`: +And now this new `examples` field takes precedence over the old single (and custom) `example` field, that is now deprecated. -```Python hl_lines="20-25" -{!../../../docs_src/schema_extra_example/tutorial003.py!} -``` +This new `examples` field in JSON Schema is **just a `list`** of examples, not a dict with extra metadata as in the other places in OpenAPI (described above). -## 文档 UI 中的例子 +!!! info + Even after OpenAPI 3.1.0 was released with this new simpler integration with JSON Schema, for a while, Swagger UI, the tool that provides the automatic docs, didn't support OpenAPI 3.1.0 (it does since version 5.0.0 🎉). -使用上面的任何方法,它在 `/docs` 中看起来都是这样的: + Because of that, versions of FastAPI previous to 0.99.0 still used versions of OpenAPI lower than 3.1.0. - +### Pydantic and FastAPI `examples` -## 技术细节 +When you add `examples` inside of a Pydantic model, using `schema_extra` or `Field(examples=["something"])` that example is added to the **JSON Schema** for that Pydantic model. -关于 `example` 和 `examples`... +And that **JSON Schema** of the Pydantic model is included in the **OpenAPI** of your API, and then it's used in the docs UI. -JSON Schema在最新的一个版本中定义了一个字段 `examples` ,但是 OpenAPI 基于之前的一个旧版JSON Schema,并没有 `examples`. +In versions of FastAPI before 0.99.0 (0.99.0 and above use the newer OpenAPI 3.1.0) when you used `example` or `examples` with any of the other utilities (`Query()`, `Body()`, etc.) those examples were not added to the JSON Schema that describes that data (not even to OpenAPI's own version of JSON Schema), they were added directly to the *path operation* declaration in OpenAPI (outside the parts of OpenAPI that use JSON Schema). -所以 OpenAPI为了相似的目的定义了自己的 `example` (使用 `example`, 而不是 `examples`), 这也是文档 UI 所使用的 (使用 Swagger UI). +But now that FastAPI 0.99.0 and above uses OpenAPI 3.1.0, that uses JSON Schema 2020-12, and Swagger UI 5.0.0 and above, everything is more consistent and the examples are included in JSON Schema. -所以,虽然 `example` 不是JSON Schema的一部分,但它是OpenAPI的一部分,这将被文档UI使用。 +### Summary -## 其他信息 +I used to say I didn't like history that much... and look at me now giving "tech history" lessons. 😅 -同样的方法,你可以添加你自己的额外信息,这些信息将被添加到每个模型的JSON模式中,例如定制前端用户界面,等等。 +In short, **upgrade to FastAPI 0.99.0 or above**, and things are much **simpler, consistent, and intuitive**, and you don't have to know all these historic details. 😎 From ff82fa21ba2d0180f748e851f6e361b459251b33 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:55 +0800 Subject: [PATCH 096/163] New translations first-steps.md (Chinese Simplified) --- docs/zh/docs/tutorial/security/first-steps.md | 243 +++++++++++------- 1 file changed, 144 insertions(+), 99 deletions(-) diff --git a/docs/zh/docs/tutorial/security/first-steps.md b/docs/zh/docs/tutorial/security/first-steps.md index 86c3320ce1e9e..25c20b8df21d6 100644 --- a/docs/zh/docs/tutorial/security/first-steps.md +++ b/docs/zh/docs/tutorial/security/first-steps.md @@ -1,40 +1,57 @@ -# 安全 - 第一步 +# Security - First Steps -假设**后端** API 在某个域。 +Let's imagine that you have your **backend** API in some domain. -**前端**在另一个域,或(移动应用中)在同一个域的不同路径下。 +And you have a **frontend** in another domain or in a different path of the same domain (or in a mobile application). -并且,前端要使用后端的 **username** 与 **password** 验证用户身份。 +And you want to have a way for the frontend to authenticate with the backend, using a **username** and **password**. -固然,**FastAPI** 支持 **OAuth2** 身份验证。 +We can use **OAuth2** to build that with **FastAPI**. -但为了节省开发者的时间,不要只为了查找很少的内容,不得不阅读冗长的规范文档。 +But let's save you the time of reading the full long specification just to find those little pieces of information you need. -我们建议使用 **FastAPI** 的安全工具。 +Let's use the tools provided by **FastAPI** to handle security. -## 概览 +## How it looks -首先,看看下面的代码是怎么运行的,然后再回过头来了解其背后的原理。 +Let's first just use the code and see how it works, and then we'll come back to understand what's happening. -## 创建 `main.py` +## Create `main.py` -把下面的示例代码复制到 `main.py`: +Copy the example in a file `main.py`: -```Python -{!../../../docs_src/security/tutorial001.py!} -``` +=== "Python 3.9+" + + ```Python + {!> ../../../docs_src/security/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" -## 运行 + ```Python + {!> ../../../docs_src/security/tutorial001_an.py!} + ``` -!!! info "说明" +=== "Python 3.6+ non-Annotated" - 先安装 `python-multipart`。 + !!! tip + Prefer to use the `Annotated` version if possible. - 安装命令: `pip install python-multipart`。 + ```Python + {!> ../../../docs_src/security/tutorial001.py!} + ``` - 这是因为 **OAuth2** 使用**表单数据**发送 `username` 与 `password`。 -用下面的命令运行该示例: +## Run it + +!!! info + First install `python-multipart`. + + E.g. `pip install python-multipart`. + + This is because **OAuth2** uses "form data" for sending the `username` and `password`. + +Run the example with:
@@ -46,144 +63,172 @@ $ uvicorn main:app --reload
-## 查看文档 - -打开 API 文档: http://127.0.0.1:8000/docs。 +## Check it -界面如下图所示: +Go to the interactive docs at: http://127.0.0.1:8000/docs. - +You will see something like this: -!!! check "Authorize 按钮!" + - 页面右上角出现了一个「**Authorize**」按钮。 +!!! check "Authorize button!" + You already have a shiny new "Authorize" button. - *路径操作*的右上角也出现了一个可以点击的小锁图标。 + And your *path operation* has a little lock in the top-right corner that you can click. -点击 **Authorize** 按钮,弹出授权表单,输入 `username` 与 `password` 及其它可选字段: +And if you click it, you have a little authorization form to type a `username` and `password` (and other optional fields): - + -!!! note "笔记" +!!! note + It doesn't matter what you type in the form, it won't work yet. But we'll get there. - 目前,在表单中输入内容不会有任何反应,后文会介绍相关内容。 +This is of course not the frontend for the final users, but it's a great automatic tool to document interactively all your API. -虽然此文档不是给前端最终用户使用的,但这个自动工具非常实用,可在文档中与所有 API 交互。 +It can be used by the frontend team (that can also be yourself). -前端团队(可能就是开发者本人)可以使用本工具。 +It can be used by third party applications and systems. -第三方应用与系统也可以调用本工具。 +And it can also be used by yourself, to debug, check and test the same application. -开发者也可以用它来调试、检查、测试应用。 +## The `password` flow -## 密码流 +Now let's go back a bit and understand what is all that. -现在,我们回过头来介绍这段代码的原理。 +The `password` "flow" is one of the ways ("flows") defined in OAuth2, to handle security and authentication. -`Password` **流**是 OAuth2 定义的,用于处理安全与身份验证的方式(**流**)。 +OAuth2 was designed so that the backend or API could be independent of the server that authenticates the user. -OAuth2 的设计目标是为了让后端或 API 独立于服务器验证用户身份。 +But in this case, the same **FastAPI** application will handle the API and the authentication. -但在本例中,**FastAPI** 应用会处理 API 与身份验证。 +So, let's review it from that simplified point of view: -下面,我们来看一下简化的运行流程: +* The user types the `username` and `password` in the frontend, and hits `Enter`. +* The frontend (running in the user's browser) sends that `username` and `password` to a specific URL in our API (declared with `tokenUrl="token"`). +* The API checks that `username` and `password`, and responds with a "token" (we haven't implemented any of this yet). + * A "token" is just a string with some content that we can use later to verify this user. + * Normally, a token is set to expire after some time. + * So, the user will have to log in again at some point later. + * And if the token is stolen, the risk is less. It is not like a permanent key that will work forever (in most of the cases). +* The frontend stores that token temporarily somewhere. +* The user clicks in the frontend to go to another section of the frontend web app. +* The frontend needs to fetch some more data from the API. + * But it needs authentication for that specific endpoint. + * So, to authenticate with our API, it sends a header `Authorization` with a value of `Bearer` plus the token. + * If the token contains `foobar`, the content of the `Authorization` header would be: `Bearer foobar`. -- 用户在前端输入 `username` 与`password`,并点击**回车** -- (用户浏览器中运行的)前端把 `username` 与`password` 发送至 API 中指定的 URL(使用 `tokenUrl="token"` 声明) -- API 检查 `username` 与`password`,并用令牌(`Token`) 响应(暂未实现此功能): - - 令牌只是用于验证用户的字符串 - - 一般来说,令牌会在一段时间后过期 - - 过时后,用户要再次登录 - - 这样一来,就算令牌被人窃取,风险也较低。因为它与永久密钥不同,**在绝大多数情况下**不会长期有效 -- 前端临时将令牌存储在某个位置 -- 用户点击前端,前往前端应用的其它部件 -- 前端需要从 API 中提取更多数据: - - 为指定的端点(Endpoint)进行身份验证 - - 因此,用 API 验证身份时,要发送值为 `Bearer` + 令牌的请求头 `Authorization` - - 假如令牌为 `foobar`,`Authorization` 请求头就是: `Bearer foobar` +## **FastAPI**'s `OAuth2PasswordBearer` -## **FastAPI** 的 `OAuth2PasswordBearer` +**FastAPI** provides several tools, at different levels of abstraction, to implement these security features. -**FastAPI** 提供了不同抽象级别的安全工具。 +In this example we are going to use **OAuth2**, with the **Password** flow, using a **Bearer** token. We do that using the `OAuth2PasswordBearer` class. -本例使用 **OAuth2** 的 **Password** 流以及 **Bearer** 令牌(`Token`)。为此要使用 `OAuth2PasswordBearer` 类。 +!!! info + A "bearer" token is not the only option. -!!! info "说明" + But it's the best one for our use case. + + And it might be the best for most use cases, unless you are an OAuth2 expert and know exactly why there's another option that suits better your needs. + + In that case, **FastAPI** also provides you with the tools to build it. - `Bearer` 令牌不是唯一的选择。 +When we create an instance of the `OAuth2PasswordBearer` class we pass in the `tokenUrl` parameter. This parameter contains the URL that the client (the frontend running in the user's browser) will use to send the `username` and `password` in order to get a token. - 但它是最适合这个用例的方案。 +=== "Python 3.9+" - 甚至可以说,它是适用于绝大多数用例的最佳方案,除非您是 OAuth2 的专家,知道为什么其它方案更合适。 + ```Python hl_lines="8" + {!> ../../../docs_src/security/tutorial001_an_py39.py!} + ``` - 本例中,**FastAPI** 还提供了构建工具。 +=== "Python 3.6+" -创建 `OAuth2PasswordBearer` 的类实例时,要传递 `tokenUrl` 参数。该参数包含客户端(用户浏览器中运行的前端) 的 URL,用于发送 `username` 与 `password`,并获取令牌。 + ```Python hl_lines="7" + {!> ../../../docs_src/security/tutorial001_an.py!} + ``` -```Python hl_lines="6" -{!../../../docs_src/security/tutorial001.py!} -``` - -!!! tip "提示" +=== "Python 3.6+ non-Annotated" - 在此,`tokenUrl="token"` 指向的是暂未创建的相对 URL `token`。这个相对 URL 相当于 `./token`。 + !!! tip + Prefer to use the `Annotated` version if possible. - 因为使用的是相对 URL,如果 API 位于 `https://example.com/`,则指向 `https://example.com/token`。但如果 API 位于 `https://example.com/api/v1/`,它指向的就是`https://example.com/api/v1/token`。 + ```Python hl_lines="6" + {!> ../../../docs_src/security/tutorial001.py!} + ``` - 使用相对 URL 非常重要,可以确保应用在遇到[使用代理](../../advanced/behind-a-proxy.md){.internal-link target=_blank}这样的高级用例时,也能正常运行。 +!!! tip + Here `tokenUrl="token"` refers to a relative URL `token` that we haven't created yet. As it's a relative URL, it's equivalent to `./token`. -该参数不会创建端点或*路径操作*,但会声明客户端用来获取令牌的 URL `/token` 。此信息用于 OpenAPI 及 API 文档。 + Because we are using a relative URL, if your API was located at `https://example.com/`, then it would refer to `https://example.com/token`. But if your API was located at `https://example.com/api/v1/`, then it would refer to `https://example.com/api/v1/token`. + + Using a relative URL is important to make sure your application keeps working even in an advanced use case like [Behind a Proxy](../../advanced/behind-a-proxy.md){.internal-link target=_blank}. -接下来,学习如何创建实际的路径操作。 +This parameter doesn't create that endpoint / *path operation*, but declares that the URL `/token` will be the one that the client should use to get the token. That information is used in OpenAPI, and then in the interactive API documentation systems. -!!! info "说明" +We will soon also create the actual path operation. - 严苛的 **Pythonista** 可能不喜欢用 `tokenUrl` 这种命名风格代替 `token_url`。 +!!! info + If you are a very strict "Pythonista" you might dislike the style of the parameter name `tokenUrl` instead of `token_url`. - 这种命名方式是因为要使用与 OpenAPI 规范中相同的名字。以便在深入校验安全方案时,能通过复制粘贴查找更多相关信息。 + That's because it is using the same name as in the OpenAPI spec. So that if you need to investigate more about any of these security schemes you can just copy and paste it to find more information about it. -`oauth2_scheme` 变量是 `OAuth2PasswordBearer` 的实例,也是**可调用项**。 +The `oauth2_scheme` variable is an instance of `OAuth2PasswordBearer`, but it is also a "callable". -以如下方式调用: +It could be called as: ```Python oauth2_scheme(some, parameters) ``` -因此,`Depends` 可以调用 `oauth2_scheme` 变量。 +So, it can be used with `Depends`. -### 使用 +### Use it -接下来,使用 `Depends` 把 `oauth2_scheme` 传入依赖项。 +Now you can pass that `oauth2_scheme` in a dependency with `Depends`. -```Python hl_lines="10" -{!../../../docs_src/security/tutorial001.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="12" + {!> ../../../docs_src/security/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="11" + {!> ../../../docs_src/security/tutorial001_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. -该依赖项使用字符串(`str`)接收*路径操作函数*的参数 `token` 。 + ```Python hl_lines="10" + {!> ../../../docs_src/security/tutorial001.py!} + ``` -**FastAPI** 使用依赖项在 OpenAPI 概图(及 API 文档)中定义**安全方案**。 +This dependency will provide a `str` that is assigned to the parameter `token` of the *path operation function*. -!!! info "技术细节" +**FastAPI** will know that it can use this dependency to define a "security scheme" in the OpenAPI schema (and the automatic API docs). - **FastAPI** 使用(在依赖项中声明的)类 `OAuth2PasswordBearer` 在 OpenAPI 中定义安全方案,这是因为它继承自 `fastapi.security.oauth2.OAuth2`,而该类又是继承自`fastapi.security.base.SecurityBase`。 +!!! info "Technical Details" + **FastAPI** will know that it can use the class `OAuth2PasswordBearer` (declared in a dependency) to define the security scheme in OpenAPI because it inherits from `fastapi.security.oauth2.OAuth2`, which in turn inherits from `fastapi.security.base.SecurityBase`. - 所有与 OpenAPI(及 API 文档)集成的安全工具都继承自 `SecurityBase`, 这就是为什么 **FastAPI** 能把它们集成至 OpenAPI 的原因。 + All the security utilities that integrate with OpenAPI (and the automatic API docs) inherit from `SecurityBase`, that's how **FastAPI** can know how to integrate them in OpenAPI. -## 实现的操作 +## What it does -FastAPI 校验请求中的 `Authorization` 请求头,核对请求头的值是不是由 `Bearer ` + 令牌组成, 并返回令牌字符串(`str`)。 +It will go and look in the request for that `Authorization` header, check if the value is `Bearer` plus some token, and will return the token as a `str`. -如果没有找到 `Authorization` 请求头,或请求头的值不是 `Bearer ` + 令牌。FastAPI 直接返回 401 错误状态码(`UNAUTHORIZED`)。 +If it doesn't see an `Authorization` header, or the value doesn't have a `Bearer` token, it will respond with a 401 status code error (`UNAUTHORIZED`) directly. -开发者不需要检查错误信息,查看令牌是否存在,只要该函数能够执行,函数中就会包含令牌字符串。 +You don't even have to check if the token exists to return an error. You can be sure that if your function is executed, it will have a `str` in that token. -正如下图所示,API 文档已经包含了这项功能: +You can try it already in the interactive docs: - + -目前,暂时还没有实现验证令牌是否有效的功能,不过后文很快就会介绍的。 +We are not verifying the validity of the token yet, but that's a start already. -## 小结 +## Recap -看到了吧,只要多写三四行代码,就可以添加基础的安全表单。 +So, in just 3 or 4 extra lines, you already have some primitive form of security. From 713063ea0b1c7ddabf826e154af4afd2976e924f Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:56 +0800 Subject: [PATCH 097/163] New translations get-current-user.md (Chinese Simplified) --- .../tutorial/security/get-current-user.md | 296 ++++++++++++++---- 1 file changed, 235 insertions(+), 61 deletions(-) diff --git a/docs/zh/docs/tutorial/security/get-current-user.md b/docs/zh/docs/tutorial/security/get-current-user.md index 477baec3afbf4..1a8c5d9a8d8fd 100644 --- a/docs/zh/docs/tutorial/security/get-current-user.md +++ b/docs/zh/docs/tutorial/security/get-current-user.md @@ -1,114 +1,288 @@ -# 获取当前用户 +# Get Current User -在上一章节中,(基于依赖项注入系统的)安全系统向*路径操作函数*提供了一个 `str` 类型的 `token`: +In the previous chapter the security system (which is based on the dependency injection system) was giving the *path operation function* a `token` as a `str`: -```Python hl_lines="10" -{!../../../docs_src/security/tutorial001.py!} -``` +=== "Python 3.9+" -但这还不是很实用。 + ```Python hl_lines="12" + {!> ../../../docs_src/security/tutorial001_an_py39.py!} + ``` -让我们来使它返回当前用户给我们。 +=== "Python 3.6+" + ```Python hl_lines="11" + {!> ../../../docs_src/security/tutorial001_an.py!} + ``` -## 创建一个用户模型 +=== "Python 3.6+ non-Annotated" -首先,让我们来创建一个用户 Pydantic 模型。 + !!! tip + Prefer to use the `Annotated` version if possible. -与使用 Pydantic 声明请求体的方式相同,我们可以在其他任何地方使用它: + ```Python hl_lines="10" + {!> ../../../docs_src/security/tutorial001.py!} + ``` -```Python hl_lines="5 12-16" -{!../../../docs_src/security/tutorial002.py!} -``` +But that is still not that useful. -## 创建一个 `get_current_user` 依赖项 +Let's make it give us the current user. -让我们来创建一个 `get_current_user` 依赖项。 +## Create a user model -还记得依赖项可以有子依赖项吗? +First, let's create a Pydantic user model. -`get_current_user` 将具有一个我们之前所创建的同一个 `oauth2_scheme` 作为依赖项。 +The same way we use Pydantic to declare bodies, we can use it anywhere else: -与我们之前直接在路径操作中所做的相同,我们新的依赖项 `get_current_user` 将从子依赖项 `oauth2_scheme` 中接收一个 `str` 类型的 `token`: +=== "Python 3.10+" -```Python hl_lines="25" -{!../../../docs_src/security/tutorial002.py!} -``` + ```Python hl_lines="5 12-16" + {!> ../../../docs_src/security/tutorial002_an_py310.py!} + ``` -## 获取用户 +=== "Python 3.9+" -`get_current_user` 将使用我们创建的(伪)工具函数,该函数接收 `str` 类型的令牌并返回我们的 Pydantic `User` 模型: + ```Python hl_lines="5 12-16" + {!> ../../../docs_src/security/tutorial002_an_py39.py!} + ``` -```Python hl_lines="19-22 26-27" -{!../../../docs_src/security/tutorial002.py!} -``` +=== "Python 3.6+" -## 注入当前用户 + ```Python hl_lines="5 13-17" + {!> ../../../docs_src/security/tutorial002_an.py!} + ``` -因此现在我们可以在*路径操作*中使用 `get_current_user` 作为 `Depends` 了: +=== "Python 3.10+ non-Annotated" -```Python hl_lines="31" -{!../../../docs_src/security/tutorial002.py!} -``` + !!! tip + Prefer to use the `Annotated` version if possible. -注意我们将 `current_user` 的类型声明为 Pydantic 模型 `User`。 + ```Python hl_lines="3 10-14" + {!> ../../../docs_src/security/tutorial002_py310.py!} + ``` -这将帮助我们在函数内部使用所有的代码补全和类型检查。 +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="5 12-16" + {!> ../../../docs_src/security/tutorial002.py!} + ``` + +## Create a `get_current_user` dependency + +Let's create a dependency `get_current_user`. + +Remember that dependencies can have sub-dependencies? + +`get_current_user` will have a dependency with the same `oauth2_scheme` we created before. + +The same as we were doing before in the *path operation* directly, our new dependency `get_current_user` will receive a `token` as a `str` from the sub-dependency `oauth2_scheme`: + +=== "Python 3.10+" + + ```Python hl_lines="25" + {!> ../../../docs_src/security/tutorial002_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="25" + {!> ../../../docs_src/security/tutorial002_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="26" + {!> ../../../docs_src/security/tutorial002_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="23" + {!> ../../../docs_src/security/tutorial002_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="25" + {!> ../../../docs_src/security/tutorial002.py!} + ``` + +## Get the user + +`get_current_user` will use a (fake) utility function we created, that takes a token as a `str` and returns our Pydantic `User` model: + +=== "Python 3.10+" + + ```Python hl_lines="19-22 26-27" + {!> ../../../docs_src/security/tutorial002_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="19-22 26-27" + {!> ../../../docs_src/security/tutorial002_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="20-23 27-28" + {!> ../../../docs_src/security/tutorial002_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="17-20 24-25" + {!> ../../../docs_src/security/tutorial002_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="19-22 26-27" + {!> ../../../docs_src/security/tutorial002.py!} + ``` + +## Inject the current user + +So now we can use the same `Depends` with our `get_current_user` in the *path operation*: + +=== "Python 3.10+" + + ```Python hl_lines="31" + {!> ../../../docs_src/security/tutorial002_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="31" + {!> ../../../docs_src/security/tutorial002_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="32" + {!> ../../../docs_src/security/tutorial002_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="29" + {!> ../../../docs_src/security/tutorial002_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="31" + {!> ../../../docs_src/security/tutorial002.py!} + ``` + +Notice that we declare the type of `current_user` as the Pydantic model `User`. + +This will help us inside of the function with all the completion and type checks. !!! tip - 你可能还记得请求体也是使用 Pydantic 模型来声明的。 + You might remember that request bodies are also declared with Pydantic models. - 在这里 **FastAPI** 不会搞混,因为你正在使用的是 `Depends`。 + Here **FastAPI** won't get confused because you are using `Depends`. !!! check - 这种依赖系统的设计方式使我们可以拥有不同的依赖项(不同的「可依赖类型」),并且它们都返回一个 `User` 模型。 + The way this dependency system is designed allows us to have different dependencies (different "dependables") that all return a `User` model. + + We are not restricted to having only one dependency that can return that type of data. + +## Other models + +You can now get the current user directly in the *path operation functions* and deal with the security mechanisms at the **Dependency Injection** level, using `Depends`. + +And you can use any model or data for the security requirements (in this case, a Pydantic model `User`). + +But you are not restricted to using some specific data model, class or type. + +Do you want to have an `id` and `email` and not have any `username` in your model? Sure. You can use these same tools. + +Do you want to just have a `str`? Or just a `dict`? Or a database class model instance directly? It all works the same way. + +You actually don't have users that log in to your application but robots, bots, or other systems, that have just an access token? Again, it all works the same. + +Just use any kind of model, any kind of class, any kind of database that you need for your application. **FastAPI** has you covered with the dependency injection system. - 我们并未被局限于只能有一个返回该类型数据的依赖项。 +## Code size +This example might seem verbose. Have in mind that we are mixing security, data models, utility functions and *path operations* in the same file. -## 其他模型 +But here's the key point. -现在你可以直接在*路径操作函数*中获取当前用户,并使用 `Depends` 在**依赖注入**级别处理安全性机制。 +The security and dependency injection stuff is written once. -你可以使用任何模型或数据来满足安全性要求(在这个示例中,使用的是 Pydantic 模型 `User`)。 +And you can make it as complex as you want. And still, have it written only once, in a single place. With all the flexibility. -但是你并未被限制只能使用某些特定的数据模型,类或类型。 +But you can have thousands of endpoints (*path operations*) using the same security system. -你想要在模型中使用 `id` 和 `email` 而不使用任何的 `username`?当然可以。你可以同样地使用这些工具。 +And all of them (or any portion of them that you want) can take the advantage of re-using these dependencies or any other dependencies you create. -你只想要一个 `str`?或者仅仅一个 `dict`?还是直接一个数据库模型类的实例?它们的工作方式都是一样的。 +And all these thousands of *path operations* can be as small as 3 lines: -实际上你没有用户登录到你的应用程序,而是只拥有访问令牌的机器人,程序或其他系统?再一次,它们的工作方式也是一样的。 +=== "Python 3.10+" -尽管去使用你的应用程序所需要的任何模型,任何类,任何数据库。**FastAPI** 通过依赖项注入系统都帮你搞定。 + ```Python hl_lines="30-32" + {!> ../../../docs_src/security/tutorial002_an_py310.py!} + ``` +=== "Python 3.9+" -## 代码体积 + ```Python hl_lines="30-32" + {!> ../../../docs_src/security/tutorial002_an_py39.py!} + ``` -这个示例似乎看起来很冗长。考虑到我们在同一文件中混合了安全性,数据模型工具函数和路径操作等代码。 +=== "Python 3.6+" -但关键的是。 + ```Python hl_lines="31-33" + {!> ../../../docs_src/security/tutorial002_an.py!} + ``` -安全性和依赖项注入内容只需要编写一次。 +=== "Python 3.10+ non-Annotated" -你可以根据需要使其变得很复杂。而且只需要在一个地方写一次。但仍然具备所有的灵活性。 + !!! tip + Prefer to use the `Annotated` version if possible. -但是,你可以有无数个使用同一安全系统的端点(*路径操作*)。 + ```Python hl_lines="28-30" + {!> ../../../docs_src/security/tutorial002_py310.py!} + ``` -所有(或所需的任何部分)的端点,都可以利用对这些或你创建的其他依赖项进行复用所带来的优势。 +=== "Python 3.6+ non-Annotated" -所有的这无数个*路径操作*甚至可以小到只需 3 行代码: + !!! tip + Prefer to use the `Annotated` version if possible. -```Python hl_lines="30-32" -{!../../../docs_src/security/tutorial002.py!} -``` + ```Python hl_lines="30-32" + {!> ../../../docs_src/security/tutorial002.py!} + ``` -## 总结 +## Recap -现在你可以直接在*路径操作函数*中获取当前用户。 +You can now get the current user directly in your *path operation function*. -我们已经进行到一半了。 +We are already halfway there. -我们只需要再为用户/客户端添加一个真正发送 `username` 和 `password` 的*路径操作*。 +We just need to add a *path operation* for the user/client to actually send the `username` and `password`. -这些内容在下一章节。 +That comes next. From b2e7ad0f95d18d4a65d87d1bb743edb9033fe52b Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:57 +0800 Subject: [PATCH 098/163] New translations index.md (Chinese Simplified) --- docs/zh/docs/tutorial/security/index.md | 100 ++++++++++++------------ 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/docs/zh/docs/tutorial/security/index.md b/docs/zh/docs/tutorial/security/index.md index 0595f5f636129..9679cda427d6a 100644 --- a/docs/zh/docs/tutorial/security/index.md +++ b/docs/zh/docs/tutorial/security/index.md @@ -1,101 +1,101 @@ -# 安全性 +# Security -有许多方法可以处理安全性、身份认证和授权等问题。 +There are many ways to handle security, authentication and authorization. -而且这通常是一个复杂而「困难」的话题。 +And it normally is a complex and "difficult" topic. -在许多框架和系统中,仅处理安全性和身份认证就会花费大量的精力和代码(在许多情况下,可能占编写的所有代码的 50% 或更多)。 +In many frameworks and systems just handling security and authentication takes a big amount of effort and code (in many cases it can be 50% or more of all the code written). -**FastAPI** 提供了多种工具,可帮助你以标准的方式轻松、快速地处理**安全性**,而无需研究和学习所有的安全规范。 +**FastAPI** provides several tools to help you deal with **Security** easily, rapidly, in a standard way, without having to study and learn all the security specifications. -但首先,让我们来看一些小的概念。 +But first, let's check some small concepts. -## 没有时间? +## In a hurry? -如果你不关心这些术语,而只需要*立即*通过基于用户名和密码的身份认证来增加安全性,请跳转到下一章。 +If you don't care about any of these terms and you just need to add security with authentication based on username and password *right now*, skip to the next chapters. ## OAuth2 -OAuth2是一个规范,它定义了几种处理身份认证和授权的方法。 +OAuth2 is a specification that defines several ways to handle authentication and authorization. -它是一个相当广泛的规范,涵盖了一些复杂的使用场景。 +It is quite an extensive specification and covers several complex use cases. -它包括了使用「第三方」进行身份认证的方法。 +It includes ways to authenticate using a "third party". -这就是所有带有「使用 Facebook,Google,Twitter,GitHub 登录」的系统背后所使用的机制。 +That's what all the systems with "login with Facebook, Google, Twitter, GitHub" use underneath. ### OAuth 1 -有一个 OAuth 1,它与 OAuth2 完全不同,并且更为复杂,因为它直接包含了有关如何加密通信的规范。 +There was an OAuth 1, which is very different from OAuth2, and more complex, as it included direct specifications on how to encrypt the communication. -如今它已经不是很流行,没有被广泛使用了。 +It is not very popular or used nowadays. -OAuth2 没有指定如何加密通信,它期望你为应用程序使用 HTTPS 进行通信。 +OAuth2 doesn't specify how to encrypt the communication, it expects you to have your application served with HTTPS. !!! tip - 在有关**部署**的章节中,你将了解如何使用 Traefik 和 Let's Encrypt 免费设置 HTTPS。 + In the section about **deployment** you will see how to set up HTTPS for free, using Traefik and Let's Encrypt. ## OpenID Connect -OpenID Connect 是另一个基于 **OAuth2** 的规范。 +OpenID Connect is another specification, based on **OAuth2**. -它只是扩展了 OAuth2,并明确了一些在 OAuth2 中相对模糊的内容,以尝试使其更具互操作性。 +It just extends OAuth2 specifying some things that are relatively ambiguous in OAuth2, to try to make it more interoperable. -例如,Google 登录使用 OpenID Connect(底层使用OAuth2)。 +For example, Google login uses OpenID Connect (which underneath uses OAuth2). -但是 Facebook 登录不支持 OpenID Connect。它具有自己的 OAuth2 风格。 +But Facebook login doesn't support OpenID Connect. It has its own flavor of OAuth2. -### OpenID(非「OpenID Connect」) +### OpenID (not "OpenID Connect") -还有一个「OpenID」规范。它试图解决与 **OpenID Connect** 相同的问题,但它不是基于 OAuth2。 +There was also an "OpenID" specification. That tried to solve the same thing as **OpenID Connect**, but was not based on OAuth2. -因此,它是一个完整的附加系统。 +So, it was a complete additional system. -如今它已经不是很流行,没有被广泛使用了。 +It is not very popular or used nowadays. ## OpenAPI -OpenAPI(以前称为 Swagger)是用于构建 API 的开放规范(现已成为 Linux Foundation 的一部分)。 +OpenAPI (previously known as Swagger) is the open specification for building APIs (now part of the Linux Foundation). -**FastAPI** 基于 **OpenAPI**。 +**FastAPI** is based on **OpenAPI**. -这就是使多个自动交互式文档界面,代码生成等成为可能的原因。 +That's what makes it possible to have multiple automatic interactive documentation interfaces, code generation, etc. -OpenAPI 有一种定义多个安全「方案」的方法。 +OpenAPI has a way to define multiple security "schemes". -通过使用它们,你可以利用所有这些基于标准的工具,包括这些交互式文档系统。 +By using them, you can take advantage of all these standard-based tools, including these interactive documentation systems. -OpenAPI 定义了以下安全方案: +OpenAPI defines the following security schemes: -* `apiKey`:一个特定于应用程序的密钥,可以来自: - * 查询参数。 - * 请求头。 - * cookie。 -* `http`:标准的 HTTP 身份认证系统,包括: - * `bearer`: 一个值为 `Bearer` 加令牌字符串的 `Authorization` 请求头。这是从 OAuth2 继承的。 - * HTTP Basic 认证方式。 - * HTTP Digest,等等。 -* `oauth2`:所有的 OAuth2 处理安全性的方式(称为「流程」)。 - *以下几种流程适合构建 OAuth 2.0 身份认证的提供者(例如 Google,Facebook,Twitter,GitHub 等): +* `apiKey`: an application specific key that can come from: + * A query parameter. + * A header. + * A cookie. +* `http`: standard HTTP authentication systems, including: + * `bearer`: a header `Authorization` with a value of `Bearer` plus a token. This is inherited from OAuth2. + * HTTP Basic authentication. + * HTTP Digest, etc. +* `oauth2`: all the OAuth2 ways to handle security (called "flows"). + * Several of these flows are appropriate for building an OAuth 2.0 authentication provider (like Google, Facebook, Twitter, GitHub, etc): * `implicit` * `clientCredentials` * `authorizationCode` - * 但是有一个特定的「流程」可以完美地用于直接在同一应用程序中处理身份认证: - * `password`:接下来的几章将介绍它的示例。 -* `openIdConnect`:提供了一种定义如何自动发现 OAuth2 身份认证数据的方法。 - * 此自动发现机制是 OpenID Connect 规范中定义的内容。 + * But there is one specific "flow" that can be perfectly used for handling authentication in the same application directly: + * `password`: some next chapters will cover examples of this. +* `openIdConnect`: has a way to define how to discover OAuth2 authentication data automatically. + * This automatic discovery is what is defined in the OpenID Connect specification. !!! tip - 集成其他身份认证/授权提供者(例如Google,Facebook,Twitter,GitHub等)也是可能的,而且较为容易。 + Integrating other authentication/authorization providers like Google, Facebook, Twitter, GitHub, etc. is also possible and relatively easy. - 最复杂的问题是创建一个像这样的身份认证/授权提供程序,但是 **FastAPI** 为你提供了轻松完成任务的工具,同时为你解决了重活。 + The most complex problem is building an authentication/authorization provider like those, but **FastAPI** gives you the tools to do it easily, while doing the heavy lifting for you. -## **FastAPI** 实用工具 +## **FastAPI** utilities -FastAPI 在 `fastapi.security` 模块中为每个安全方案提供了几种工具,这些工具简化了这些安全机制的使用方法。 +FastAPI provides several tools for each of these security schemes in the `fastapi.security` module that simplify using these security mechanisms. -在下一章中,你将看到如何使用 **FastAPI** 所提供的这些工具为你的 API 增加安全性。 +In the next chapters you will see how to add security to your API using those tools provided by **FastAPI**. -而且你还将看到它如何自动地被集成到交互式文档系统中。 +And you will also see how it gets automatically integrated into the interactive documentation system. From 41559535a402a2076e58803850450893f748e711 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:58 +0800 Subject: [PATCH 099/163] New translations oauth2-jwt.md (Chinese Simplified) --- docs/zh/docs/tutorial/security/oauth2-jwt.md | 364 +++++++++++++------ 1 file changed, 243 insertions(+), 121 deletions(-) diff --git a/docs/zh/docs/tutorial/security/oauth2-jwt.md b/docs/zh/docs/tutorial/security/oauth2-jwt.md index 054198545ef8e..b663abcb7b696 100644 --- a/docs/zh/docs/tutorial/security/oauth2-jwt.md +++ b/docs/zh/docs/tutorial/security/oauth2-jwt.md @@ -1,132 +1,160 @@ -# OAuth2 实现密码哈希与 Bearer JWT 令牌验证 +# OAuth2 with Password (and hashing), Bearer with JWT tokens -至此,我们已经编写了所有安全流,本章学习如何使用 JWT 令牌(Token)和安全密码哈希(Hash)实现真正的安全机制。 +Now that we have all the security flow, let's make the application actually secure, using JWT tokens and secure password hashing. -本章的示例代码真正实现了在应用的数据库中保存哈希密码等功能。 +This code is something you can actually use in your application, save the password hashes in your database, etc. -接下来,我们紧接上一章,继续完善安全机制。 +We are going to start from where we left in the previous chapter and increment it. -## JWT 简介 +## About JWT -JWT 即**JSON 网络令牌**(JSON Web Tokens)。 +JWT means "JSON Web Tokens". -JWT 是一种将 JSON 对象编码为没有空格,且难以理解的长字符串的标准。JWT 的内容如下所示: +It's a standard to codify a JSON object in a long dense string without spaces. It looks like this: ``` eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ``` -JWT 字符串没有加密,任何人都能用它恢复原始信息。 +It is not encrypted, so, anyone could recover the information from the contents. -但 JWT 使用了签名机制。接受令牌时,可以用签名校验令牌。 +But it's signed. So, when you receive a token that you emitted, you can verify that you actually emitted it. -使用 JWT 创建有效期为一周的令牌。第二天,用户持令牌再次访问时,仍为登录状态。 +That way, you can create a token with an expiration of, let's say, 1 week. And then when the user comes back the next day with the token, you know that user is still logged in to your system. -令牌于一周后过期,届时,用户身份验证就会失败。只有再次登录,才能获得新的令牌。如果用户(或第三方)篡改令牌的过期时间,因为签名不匹配会导致身份验证失败。 +After a week, the token will be expired and the user will not be authorized and will have to sign in again to get a new token. And if the user (or a third party) tried to modify the token to change the expiration, you would be able to discover it, because the signatures would not match. -如需深入了解 JWT 令牌,了解它的工作方式,请参阅 https://jwt.io。 +If you want to play with JWT tokens and see how they work, check https://jwt.io. -## 安装 `python-jose` +## Install `python-jose` -安装 `python-jose`,在 Python 中生成和校验 JWT 令牌: +We need to install `python-jose` to generate and verify the JWT tokens in Python:
```console -$ pip install python-jose[cryptography] +$ pip install "python-jose[cryptography]" ---> 100% ```
-Python-jose 需要安装配套的加密后端。 +Python-jose requires a cryptographic backend as an extra. -本教程推荐的后端是:pyca/cryptography。 +Here we are using the recommended one: pyca/cryptography. -!!! tip "提示" +!!! tip + This tutorial previously used PyJWT. - 本教程以前使用 PyJWT。 + But it was updated to use Python-jose instead as it provides all the features from PyJWT plus some extras that you might need later when building integrations with other tools. - 但后来换成了 Python-jose,因为 Python-jose 支持 PyJWT 的所有功能,还支持与其它工具集成时可能会用到的一些其它功能。 +## Password hashing -## 密码哈希 +"Hashing" means converting some content (a password in this case) into a sequence of bytes (just a string) that looks like gibberish. -**哈希**是指把特定内容(本例中为密码)转换为乱码形式的字节序列(其实就是字符串)。 +Whenever you pass exactly the same content (exactly the same password) you get exactly the same gibberish. -每次传入完全相同的内容时(比如,完全相同的密码),返回的都是完全相同的乱码。 +But you cannot convert from the gibberish back to the password. -但这个乱码无法转换回传入的密码。 +### Why use password hashing -### 为什么使用密码哈希 +If your database is stolen, the thief won't have your users' plaintext passwords, only the hashes. -原因很简单,假如数据库被盗,窃贼无法获取用户的明文密码,得到的只是哈希值。 +So, the thief won't be able to try to use that password in another system (as many users use the same password everywhere, this would be dangerous). -这样一来,窃贼就无法在其它应用中使用窃取的密码,要知道,很多用户在所有系统中都使用相同的密码,风险超大)。 +## Install `passlib` -## 安装 `passlib` +PassLib is a great Python package to handle password hashes. -Passlib 是处理密码哈希的 Python 包。 +It supports many secure hashing algorithms and utilities to work with them. -它支持很多安全哈希算法及配套工具。 +The recommended algorithm is "Bcrypt". -本教程推荐的算法是 **Bcrypt**。 - -因此,请先安装附带 Bcrypt 的 PassLib: +So, install PassLib with Bcrypt:
```console -$ pip install passlib[bcrypt] +$ pip install "passlib[bcrypt]" ---> 100% ```
-!!! tip "提示" +!!! tip + With `passlib`, you could even configure it to be able to read passwords created by **Django**, a **Flask** security plug-in or many others. - `passlib` 甚至可以读取 Django、Flask 的安全插件等工具创建的密码。 + So, you would be able to, for example, share the same data from a Django application in a database with a FastAPI application. Or gradually migrate a Django application using the same database. + + And your users would be able to login from your Django app or from your **FastAPI** app, at the same time. - 例如,把 Django 应用的数据共享给 FastAPI 应用的数据库。或利用同一个数据库,可以逐步把应用从 Django 迁移到 FastAPI。 +## Hash and verify the passwords - 并且,用户可以同时从 Django 应用或 FastAPI 应用登录。 +Import the tools we need from `passlib`. -## 密码哈希与校验 +Create a PassLib "context". This is what will be used to hash and verify passwords. -从 `passlib` 导入所需工具。 +!!! tip + The PassLib context also has functionality to use different hashing algorithms, including deprecated old ones only to allow verifying them, etc. -创建用于密码哈希和身份校验的 PassLib **上下文**。 + For example, you could use it to read and verify passwords generated by another system (like Django) but hash any new passwords with a different algorithm like Bcrypt. + + And be compatible with all of them at the same time. -!!! tip "提示" +Create a utility function to hash a password coming from the user. - PassLib 上下文还支持使用不同哈希算法的功能,包括只能校验的已弃用旧算法等。 +And another utility to verify if a received password matches the hash stored. - 例如,用它读取和校验其它系统(如 Django)生成的密码,但要使用其它算法,如 Bcrypt,生成新的哈希密码。 +And another one to authenticate and return a user. - 同时,这些功能都是兼容的。 +=== "Python 3.10+" -接下来,创建三个工具函数,其中一个函数用于哈希用户的密码。 + ```Python hl_lines="7 48 55-56 59-60 69-75" + {!> ../../../docs_src/security/tutorial004_an_py310.py!} + ``` -第一个函数用于校验接收的密码是否匹配存储的哈希值。 +=== "Python 3.9+" -第三个函数用于身份验证,并返回用户。 + ```Python hl_lines="7 48 55-56 59-60 69-75" + {!> ../../../docs_src/security/tutorial004_an_py39.py!} + ``` -```Python hl_lines="7 48 55-56 59-60 69-75" -{!../../../docs_src/security/tutorial004.py!} -``` +=== "Python 3.6+" + + ```Python hl_lines="7 49 56-57 60-61 70-76" + {!> ../../../docs_src/security/tutorial004_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="6 47 54-55 58-59 68-74" + {!> ../../../docs_src/security/tutorial004_py310.py!} + ``` -!!! note "笔记" +=== "Python 3.6+ non-Annotated" - 查看新的(伪)数据库 `fake_users_db`,就能看到哈希后的密码:`"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"`。 + !!! tip + Prefer to use the `Annotated` version if possible. -## 处理 JWT 令牌 + ```Python hl_lines="7 48 55-56 59-60 69-75" + {!> ../../../docs_src/security/tutorial004.py!} + ``` -导入已安装的模块。 +!!! note + If you check the new (fake) database `fake_users_db`, you will see how the hashed password looks like now: `"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"`. -创建用于 JWT 令牌签名的随机密钥。 +## Handle JWT tokens -使用以下命令,生成安全的随机密钥: +Import the modules installed. + +Create a random secret key that will be used to sign the JWT tokens. + +To generate a secure random secret key use the command:
@@ -138,85 +166,180 @@ $ openssl rand -hex 32
-然后,把生成的密钥复制到变量**SECRET_KEY**,注意,不要使用本例所示的密钥。 +And copy the output to the variable `SECRET_KEY` (don't use the one in the example). -创建指定 JWT 令牌签名算法的变量 **ALGORITHM**,本例中的值为 `"HS256"`。 +Create a variable `ALGORITHM` with the algorithm used to sign the JWT token and set it to `"HS256"`. -创建设置令牌过期时间的变量。 +Create a variable for the expiration of the token. -定义令牌端点响应的 Pydantic 模型。 +Define a Pydantic Model that will be used in the token endpoint for the response. -创建生成新的访问令牌的工具函数。 +Create a utility function to generate a new access token. -```Python hl_lines="6 12-14 28-30 78-86" -{!../../../docs_src/security/tutorial004.py!} -``` +=== "Python 3.10+" -## 更新依赖项 + ```Python hl_lines="6 12-14 28-30 78-86" + {!> ../../../docs_src/security/tutorial004_an_py310.py!} + ``` -更新 `get_current_user` 以接收与之前相同的令牌,但这里用的是 JWT 令牌。 +=== "Python 3.9+" -解码并校验接收到的令牌,然后,返回当前用户。 + ```Python hl_lines="6 12-14 28-30 78-86" + {!> ../../../docs_src/security/tutorial004_an_py39.py!} + ``` -如果令牌无效,则直接返回 HTTP 错误。 +=== "Python 3.6+" -```Python hl_lines="89-106" -{!../../../docs_src/security/tutorial004.py!} -``` + ```Python hl_lines="6 13-15 29-31 79-87" + {!> ../../../docs_src/security/tutorial004_an.py!} + ``` -## 更新 `/token` *路径操作* +=== "Python 3.10+ non-Annotated" -用令牌过期时间创建 `timedelta` 对象。 + !!! tip + Prefer to use the `Annotated` version if possible. -创建并返回真正的 JWT 访问令牌。 + ```Python hl_lines="5 11-13 27-29 77-85" + {!> ../../../docs_src/security/tutorial004_py310.py!} + ``` -```Python hl_lines="115-128" -{!../../../docs_src/security/tutorial004.py!} -``` +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="6 12-14 28-30 78-86" + {!> ../../../docs_src/security/tutorial004.py!} + ``` + +## Update the dependencies + +Update `get_current_user` to receive the same token as before, but this time, using JWT tokens. + +Decode the received token, verify it, and return the current user. + +If the token is invalid, return an HTTP error right away. + +=== "Python 3.10+" + + ```Python hl_lines="89-106" + {!> ../../../docs_src/security/tutorial004_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="89-106" + {!> ../../../docs_src/security/tutorial004_an_py39.py!} + ``` + +=== "Python 3.6+" -### JWT `sub` 的技术细节 + ```Python hl_lines="90-107" + {!> ../../../docs_src/security/tutorial004_an.py!} + ``` -JWT 规范还包括 `sub` 键,值是令牌的主题。 +=== "Python 3.10+ non-Annotated" -该键是可选的,但要把用户标识放在这个键里,所以本例使用了该键。 + !!! tip + Prefer to use the `Annotated` version if possible. -除了识别用户与许可用户在 API 上直接执行操作之外,JWT 还可能用于其它事情。 + ```Python hl_lines="88-105" + {!> ../../../docs_src/security/tutorial004_py310.py!} + ``` -例如,识别**汽车**或**博客**。 +=== "Python 3.6+ non-Annotated" -接着,为实体添加权限,比如**驾驶**(汽车)或**编辑**(博客)。 + !!! tip + Prefer to use the `Annotated` version if possible. -然后,把 JWT 令牌交给用户(或机器人),他们就可以执行驾驶汽车,或编辑博客等操作。无需注册账户,只要有 API 生成的 JWT 令牌就可以。 + ```Python hl_lines="89-106" + {!> ../../../docs_src/security/tutorial004.py!} + ``` -同理,JWT 可以用于更复杂的场景。 +## Update the `/token` *path operation* -在这些情况下,多个实体的 ID 可能是相同的,以 ID `foo` 为例,用户的 ID 是 `foo`,车的 ID 是 `foo`,博客的 ID 也是 `foo`。 +Create a `timedelta` with the expiration time of the token. -为了避免 ID 冲突,在给用户创建 JWT 令牌时,可以为 `sub` 键的值加上前缀,例如 `username:`。因此,在本例中,`sub` 的值可以是:`username:johndoe`。 +Create a real JWT access token and return it -注意,划重点,`sub` 键在整个应用中应该只有一个唯一的标识符,而且应该是字符串。 +=== "Python 3.10+" -## 检查 + ```Python hl_lines="117-132" + {!> ../../../docs_src/security/tutorial004_an_py310.py!} + ``` -运行服务器并访问文档: http://127.0.0.1:8000/docs。 +=== "Python 3.9+" -可以看到如下用户界面: + ```Python hl_lines="117-132" + {!> ../../../docs_src/security/tutorial004_an_py39.py!} + ``` - +=== "Python 3.6+" -用与上一章同样的方式实现应用授权。 + ```Python hl_lines="118-133" + {!> ../../../docs_src/security/tutorial004_an.py!} + ``` -使用如下凭证: +=== "Python 3.10+ non-Annotated" -用户名: `johndoe` 密码: `secret` + !!! tip + Prefer to use the `Annotated` version if possible. -!!! check "检查" + ```Python hl_lines="114-127" + {!> ../../../docs_src/security/tutorial004_py310.py!} + ``` - 注意,代码中没有明文密码**`secret`**,只保存了它的哈希值。 +=== "Python 3.6+ non-Annotated" - + !!! tip + Prefer to use the `Annotated` version if possible. -调用 `/users/me/` 端点,收到下面的响应: + ```Python hl_lines="115-128" + {!> ../../../docs_src/security/tutorial004.py!} + ``` + +### Technical details about the JWT "subject" `sub` + +The JWT specification says that there's a key `sub`, with the subject of the token. + +It's optional to use it, but that's where you would put the user's identification, so we are using it here. + +JWT might be used for other things apart from identifying a user and allowing them to perform operations directly on your API. + +For example, you could identify a "car" or a "blog post". + +Then you could add permissions about that entity, like "drive" (for the car) or "edit" (for the blog). + +And then, you could give that JWT token to a user (or bot), and they could use it to perform those actions (drive the car, or edit the blog post) without even needing to have an account, just with the JWT token your API generated for that. + +Using these ideas, JWT can be used for way more sophisticated scenarios. + +In those cases, several of those entities could have the same ID, let's say `foo` (a user `foo`, a car `foo`, and a blog post `foo`). + +So, to avoid ID collisions, when creating the JWT token for the user, you could prefix the value of the `sub` key, e.g. with `username:`. So, in this example, the value of `sub` could have been: `username:johndoe`. + +The important thing to have in mind is that the `sub` key should have a unique identifier across the entire application, and it should be a string. + +## Check it + +Run the server and go to the docs: http://127.0.0.1:8000/docs. + +You'll see the user interface like: + + + +Authorize the application the same way as before. + +Using the credentials: + +Username: `johndoe` Password: `secret` + +!!! check + Notice that nowhere in the code is the plaintext password "`secret`", we only have the hashed version. + + + +Call the endpoint `/users/me/`, you will get the response as: ```JSON { @@ -227,44 +350,43 @@ JWT 规范还包括 `sub` 键,值是令牌的主题。 } ``` - - -打开浏览器的开发者工具,查看数据是怎么发送的,而且数据里只包含了令牌,只有验证用户的第一个请求才发送密码,并获取访问令牌,但之后不会再发送密码: + - +If you open the developer tools, you could see how the data sent only includes the token, the password is only sent in the first request to authenticate the user and get that access token, but not afterwards: -!!! note "笔记" + - 注意,请求中 `Authorization` 响应头的值以 `Bearer` 开头。 +!!! note + Notice the header `Authorization`, with a value that starts with `Bearer`. -## `scopes` 高级用法 +## Advanced usage with `scopes` -OAuth2 支持**`scopes`**(作用域)。 +OAuth2 has the notion of "scopes". -**`scopes`**为 JWT 令牌添加指定权限。 +You can use them to add a specific set of permissions to a JWT token. -让持有令牌的用户或第三方在指定限制条件下与 API 交互。 +Then you can give this token to a user directly or a third party, to interact with your API with a set of restrictions. -**高级用户指南**中将介绍如何使用 `scopes`,及如何把 `scopes` 集成至 **FastAPI**。 +You can learn how to use them and how they are integrated into **FastAPI** later in the **Advanced User Guide**. -## 小结 +## Recap -至此,您可以使用 OAuth2 和 JWT 等标准配置安全的 **FastAPI** 应用。 +With what you have seen up to now, you can set up a secure **FastAPI** application using standards like OAuth2 and JWT. -几乎在所有框架中,处理安全问题很快都会变得非常复杂。 +In almost any framework handling the security becomes a rather complex subject quite quickly. -有些包为了简化安全流,不得不在数据模型、数据库和功能上做出妥协。而有些过于简化的软件包其实存在了安全隐患。 +Many packages that simplify it a lot have to make many compromises with the data model, database, and available features. And some of these packages that simplify things too much actually have security flaws underneath. --- -**FastAPI** 不向任何数据库、数据模型或工具做妥协。 +**FastAPI** doesn't make any compromise with any database, data model or tool. -开发者可以灵活选择最适合项目的安全机制。 +It gives you all the flexibility to choose the ones that fit your project the best. -还可以直接使用 `passlib` 和 `python-jose` 等维护良好、使用广泛的包,这是因为 **FastAPI** 不需要任何复杂机制,就能集成外部的包。 +And you can use directly many well maintained and widely used packages like `passlib` and `python-jose`, because **FastAPI** doesn't require any complex mechanisms to integrate external packages. -而且,**FastAPI** 还提供了一些工具,在不影响灵活、稳定和安全的前提下,尽可能地简化安全机制。 +But it provides you the tools to simplify the process as much as possible without compromising flexibility, robustness, or security. -**FastAPI** 还支持以相对简单的方式,使用 OAuth2 等安全、标准的协议。 +And you can use and implement secure, standard protocols, like OAuth2 in a relatively simple way. -**高级用户指南**中详细介绍了 OAuth2**`scopes`**的内容,遵循同样的标准,实现更精密的权限系统。OAuth2 的作用域是脸书、谷歌、GitHub、微软、推特等第三方身份验证应用使用的机制,让用户授权第三方应用与 API 交互。 +You can learn more in the **Advanced User Guide** about how to use OAuth2 "scopes", for a more fine-grained permission system, following these same standards. OAuth2 with scopes is the mechanism used by many big authentication providers, like Facebook, Google, GitHub, Microsoft, Twitter, etc. to authorize third party applications to interact with their APIs on behalf of their users. From e628b0ae10c7da33831f7ac4dfb2feb67c322209 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:37:59 +0800 Subject: [PATCH 100/163] New translations simple-oauth2.md (Chinese Simplified) --- .../docs/tutorial/security/simple-oauth2.md | 414 ++++++++++++------ 1 file changed, 287 insertions(+), 127 deletions(-) diff --git a/docs/zh/docs/tutorial/security/simple-oauth2.md b/docs/zh/docs/tutorial/security/simple-oauth2.md index 276f3d63b69f9..f55ee6fb8f029 100644 --- a/docs/zh/docs/tutorial/security/simple-oauth2.md +++ b/docs/zh/docs/tutorial/security/simple-oauth2.md @@ -1,132 +1,228 @@ -# 使用密码和 Bearer 的简单 OAuth2 +# Simple OAuth2 with Password and Bearer -现在让我们接着上一章继续开发,并添加缺少的部分以实现一个完整的安全性流程。 +Now let's build from the previous chapter and add the missing parts to have a complete security flow. -## 获取 `username` 和 `password` +## Get the `username` and `password` -我们将使用 **FastAPI** 的安全性实用工具来获取 `username` 和 `password`。 +We are going to use **FastAPI** security utilities to get the `username` and `password`. -OAuth2 规定在使用(我们打算用的)「password 流程」时,客户端/用户必须将 `username` 和 `password` 字段作为表单数据发送。 +OAuth2 specifies that when using the "password flow" (that we are using) the client/user must send a `username` and `password` fields as form data. -而且规范明确了字段必须这样命名。因此 `user-name` 或 `email` 是行不通的。 +And the spec says that the fields have to be named like that. So `user-name` or `email` wouldn't work. -不过不用担心,你可以在前端按照你的想法将它展示给最终用户。 +But don't worry, you can show it as you wish to your final users in the frontend. -而且你的数据库模型也可以使用你想用的任何其他名称。 +And your database models can use any other names you want. -但是对于登录*路径操作*,我们需要使用这些名称来与规范兼容(以具备例如使用集成的 API 文档系统的能力)。 +But for the login *path operation*, we need to use these names to be compatible with the spec (and be able to, for example, use the integrated API documentation system). -规范还写明了 `username` 和 `password` 必须作为表单数据发送(因此,此处不能使用 JSON)。 +The spec also states that the `username` and `password` must be sent as form data (so, no JSON here). ### `scope` -规范还提到客户端可以发送另一个表单字段「`scope`」。 +The spec also says that the client can send another form field "`scope`". -这个表单字段的名称为 `scope`(单数形式),但实际上它是一个由空格分隔的「作用域」组成的长字符串。 +The form field name is `scope` (in singular), but it is actually a long string with "scopes" separated by spaces. -每个「作用域」只是一个字符串(中间没有空格)。 +Each "scope" is just a string (without spaces). -它们通常用于声明特定的安全权限,例如: +They are normally used to declare specific security permissions, for example: -* `users:read` 或者 `users:write` 是常见的例子。 -* Facebook / Instagram 使用 `instagram_basic`。 -* Google 使用了 `https://www.googleapis.com/auth/drive` 。 +* `users:read` or `users:write` are common examples. +* `instagram_basic` is used by Facebook / Instagram. +* `https://www.googleapis.com/auth/drive` is used by Google. !!! info - 在 OAuth2 中「作用域」只是一个声明所需特定权限的字符串。 + In OAuth2 a "scope" is just a string that declares a specific permission required. - 它有没有 `:` 这样的其他字符或者是不是 URL 都没有关系。 + It doesn't matter if it has other characters like `:` or if it is a URL. + + Those details are implementation specific. + + For OAuth2 they are just strings. - 这些细节是具体的实现。 +## Code to get the `username` and `password` - 对 OAuth2 来说它们就只是字符串而已。 +Now let's use the utilities provided by **FastAPI** to handle this. -## 获取 `username` 和 `password` 的代码 +### `OAuth2PasswordRequestForm` -现在,让我们使用 **FastAPI** 提供的实用工具来处理此问题。 +First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depends` in the *path operation* for `/token`: -### `OAuth2PasswordRequestForm` +=== "Python 3.10+" -首先,导入 `OAuth2PasswordRequestForm`,然后在 `token` 的*路径操作*中通过 `Depends` 将其作为依赖项使用。 + ```Python hl_lines="4 78" + {!> ../../../docs_src/security/tutorial003_an_py310.py!} + ``` -```Python hl_lines="4 76" -{!../../../docs_src/security/tutorial003.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="4 78" + {!> ../../../docs_src/security/tutorial003_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="4 79" + {!> ../../../docs_src/security/tutorial003_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" -`OAuth2PasswordRequestForm` 是一个类依赖项,声明了如下的请求表单: + !!! tip + Prefer to use the `Annotated` version if possible. -* `username`。 -* `password`。 -* 一个可选的 `scope` 字段,是一个由空格分隔的字符串组成的大字符串。 -* 一个可选的 `grant_type`. + ```Python hl_lines="2 74" + {!> ../../../docs_src/security/tutorial003_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="4 76" + {!> ../../../docs_src/security/tutorial003.py!} + ``` + +`OAuth2PasswordRequestForm` is a class dependency that declares a form body with: + +* The `username`. +* The `password`. +* An optional `scope` field as a big string, composed of strings separated by spaces. +* An optional `grant_type`. !!! tip - OAuth2 规范实际上*要求* `grant_type` 字段使用一个固定的值 `password`,但是 `OAuth2PasswordRequestForm` 没有作强制约束。 + The OAuth2 spec actually *requires* a field `grant_type` with a fixed value of `password`, but `OAuth2PasswordRequestForm` doesn't enforce it. - 如果你需要强制要求这一点,请使用 `OAuth2PasswordRequestFormStrict` 而不是 `OAuth2PasswordRequestForm`。 + If you need to enforce it, use `OAuth2PasswordRequestFormStrict` instead of `OAuth2PasswordRequestForm`. -* 一个可选的 `client_id`(我们的示例不需要它)。 -* 一个可选的 `client_secret`(我们的示例不需要它)。 +* An optional `client_id` (we don't need it for our example). +* An optional `client_secret` (we don't need it for our example). !!! info - `OAuth2PasswordRequestForm` 并不像 `OAuth2PasswordBearer` 一样是 FastAPI 的一个特殊的类。 + The `OAuth2PasswordRequestForm` is not a special class for **FastAPI** as is `OAuth2PasswordBearer`. - `OAuth2PasswordBearer` 使得 **FastAPI** 明白它是一个安全方案。所以它得以通过这种方式添加到 OpenAPI 中。 + `OAuth2PasswordBearer` makes **FastAPI** know that it is a security scheme. So it is added that way to OpenAPI. + + But `OAuth2PasswordRequestForm` is just a class dependency that you could have written yourself, or you could have declared `Form` parameters directly. + + But as it's a common use case, it is provided by **FastAPI** directly, just to make it easier. - 但 `OAuth2PasswordRequestForm` 只是一个你可以自己编写的类依赖项,或者你也可以直接声明 `Form` 参数。 +### Use the form data - 但是由于这是一种常见的使用场景,因此 FastAPI 出于简便直接提供了它。 +!!! tip + The instance of the dependency class `OAuth2PasswordRequestForm` won't have an attribute `scope` with the long string separated by spaces, instead, it will have a `scopes` attribute with the actual list of strings for each scope sent. -### 使用表单数据 + We are not using `scopes` in this example, but the functionality is there if you need it. -!!! tip - 类依赖项 `OAuth2PasswordRequestForm` 的实例不会有用空格分隔的长字符串属性 `scope`,而是具有一个 `scopes` 属性,该属性将包含实际被发送的每个作用域字符串组成的列表。 +Now, get the user data from the (fake) database, using the `username` from the form field. - 在此示例中我们没有使用 `scopes`,但如果你需要的话可以使用该功能。 +If there is no such user, we return an error saying "incorrect username or password". -现在,使用表单字段中的 `username` 从(伪)数据库中获取用户数据。 +For the error, we use the exception `HTTPException`: -如果没有这个用户,我们将返回一个错误消息,提示「用户名或密码错误」。 +=== "Python 3.10+" -对于这个错误,我们使用 `HTTPException` 异常: + ```Python hl_lines="3 79-81" + {!> ../../../docs_src/security/tutorial003_an_py310.py!} + ``` -```Python hl_lines="3 77-79" -{!../../../docs_src/security/tutorial003.py!} -``` +=== "Python 3.9+" -### 校验密码 + ```Python hl_lines="3 79-81" + {!> ../../../docs_src/security/tutorial003_an_py39.py!} + ``` -目前我们已经从数据库中获取了用户数据,但尚未校验密码。 +=== "Python 3.6+" -让我们首先将这些数据放入 Pydantic `UserInDB` 模型中。 + ```Python hl_lines="3 80-82" + {!> ../../../docs_src/security/tutorial003_an.py!} + ``` -永远不要保存明文密码,因此,我们将使用(伪)哈希密码系统。 +=== "Python 3.10+ non-Annotated" -如果密码不匹配,我们将返回同一个错误。 + !!! tip + Prefer to use the `Annotated` version if possible. -#### 哈希密码 + ```Python hl_lines="1 75-77" + {!> ../../../docs_src/security/tutorial003_py310.py!} + ``` -「哈希」的意思是:将某些内容(在本例中为密码)转换为看起来像乱码的字节序列(只是一个字符串)。 +=== "Python 3.6+ non-Annotated" -每次你传入完全相同的内容(完全相同的密码)时,你都会得到完全相同的乱码。 + !!! tip + Prefer to use the `Annotated` version if possible. -但是你不能从乱码转换回密码。 + ```Python hl_lines="3 77-79" + {!> ../../../docs_src/security/tutorial003.py!} + ``` -##### 为什么使用哈希密码 +### Check the password -如果你的数据库被盗,小偷将无法获得用户的明文密码,只有哈希值。 +At this point we have the user data from our database, but we haven't checked the password. -因此,小偷将无法尝试在另一个系统中使用这些相同的密码(由于许多用户在任何地方都使用相同的密码,因此这很危险)。 +Let's put that data in the Pydantic `UserInDB` model first. -```Python hl_lines="80-83" -{!../../../docs_src/security/tutorial003.py!} -``` +You should never save plaintext passwords, so, we'll use the (fake) password hashing system. + +If the passwords don't match, we return the same error. + +#### Password hashing + +"Hashing" means: converting some content (a password in this case) into a sequence of bytes (just a string) that looks like gibberish. + +Whenever you pass exactly the same content (exactly the same password) you get exactly the same gibberish. + +But you cannot convert from the gibberish back to the password. + +##### Why use password hashing + +If your database is stolen, the thief won't have your users' plaintext passwords, only the hashes. + +So, the thief won't be able to try to use those same passwords in another system (as many users use the same password everywhere, this would be dangerous). + +=== "Python 3.10+" + + ```Python hl_lines="82-85" + {!> ../../../docs_src/security/tutorial003_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="82-85" + {!> ../../../docs_src/security/tutorial003_an_py39.py!} + ``` -#### 关于 `**user_dict` +=== "Python 3.6+" -`UserInDB(**user_dict)` 表示: + ```Python hl_lines="83-86" + {!> ../../../docs_src/security/tutorial003_an.py!} + ``` -*直接将 `user_dict` 的键和值作为关键字参数传递,等同于:* +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="78-81" + {!> ../../../docs_src/security/tutorial003_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="80-83" + {!> ../../../docs_src/security/tutorial003.py!} + ``` + +#### About `**user_dict` + +`UserInDB(**user_dict)` means: + +*Pass the keys and values of the `user_dict` directly as key-value arguments, equivalent to:* ```Python UserInDB( @@ -139,92 +235,156 @@ UserInDB( ``` !!! info - 有关 `user_dict` 的更完整说明,请参阅[**额外的模型**文档](../extra-models.md#about-user_indict){.internal-link target=_blank}。 + For a more complete explanation of `**user_dict` check back in [the documentation for **Extra Models**](../extra-models.md#about-user_indict){.internal-link target=_blank}. -## 返回令牌 +## Return the token -`token` 端点的响应必须是一个 JSON 对象。 +The response of the `token` endpoint must be a JSON object. -它应该有一个 `token_type`。在我们的例子中,由于我们使用的是「Bearer」令牌,因此令牌类型应为「`bearer`」。 +It should have a `token_type`. In our case, as we are using "Bearer" tokens, the token type should be "`bearer`". -并且还应该有一个 `access_token` 字段,它是一个包含我们的访问令牌的字符串。 +And it should have an `access_token`, with a string containing our access token. -对于这个简单的示例,我们将极其不安全地返回相同的 `username` 作为令牌。 +For this simple example, we are going to just be completely insecure and return the same `username` as the token. !!! tip - 在下一章中,你将看到一个真实的安全实现,使用了哈希密码和 JWT 令牌。 + In the next chapter, you will see a real secure implementation, with password hashing and JWT tokens. - 但现在,让我们仅关注我们需要的特定细节。 + But for now, let's focus on the specific details we need. -```Python hl_lines="85" -{!../../../docs_src/security/tutorial003.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="87" + {!> ../../../docs_src/security/tutorial003_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="87" + {!> ../../../docs_src/security/tutorial003_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="88" + {!> ../../../docs_src/security/tutorial003_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="83" + {!> ../../../docs_src/security/tutorial003_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="85" + {!> ../../../docs_src/security/tutorial003.py!} + ``` !!! tip - 根据规范,你应该像本示例一样,返回一个带有 `access_token` 和 `token_type` 的 JSON。 + By the spec, you should return a JSON with an `access_token` and a `token_type`, the same as in this example. - 这是你必须在代码中自行完成的工作,并且要确保使用了这些 JSON 字段。 + This is something that you have to do yourself in your code, and make sure you use those JSON keys. + + It's almost the only thing that you have to remember to do correctly yourself, to be compliant with the specifications. + + For the rest, **FastAPI** handles it for you. - 这几乎是唯一的你需要自己记住并正确地执行以符合规范的事情。 +## Update the dependencies - 其余的,**FastAPI** 都会为你处理。 +Now we are going to update our dependencies. -## 更新依赖项 +We want to get the `current_user` *only* if this user is active. -现在我们将更新我们的依赖项。 +So, we create an additional dependency `get_current_active_user` that in turn uses `get_current_user` as a dependency. -我们想要仅当此用户处于启用状态时才能获取 `current_user`。 +Both of these dependencies will just return an HTTP error if the user doesn't exist, or if is inactive. -因此,我们创建了一个额外的依赖项 `get_current_active_user`,而该依赖项又以 `get_current_user` 作为依赖项。 +So, in our endpoint, we will only get a user if the user exists, was correctly authenticated, and is active: -如果用户不存在或处于未启用状态,则这两个依赖项都将仅返回 HTTP 错误。 +=== "Python 3.10+" -因此,在我们的端点中,只有当用户存在,身份认证通过且处于启用状态时,我们才能获得该用户: + ```Python hl_lines="58-66 69-74 94" + {!> ../../../docs_src/security/tutorial003_an_py310.py!} + ``` -```Python hl_lines="58-67 69-72 90" -{!../../../docs_src/security/tutorial003.py!} -``` +=== "Python 3.9+" -!!! info - 我们在此处返回的值为 `Bearer` 的额外响应头 `WWW-Authenticate` 也是规范的一部分。 + ```Python hl_lines="58-66 69-74 94" + {!> ../../../docs_src/security/tutorial003_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="59-67 70-75 95" + {!> ../../../docs_src/security/tutorial003_an.py!} + ``` - 任何的 401「未认证」HTTP(错误)状态码都应该返回 `WWW-Authenticate` 响应头。 +=== "Python 3.10+ non-Annotated" - 对于 bearer 令牌(我们的例子),该响应头的值应为 `Bearer`。 + !!! tip + Prefer to use the `Annotated` version if possible. - 实际上你可以忽略这个额外的响应头,不会有什么问题。 + ```Python hl_lines="56-64 67-70 88" + {!> ../../../docs_src/security/tutorial003_py310.py!} + ``` - 但此处提供了它以符合规范。 +=== "Python 3.6+ non-Annotated" - 而且,(现在或将来)可能会有工具期望得到并使用它,然后对你或你的用户有用处。 + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="58-66 69-72 90" + {!> ../../../docs_src/security/tutorial003.py!} + ``` + +!!! info + The additional header `WWW-Authenticate` with value `Bearer` we are returning here is also part of the spec. - 这就是遵循标准的好处... + Any HTTP (error) status code 401 "UNAUTHORIZED" is supposed to also return a `WWW-Authenticate` header. + + In the case of bearer tokens (our case), the value of that header should be `Bearer`. + + You can actually skip that extra header and it would still work. + + But it's provided here to be compliant with the specifications. + + Also, there might be tools that expect and use it (now or in the future) and that might be useful for you or your users, now or in the future. + + That's the benefit of standards... -## 实际效果 +## See it in action -打开交互式文档:http://127.0.0.1:8000/docs。 +Open the interactive docs: http://127.0.0.1:8000/docs. -### 身份认证 +### Authenticate -点击「Authorize」按钮。 +Click the "Authorize" button. -使用以下凭证: +Use the credentials: -用户名:`johndoe` +User: `johndoe` -密码:`secret` +Password: `secret` - + -在系统中进行身份认证后,你将看到: +After authenticating in the system, you will see it like: - + -### 获取本人的用户数据 +### Get your own user data -现在执行 `/users/me` 路径的 `GET` 操作。 +Now use the operation `GET` with the path `/users/me`. -你将获得你的用户数据,如: +You will get your user's data, like: ```JSON { @@ -236,9 +396,9 @@ UserInDB( } ``` - + -如果你点击锁定图标并注销,然后再次尝试同一操作,则会得到 HTTP 401 错误: +If you click the lock icon and logout, and then try the same operation again, you will get an HTTP 401 error of: ```JSON { @@ -246,17 +406,17 @@ UserInDB( } ``` -### 未启用的用户 +### Inactive user -现在尝试使用未启用的用户,并通过以下方式进行身份认证: +Now try with an inactive user, authenticate with: -用户名:`alice` +User: `alice` -密码:`secret2` +Password: `secret2` -然后尝试执行 `/users/me` 路径的 `GET` 操作。 +And try to use the operation `GET` with the path `/users/me`. -你将得到一个「未启用的用户」错误,如: +You will get an "inactive user" error, like: ```JSON { @@ -264,12 +424,12 @@ UserInDB( } ``` -## 总结 +## Recap -现在你掌握了为你的 API 实现一个基于 `username` 和 `password` 的完整安全系统的工具。 +You now have the tools to implement a complete security system based on `username` and `password` for your API. -使用这些工具,你可以使安全系统与任何数据库以及任何用户或数据模型兼容。 +Using these tools, you can make the security system compatible with any database and with any user or data model. -唯一缺少的细节是它实际上还并不「安全」。 +The only detail missing is that it is not actually "secure" yet. -在下一章中,你将看到如何使用一个安全的哈希密码库和 JWT 令牌。 +In the next chapter you'll see how to use a secure password hashing library and JWT tokens. From 02dd4b94da7baa201be9a39525e3462c4988cf7c Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:38:00 +0800 Subject: [PATCH 101/163] New translations sql-databases.md (Chinese Simplified) --- docs/zh/docs/tutorial/sql-databases.md | 535 +++++++++++++------------ 1 file changed, 279 insertions(+), 256 deletions(-) diff --git a/docs/zh/docs/tutorial/sql-databases.md b/docs/zh/docs/tutorial/sql-databases.md index 482588f94d7ec..136946bddddda 100644 --- a/docs/zh/docs/tutorial/sql-databases.md +++ b/docs/zh/docs/tutorial/sql-databases.md @@ -1,67 +1,76 @@ -# SQL (关系型) 数据库 +# SQL (Relational) Databases -**FastAPI**不需要你使用SQL(关系型)数据库。 +!!! info + These docs are about to be updated. 🎉 + + The current version assumes Pydantic v1, and SQLAlchemy versions less than 2.0. + + The new docs will include Pydantic v2 and will use SQLModel (which is also based on SQLAlchemy) once it is updated to use Pydantic v2 as well. -但是您可以使用任何您想要的关系型数据库。 +**FastAPI** doesn't require you to use a SQL (relational) database. -在这里,让我们看一个使用着[SQLAlchemy](https://www.sqlalchemy.org/)的示例。 +But you can use any relational database that you want. -您可以很容易地将SQLAlchemy支持任何数据库,像: +Here we'll see an example using SQLAlchemy. + +You can easily adapt it to any database supported by SQLAlchemy, like: * PostgreSQL * MySQL * SQLite * Oracle -* Microsoft SQL Server,等等其它数据库 +* Microsoft SQL Server, etc. -在此示例中,我们将使用**SQLite**,因为它使用单个文件并且 在Python中具有集成支持。因此,您可以复制此示例并按原样来运行它。 +In this example, we'll use **SQLite**, because it uses a single file and Python has integrated support. So, you can copy this example and run it as is. -稍后,对于您的产品级别的应用程序,您可能会要使用像**PostgreSQL**这样的数据库服务器。 +Later, for your production application, you might want to use a database server like **PostgreSQL**. !!! tip - 这儿有一个**FastAPI**和**PostgreSQL**的官方项目生成器,全部基于**Docker**,包括前端和更多工具:https://github.com/tiangolo/full-stack-fastapi-postgresql + There is an official project generator with **FastAPI** and **PostgreSQL**, all based on **Docker**, including a frontend and more tools: https://github.com/tiangolo/full-stack-fastapi-postgresql !!! note - 请注意,大部分代码是`SQLAlchemy`的标准代码,您可以用于任何框架。FastAPI特定的代码和往常一样少。 + Notice that most of the code is the standard `SQLAlchemy` code you would use with any framework. + + The **FastAPI** specific code is as small as always. -## ORMs(对象关系映射) +## ORMs -**FastAPI**可与任何数据库在任何样式的库中一起与 数据库进行通信。 +**FastAPI** works with any database and any style of library to talk to the database. -一种常见的模式是使用“ORM”:对象关系映射。 +A common pattern is to use an "ORM": an "object-relational mapping" library. -ORM 具有在代码和数据库表(“*关系型”)中的**对象**之间转换(“*映射*”)的工具。 +An ORM has tools to convert ("*map*") between *objects* in code and database tables ("*relations*"). -使用 ORM,您通常会在 SQL 数据库中创建一个代表映射的类,该类的每个属性代表一个列,具有名称和类型。 +With an ORM, you normally create a class that represents a table in a SQL database, each attribute of the class represents a column, with a name and a type. -例如,一个类`Pet`可以表示一个 SQL 表`pets`。 +For example a class `Pet` could represent a SQL table `pets`. -该类的每个*实例对象都代表数据库中的一行数据。* +And each *instance* object of that class represents a row in the database. -又例如,一个对象`orion_cat`(`Pet`的一个实例)可以有一个属性`orion_cat.type`, 对标数据库中的`type`列。并且该属性的值可以是其它,例如`"cat"`。 +For example an object `orion_cat` (an instance of `Pet`) could have an attribute `orion_cat.type`, for the column `type`. And the value of that attribute could be, e.g. `"cat"`. -这些 ORM 还具有在表或实体之间建立关系的工具(比如创建多表关系)。 +These ORMs also have tools to make the connections or relations between tables or entities. -这样,您还可以拥有一个属性`orion_cat.owner`,它包含该宠物所有者的数据,这些数据取自另外一个表。 +This way, you could also have an attribute `orion_cat.owner` and the owner would contain the data for this pet's owner, taken from the table *owners*. -因此,`orion_cat.owner.name`可能是该宠物主人的姓名(来自表`owners`中的列`name`)。 +So, `orion_cat.owner.name` could be the name (from the `name` column in the `owners` table) of this pet's owner. -它可能有一个像`"Arquilian"`(一种业务逻辑)。 +It could have a value like `"Arquilian"`. -当您尝试从您的宠物对象访问它时,ORM 将完成所有工作以从相应的表*所有者那里再获取信息。* +And the ORM will do all the work to get the information from the corresponding table *owners* when you try to access it from your pet object. -常见的 ORM 例如:Django-ORM(Django 框架的一部分)、SQLAlchemy ORM(SQLAlchemy 的一部分,独立于框架)和 Peewee(独立于框架)等。 +Common ORMs are for example: Django-ORM (part of the Django framework), SQLAlchemy ORM (part of SQLAlchemy, independent of framework) and Peewee (independent of framework), among others. -在这里,我们将看到如何使用**SQLAlchemy ORM**。 +Here we will see how to work with **SQLAlchemy ORM**. -以类似的方式,您也可以使用任何其他 ORM。 +In a similar way you could use any other ORM. !!! tip - 在文档中也有一篇使用 Peewee 的等效的文章。 + There's an equivalent article using Peewee here in the docs. -## 文件结构 +## File structure -对于这些示例,假设您有一个名为的目录`my_super_project`,其中包含一个名为的子目录`sql_app`,其结构如下: +For these examples, let's say you have a directory named `my_super_project` that contains a sub-directory called `sql_app` with a structure like this: ``` . @@ -74,177 +83,191 @@ ORM 具有在代码和数据库表(“*关系型”)中的**对象**之间 └── schemas.py ``` -该文件`__init__.py`只是一个空文件,但它告诉 Python 其中`sql_app`的所有模块(Python 文件)都是一个包。 +The file `__init__.py` is just an empty file, but it tells Python that `sql_app` with all its modules (Python files) is a package. -现在让我们看看每个文件/模块的作用。 +Now let's see what each file/module does. -## 创建 SQLAlchemy 部件 +## Install `SQLAlchemy` -让我们涉及到文件`sql_app/database.py`。 +First you need to install `SQLAlchemy`: -### 导入 SQLAlchemy 部件 +
+ +```console +$ pip install sqlalchemy + +---> 100% +``` + +
+ +## Create the SQLAlchemy parts + +Let's refer to the file `sql_app/database.py`. + +### Import the SQLAlchemy parts ```Python hl_lines="1-3" {!../../../docs_src/sql_databases/sql_app/database.py!} ``` -### 为 SQLAlchemy 定义数据库 URL地址 +### Create a database URL for SQLAlchemy ```Python hl_lines="5-6" {!../../../docs_src/sql_databases/sql_app/database.py!} ``` -在这个例子中,我们正在“连接”到一个 SQLite 数据库(用 SQLite 数据库打开一个文件)。 +In this example, we are "connecting" to a SQLite database (opening a file with the SQLite database). -该文件将位于文件中的同一目录中`sql_app.db`。 +The file will be located at the same directory in the file `sql_app.db`. -这就是为什么最后一部分是`./sql_app.db`. +That's why the last part is `./sql_app.db`. -如果您使用的是**PostgreSQL**数据库,则只需取消注释该行: +If you were using a **PostgreSQL** database instead, you would just have to uncomment the line: ```Python SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db" ``` -...并根据您的数据库数据和相关凭据(也适用于 MySQL、MariaDB 或任何其他)对其进行调整。 +...and adapt it with your database data and credentials (equivalently for MySQL, MariaDB or any other). !!! tip - 如果您想使用不同的数据库,这是就是您必须修改的地方。 + This is the main line that you would have to modify if you wanted to use a different database. -### 创建 SQLAlchemy 引擎 +### Create the SQLAlchemy `engine` -第一步,创建一个 SQLAlchemy的“引擎”。 +The first step is to create a SQLAlchemy "engine". -我们稍后会将这个`engine`在其他地方使用。 +We will later use this `engine` in other places. ```Python hl_lines="8-10" {!../../../docs_src/sql_databases/sql_app/database.py!} ``` -#### 注意 +#### Note -参数: +The argument: ```Python connect_args={"check_same_thread": False} ``` -...仅用于`SQLite`,在其他数据库不需要它。 - -!!! info "技术细节" +...is needed only for `SQLite`. It's not needed for other databases. - 默认情况下,SQLite 只允许一个线程与其通信,假设有多个线程的话,也只将处理一个独立的请求。 +!!! info "Technical Details" - 这是为了防止意外地为不同的事物(不同的请求)共享相同的连接。 + By default SQLite will only allow one thread to communicate with it, assuming that each thread would handle an independent request. + + This is to prevent accidentally sharing the same connection for different things (for different requests). + + But in FastAPI, using normal functions (`def`) more than one thread could interact with the database for the same request, so we need to make SQLite know that it should allow that with `connect_args={"check_same_thread": False}`. + + Also, we will make sure each request gets its own database connection session in a dependency, so there's no need for that default mechanism. - 但是在 FastAPI 中,普遍使用def函数,多个线程可以为同一个请求与数据库交互,所以我们需要使用`connect_args={"check_same_thread": False}`来让SQLite允许这样。 +### Create a `SessionLocal` class - 此外,我们将确保每个请求都在依赖项中获得自己的数据库连接会话,因此不需要该默认机制。 +Each instance of the `SessionLocal` class will be a database session. The class itself is not a database session yet. -### 创建一个`SessionLocal`类 +But once we create an instance of the `SessionLocal` class, this instance will be the actual database session. -每个实例`SessionLocal`都会是一个数据库会话。当然该类本身还不是数据库会话。 +We name it `SessionLocal` to distinguish it from the `Session` we are importing from SQLAlchemy. -但是一旦我们创建了一个`SessionLocal`类的实例,这个实例将是实际的数据库会话。 +We will use `Session` (the one imported from SQLAlchemy) later. -我们命名它是`SessionLocal`为了将它与我们从 SQLAlchemy 导入的`Session`区别开来。 - -稍后我们将使用`Session`(从 SQLAlchemy 导入的那个)。 - -要创建`SessionLocal`类,请使用函数`sessionmaker`: +To create the `SessionLocal` class, use the function `sessionmaker`: ```Python hl_lines="11" {!../../../docs_src/sql_databases/sql_app/database.py!} ``` -### 创建一个`Base`类 +### Create a `Base` class -现在我们将使用`declarative_base()`返回一个类。 +Now we will use the function `declarative_base()` that returns a class. -稍后我们将用这个类继承,来创建每个数据库模型或类(ORM 模型): +Later we will inherit from this class to create each of the database models or classes (the ORM models): ```Python hl_lines="13" {!../../../docs_src/sql_databases/sql_app/database.py!} ``` -## 创建数据库模型 +## Create the database models -现在让我们看看文件`sql_app/models.py`。 +Let's now see the file `sql_app/models.py`. -### 用`Base`类来创建 SQLAlchemy 模型 +### Create SQLAlchemy models from the `Base` class -我们将使用我们之前创建的`Base`类来创建 SQLAlchemy 模型。 +We will use this `Base` class we created before to create the SQLAlchemy models. !!! tip - SQLAlchemy 使用的“**模型**”这个术语 来指代与数据库交互的这些类和实例。 + SQLAlchemy uses the term "**model**" to refer to these classes and instances that interact with the database. - 而 Pydantic 也使用“模型”这个术语 来指代不同的东西,即数据验证、转换以及文档类和实例。 + But Pydantic also uses the term "**model**" to refer to something different, the data validation, conversion, and documentation classes and instances. -从`database`(来自上面的`database.py`文件)导入`Base`。 +Import `Base` from `database` (the file `database.py` from above). -创建从它继承的类。 +Create classes that inherit from it. -这些类就是 SQLAlchemy 模型。 +These classes are the SQLAlchemy models. ```Python hl_lines="4 7-8 18-19" {!../../../docs_src/sql_databases/sql_app/models.py!} ``` -这个`__tablename__`属性是用来告诉 SQLAlchemy 要在数据库中为每个模型使用的数据库表的名称。 +The `__tablename__` attribute tells SQLAlchemy the name of the table to use in the database for each of these models. -### 创建模型属性/列 +### Create model attributes/columns -现在创建所有模型(类)属性。 +Now create all the model (class) attributes. -这些属性中的每一个都代表其相应数据库表中的一列。 +Each of these attributes represents a column in its corresponding database table. -我们使用`Column`来表示 SQLAlchemy 中的默认值。 +We use `Column` from SQLAlchemy as the default value. -我们传递一个 SQLAlchemy “类型”,如`Integer`、`String`和`Boolean`,它定义了数据库中的类型,作为参数。 +And we pass a SQLAlchemy class "type", as `Integer`, `String`, and `Boolean`, that defines the type in the database, as an argument. ```Python hl_lines="1 10-13 21-24" {!../../../docs_src/sql_databases/sql_app/models.py!} ``` -### 创建关系 +### Create the relationships -现在创建关系。 +Now create the relationships. -为此,我们使用SQLAlchemy ORM提供的`relationship`。 +For this, we use `relationship` provided by SQLAlchemy ORM. -这将或多或少会成为一种“神奇”属性,其中表示该表与其他相关的表中的值。 +This will become, more or less, a "magic" attribute that will contain the values from other tables related to this one. ```Python hl_lines="2 15 26" {!../../../docs_src/sql_databases/sql_app/models.py!} ``` -当访问 user 中的属性`items`时,如 中`my_user.items`,它将有一个`Item`SQLAlchemy 模型列表(来自`items`表),这些模型具有指向`users`表中此记录的外键。 +When accessing the attribute `items` in a `User`, as in `my_user.items`, it will have a list of `Item` SQLAlchemy models (from the `items` table) that have a foreign key pointing to this record in the `users` table. -当您访问`my_user.items`时,SQLAlchemy 实际上会从`items`表中的获取一批记录并在此处填充进去。 +When you access `my_user.items`, SQLAlchemy will actually go and fetch the items from the database in the `items` table and populate them here. -同样,当访问 Item中的属性`owner`时,它将包含表中的`User`SQLAlchemy 模型`users`。使用`owner_id`属性/列及其外键来了解要从`users`表中获取哪条记录。 +And when accessing the attribute `owner` in an `Item`, it will contain a `User` SQLAlchemy model from the `users` table. It will use the `owner_id` attribute/column with its foreign key to know which record to get from the `users` table. -## 创建 Pydantic 模型 +## Create the Pydantic models -现在让我们查看一下文件`sql_app/schemas.py`。 +Now let's check the file `sql_app/schemas.py`. !!! tip - 为了避免 SQLAlchemy*模型*和 Pydantic*模型*之间的混淆,我们将有`models.py`(SQLAlchemy 模型的文件)和`schemas.py`( Pydantic 模型的文件)。 + To avoid confusion between the SQLAlchemy *models* and the Pydantic *models*, we will have the file `models.py` with the SQLAlchemy models, and the file `schemas.py` with the Pydantic models. - 这些 Pydantic 模型或多或少地定义了一个“schema”(一个有效的数据形状)。 + These Pydantic models define more or less a "schema" (a valid data shape). + + So this will help us avoiding confusion while using both. - 因此,这将帮助我们在使用两者时避免混淆。 +### Create initial Pydantic *models* / schemas -### 创建初始 Pydantic*模型*/模式 +Create an `ItemBase` and `UserBase` Pydantic *models* (or let's say "schemas") to have common attributes while creating or reading data. -创建一个`ItemBase`和`UserBase`Pydantic*模型*(或者我们说“schema”)以及在创建或读取数据时具有共同的属性。 +And create an `ItemCreate` and `UserCreate` that inherit from them (so they will have the same attributes), plus any additional data (attributes) needed for creation. -`ItemCreate`为 创建一个`UserCreate`继承自它们的所有属性(因此它们将具有相同的属性),以及创建所需的任何其他数据(属性)。 +So, the user will also have a `password` when creating it. -因此在创建时也应当有一个`password`属性。 - -但是为了安全起见,`password`不会出现在其他同类 Pydantic*模型*中,例如用户请求时不应该从 API 返回响应中包含它。 +But for security, the `password` won't be in other Pydantic *models*, for example, it won't be sent from the API when reading a user. === "Python 3.10+" @@ -264,31 +287,31 @@ connect_args={"check_same_thread": False} {!> ../../../docs_src/sql_databases/sql_app/schemas.py!} ``` -#### SQLAlchemy 风格和 Pydantic 风格 +#### SQLAlchemy style and Pydantic style -请注意,SQLAlchemy*模型*使用 `=`来定义属性,并将类型作为参数传递给`Column`,例如: +Notice that SQLAlchemy *models* define attributes using `=`, and pass the type as a parameter to `Column`, like in: ```Python name = Column(String) ``` -虽然 Pydantic*模型*使用`:` 声明类型,但新的类型注释语法/类型提示是: +while Pydantic *models* declare the types using `:`, the new type annotation syntax/type hints: ```Python name: str ``` -请牢记这一点,这样您在使用`:`还是`=`时就不会感到困惑。 +Have it in mind, so you don't get confused when using `=` and `:` with them. -### 创建用于读取/返回的Pydantic*模型/模式* +### Create Pydantic *models* / schemas for reading / returning -现在创建当从 API 返回数据时、将在读取数据时使用的Pydantic*模型(schemas)。* +Now create Pydantic *models* (schemas) that will be used when reading data, when returning it from the API. -例如,在创建一个项目之前,我们不知道分配给它的 ID 是什么,但是在读取它时(从 API 返回时)我们已经知道它的 ID。 +For example, before creating an item, we don't know what will be the ID assigned to it, but when reading it (when returning it from the API) we will already know its ID. -同样,当读取用户时,我们现在可以声明`items`,将包含属于该用户的项目。 +The same way, when reading a user, we can now declare that `items` will contain the items that belong to this user. -不仅是这些项目的 ID,还有我们在 Pydantic*模型*中定义的用于读取项目的所有数据:`Item`. +Not only the IDs of those items, but all the data that we defined in the Pydantic *model* for reading items: `Item`. === "Python 3.10+" @@ -309,15 +332,15 @@ name: str ``` !!! tip - 请注意,读取用户(从 API 返回)时将使用不包括`password`的`User` Pydantic*模型*。 + Notice that the `User`, the Pydantic *model* that will be used when reading a user (returning it from the API) doesn't include the `password`. -### 使用 Pydantic 的`orm_mode` +### Use Pydantic's `orm_mode` -现在,在用于查询的 Pydantic*模型*`Item`中`User`,添加一个内部`Config`类。 +Now, in the Pydantic *models* for reading, `Item` and `User`, add an internal `Config` class. -此类[`Config`](https://pydantic-docs.helpmanual.io/usage/model_config/)用于为 Pydantic 提供配置。 +This `Config` class is used to provide configurations to Pydantic. -在`Config`类中,设置属性`orm_mode = True`。 +In the `Config` class, set the attribute `orm_mode = True`. === "Python 3.10+" @@ -338,132 +361,132 @@ name: str ``` !!! tip - 请注意,它使用`=`分配一个值,例如: + Notice it's assigning a value with `=`, like: `orm_mode = True` + + It doesn't use `:` as for the type declarations before. + + This is setting a config value, not declaring a type. - 它不使用之前的`:`来类型声明。 - - 这是设置配置值,而不是声明类型。 +Pydantic's `orm_mode` will tell the Pydantic *model* to read the data even if it is not a `dict`, but an ORM model (or any other arbitrary object with attributes). -Pydantic`orm_mode`将告诉 Pydantic*模型*读取数据,即它不是一个`dict`,而是一个 ORM 模型(或任何其他具有属性的任意对象)。 - -这样,而不是仅仅试图从`dict`上 `id` 中获取值,如下所示: +This way, instead of only trying to get the `id` value from a `dict`, as in: ```Python id = data["id"] ``` -尝试从属性中获取它,如: +it will also try to get it from an attribute, as in: ```Python id = data.id ``` -有了这个,Pydantic*模型*与 ORM 兼容,您只需在*路径操作*`response_model`的参数中声明它即可。 +And with this, the Pydantic *model* is compatible with ORMs, and you can just declare it in the `response_model` argument in your *path operations*. -您将能够返回一个数据库模型,它将从中读取数据。 +You will be able to return a database model and it will read the data from it. -#### ORM 模式的技术细节 +#### Technical Details about ORM mode -SQLAlchemy 和许多其他默认情况下是“延迟加载”。 +SQLAlchemy and many others are by default "lazy loading". -这意味着,例如,除非您尝试访问包含该数据的属性,否则它们不会从数据库中获取关系数据。 +That means, for example, that they don't fetch the data for relationships from the database unless you try to access the attribute that would contain that data. -例如,访问属性`items`: +For example, accessing the attribute `items`: ```Python current_user.items ``` -将使 SQLAlchemy 转到`items`表并获取该用户的项目,在调用`.items`之前不会去查询数据库。 +would make SQLAlchemy go to the `items` table and get the items for this user, but not before. -没有`orm_mode`,如果您从*路径操作*返回一个 SQLAlchemy 模型,它不会包含关系数据。 +Without `orm_mode`, if you returned a SQLAlchemy model from your *path operation*, it wouldn't include the relationship data. -即使您在 Pydantic 模型中声明了这些关系,也没有用处。 +Even if you declared those relationships in your Pydantic models. -但是在 ORM 模式下,由于 Pydantic 本身会尝试从属性访问它需要的数据(而不是假设为 `dict`),你可以声明你想要返回的特定数据,它甚至可以从 ORM 中获取它。 +But with ORM mode, as Pydantic itself will try to access the data it needs from attributes (instead of assuming a `dict`), you can declare the specific data you want to return and it will be able to go and get it, even from ORMs. -## CRUD工具 +## CRUD utils -现在让我们看看文件`sql_app/crud.py`。 +Now let's see the file `sql_app/crud.py`. -在这个文件中,我们将编写可重用的函数用来与数据库中的数据进行交互。 +In this file we will have reusable functions to interact with the data in the database. -**CRUD**分别为:**增加**、**查询**、**更改**和**删除**,即增删改查。 +**CRUD** comes from: **C**reate, **R**ead, **U**pdate, and **D**elete. -...虽然在这个例子中我们只是新增和查询。 +...although in this example we are only creating and reading. -### 读取数据 +### Read data -从 `sqlalchemy.orm`中导入`Session`,这将允许您声明`db`参数的类型,并在您的函数中进行更好的类型检查和完成。 +Import `Session` from `sqlalchemy.orm`, this will allow you to declare the type of the `db` parameters and have better type checks and completion in your functions. -导入之前的`models`(SQLAlchemy 模型)和`schemas`(Pydantic*模型*/模式)。 +Import `models` (the SQLAlchemy models) and `schemas` (the Pydantic *models* / schemas). -创建一些实用函数来完成: +Create utility functions to: -* 通过 ID 和电子邮件查询单个用户。 -* 查询多个用户。 -* 查询多个项目。 +* Read a single user by ID and by email. +* Read multiple users. +* Read multiple items. ```Python hl_lines="1 3 6-7 10-11 14-15 27-28" {!../../../docs_src/sql_databases/sql_app/crud.py!} ``` !!! tip - 通过创建仅专用于与数据库交互(获取用户或项目)的函数,独立于*路径操作函数*,您可以更轻松地在多个部分中重用它们,并为它们添加单元测试。 + By creating functions that are only dedicated to interacting with the database (get a user or an item) independent of your *path operation function*, you can more easily reuse them in multiple parts and also add unit tests for them. -### 创建数据 +### Create data -现在创建实用程序函数来创建数据。 +Now create utility functions to create data. -它的步骤是: +The steps are: -* 使用您的数据创建一个 SQLAlchemy 模型*实例。* -* 使用`add`来将该实例对象添加到您的数据库。 -* 使用`commit`来对数据库的事务提交(以便保存它们)。 -* 使用`refresh`来刷新您的数据库实例(以便它包含来自数据库的任何新数据,例如生成的 ID)。 +* Create a SQLAlchemy model *instance* with your data. +* `add` that instance object to your database session. +* `commit` the changes to the database (so that they are saved). +* `refresh` your instance (so that it contains any new data from the database, like the generated ID). ```Python hl_lines="18-24 31-36" {!../../../docs_src/sql_databases/sql_app/crud.py!} ``` !!! tip - SQLAlchemy 模型`User`包含一个`hashed_password`,它应该是一个包含散列的安全密码。 - - 但由于 API 客户端提供的是原始密码,因此您需要将其提取并在应用程序中生成散列密码。 + The SQLAlchemy model for `User` contains a `hashed_password` that should contain a secure hashed version of the password. - 然后将hashed_password参数与要保存的值一起传递。 + But as what the API client provides is the original password, you need to extract it and generate the hashed password in your application. + + And then pass the `hashed_password` argument with the value to save. !!! warning - 此示例不安全,密码未经过哈希处理。 - - 在现实生活中的应用程序中,您需要对密码进行哈希处理,并且永远不要以明文形式保存它们。 - - 有关更多详细信息,请返回教程中的安全部分。 + This example is not secure, the password is not hashed. - 在这里,我们只关注数据库的工具和机制。 + In a real life application you would need to hash the password and never save them in plaintext. + + For more details, go back to the Security section in the tutorial. + + Here we are focusing only on the tools and mechanics of databases. !!! tip - 这里不是将每个关键字参数传递给Item并从Pydantic模型中读取每个参数,而是先生成一个字典,其中包含Pydantic模型的数据: + Instead of passing each of the keyword arguments to `Item` and reading each one of them from the Pydantic *model*, we are generating a `dict` with the Pydantic *model*'s data with: `item.dict()` - - 然后我们将dict的键值对 作为关键字参数传递给 SQLAlchemy `Item`: - + + and then we are passing the `dict`'s key-value pairs as the keyword arguments to the SQLAlchemy `Item`, with: + `Item(**item.dict())` - - 然后我们传递 Pydantic模型未提供的额外关键字参数`owner_id`: - + + And then we pass the extra keyword argument `owner_id` that is not provided by the Pydantic *model*, with: + `Item(**item.dict(), owner_id=user_id)` -## 主**FastAPI**应用程序 +## Main **FastAPI** app -现在在`sql_app/main.py`文件中 让我们集成和使用我们之前创建的所有其他部分。 +And now in the file `sql_app/main.py` let's integrate and use all the other parts we created before. -### 创建数据库表 +### Create the database tables -以非常简单的方式创建数据库表: +In a very simplistic way create the database tables: === "Python 3.9+" @@ -477,27 +500,27 @@ current_user.items {!> ../../../docs_src/sql_databases/sql_app/main.py!} ``` -#### Alembic 注意 +#### Alembic Note -通常你可能会使用 Alembic,来进行格式化数据库(创建表等)。 +Normally you would probably initialize your database (create tables, etc) with Alembic. -而且您还可以将 Alembic 用于“迁移”(这是它的主要工作)。 +And you would also use Alembic for "migrations" (that's its main job). -“迁移”是每当您更改 SQLAlchemy 模型的结构、添加新属性等以在数据库中复制这些更改、添加新列、新表等时所需的一组步骤。 +A "migration" is the set of steps needed whenever you change the structure of your SQLAlchemy models, add a new attribute, etc. to replicate those changes in the database, add a new column, a new table, etc. -您可以在[Project Generation - Template](https://fastapi.tiangolo.com/zh/project-generation/)的模板中找到一个 FastAPI 项目中的 Alembic 示例。具体在[`alembic`代码目录中](https://github.com/tiangolo/full-stack-fastapi-postgresql/tree/master/%7B%7Bcookiecutter.project_slug%7D%7D/backend/app/alembic/)。 +You can find an example of Alembic in a FastAPI project in the templates from [Project Generation - Template](../project-generation.md){.internal-link target=_blank}. Specifically in the `alembic` directory in the source code. -### 创建依赖项 +### Create a dependency -现在使用我们在`sql_app/database.py`文件中创建的`SessionLocal`来创建依赖项。 +Now use the `SessionLocal` class we created in the `sql_app/database.py` file to create a dependency. -我们需要每个请求有一个独立的数据库会话/连接(`SessionLocal`),在所有请求中使用相同的会话,然后在请求完成后关闭它。 +We need to have an independent database session/connection (`SessionLocal`) per request, use the same session through all the request and then close it after the request is finished. -然后将为下一个请求创建一个新会话。 +And then a new session will be created for the next request. -为此,我们将创建一个新的依赖项`yield`,正如前面关于[Dependencies with`yield`](https://fastapi.tiangolo.com/zh/tutorial/dependencies/dependencies-with-yield/)的部分中所解释的那样。 +For that, we will create a new dependency with `yield`, as explained before in the section about [Dependencies with `yield`](dependencies/dependencies-with-yield.md){.internal-link target=_blank}. -我们的依赖项将创建一个新的 SQLAlchemy `SessionLocal`,它将在单个请求中使用,然后在请求完成后关闭它。 +Our dependency will create a new SQLAlchemy `SessionLocal` that will be used in a single request, and then close it once the request is finished. === "Python 3.9+" @@ -512,17 +535,17 @@ current_user.items ``` !!! info - 我们将`SessionLocal()`请求的创建和处理放在一个`try`块中。 + We put the creation of the `SessionLocal()` and handling of the requests in a `try` block. - 然后我们在finally块中关闭它。 + And then we close it in the `finally` block. + + This way we make sure the database session is always closed after the request. Even if there was an exception while processing the request. + + But you can't raise another exception from the exit code (after `yield`). See more in [Dependencies with `yield` and `HTTPException`](./dependencies/dependencies-with-yield.md#dependencies-with-yield-and-httpexception){.internal-link target=_blank} - 通过这种方式,我们确保数据库会话在请求后始终关闭。即使在处理请求时出现异常。 +And then, when using the dependency in a *path operation function*, we declare it with the type `Session` we imported directly from SQLAlchemy. - 但是您不能从退出代码中引发另一个异常(在yield之后)。可以查阅 [Dependencies with yield and HTTPException](https://fastapi.tiangolo.com/zh/tutorial/dependencies/dependencies-with-yield/#dependencies-with-yield-and-httpexception) - -*然后,当在路径操作函数*中使用依赖项时,我们使用`Session`,直接从 SQLAlchemy 导入的类型声明它。 - -*这将为我们在路径操作函数*中提供更好的编辑器支持,因为编辑器将知道`db`参数的类型`Session`: +This will then give us better editor support inside the *path operation function*, because the editor will know that the `db` parameter is of type `Session`: === "Python 3.9+" @@ -536,14 +559,14 @@ current_user.items {!> ../../../docs_src/sql_databases/sql_app/main.py!} ``` -!!! info "技术细节" - 参数`db`实际上是 type `SessionLocal`,但是这个类(用 创建`sessionmaker()`)是 SQLAlchemy 的“代理” `Session`,所以,编辑器并不真正知道提供了哪些方法。 +!!! info "Technical Details" + The parameter `db` is actually of type `SessionLocal`, but this class (created with `sessionmaker()`) is a "proxy" of a SQLAlchemy `Session`, so, the editor doesn't really know what methods are provided. - 但是通过将类型声明为Session,编辑器现在可以知道可用的方法(.add()、.query()、.commit()等)并且可以提供更好的支持(比如完成)。类型声明不影响实际对象。 + But by declaring the type as `Session`, the editor now can know the available methods (`.add()`, `.query()`, `.commit()`, etc) and can provide better support (like completion). The type declaration doesn't affect the actual object. -### 创建您的**FastAPI** *路径操作* +### Create your **FastAPI** *path operations* -现在,到了最后,编写标准的**FastAPI** *路径操作*代码。 +Now, finally, here's the standard **FastAPI** *path operations* code. === "Python 3.9+" @@ -557,41 +580,41 @@ current_user.items {!> ../../../docs_src/sql_databases/sql_app/main.py!} ``` -我们在依赖项中的每个请求之前利用`yield`创建数据库会话,然后关闭它。 +We are creating the database session before each request in the dependency with `yield`, and then closing it afterwards. -所以我们就可以在*路径操作函数*中创建需要的依赖,就能直接获取会话。 +And then we can create the required dependency in the *path operation function*, to get that session directly. -这样,我们就可以直接从*路径操作函数*内部调用`crud.get_user`并使用该会话,来进行对数据库操作。 +With that, we can just call `crud.get_user` directly from inside of the *path operation function* and use that session. !!! tip - 请注意,您返回的值是 SQLAlchemy 模型或 SQLAlchemy 模型列表。 + Notice that the values you return are SQLAlchemy models, or lists of SQLAlchemy models. - 但是由于所有路径操作的response_model都使用 Pydantic模型/使用orm_mode模式,因此您的 Pydantic 模型中声明的数据将从它们中提取并返回给客户端,并进行所有正常的过滤和验证。 + But as all the *path operations* have a `response_model` with Pydantic *models* / schemas using `orm_mode`, the data declared in your Pydantic models will be extracted from them and returned to the client, with all the normal filtering and validation. !!! tip - 另请注意,`response_models`应当是标准 Python 类型,例如`List[schemas.Item]`. + Also notice that there are `response_models` that have standard Python types like `List[schemas.Item]`. - 但是由于它的内容/参数List是一个 使用orm_mode模式的Pydantic模型,所以数据将被正常检索并返回给客户端,所以没有问题。 + But as the content/parameter of that `List` is a Pydantic *model* with `orm_mode`, the data will be retrieved and returned to the client as normally, without problems. -### 关于 `def` 对比 `async def` +### About `def` vs `async def` -*在这里,我们在路径操作函数*和依赖项中都使用着 SQLAlchemy 模型,它将与外部数据库进行通信。 +Here we are using SQLAlchemy code inside of the *path operation function* and in the dependency, and, in turn, it will go and communicate with an external database. -这会需要一些“等待时间”。 +That could potentially require some "waiting". -但是由于 SQLAlchemy 不具有`await`直接使用的兼容性,因此类似于: +But as SQLAlchemy doesn't have compatibility for using `await` directly, as would be with something like: ```Python user = await db.query(User).first() ``` -...相反,我们可以使用: +...and instead we are using: ```Python user = db.query(User).first() ``` -然后我们应该声明*路径操作函数*和不带 的依赖关系`async def`,只需使用普通的`def`,如下: +Then we should declare the *path operation functions* and the dependency without `async def`, just with a normal `def`, as: ```Python hl_lines="2" @app.get("/users/{user_id}", response_model=schemas.User) @@ -601,30 +624,30 @@ def read_user(user_id: int, db: Session = Depends(get_db)): ``` !!! info - 如果您需要异步连接到关系数据库,请参阅[Async SQL (Relational) Databases](https://fastapi.tiangolo.com/zh/advanced/async-sql-databases/) + If you need to connect to your relational database asynchronously, see [Async SQL (Relational) Databases](../advanced/async-sql-databases.md){.internal-link target=_blank}. !!! note "Very Technical Details" - 如果您很好奇并且拥有深厚的技术知识,您可以在[Async](https://fastapi.tiangolo.com/zh/async/#very-technical-details)文档中查看有关如何处理 `async def`于`def`差别的技术细节。 + If you are curious and have a deep technical knowledge, you can check the very technical details of how this `async def` vs `def` is handled in the [Async](../async.md#very-technical-details){.internal-link target=_blank} docs. -## 迁移 +## Migrations -因为我们直接使用 SQLAlchemy,并且我们不需要任何类型的插件来使用**FastAPI**,所以我们可以直接将数据库迁移至[Alembic](https://alembic.sqlalchemy.org/)进行集成。 +Because we are using SQLAlchemy directly and we don't require any kind of plug-in for it to work with **FastAPI**, we could integrate database migrations with Alembic directly. -由于与 SQLAlchemy 和 SQLAlchemy 模型相关的代码位于单独的独立文件中,您甚至可以使用 Alembic 执行迁移,而无需安装 FastAPI、Pydantic 或其他任何东西。 +And as the code related to SQLAlchemy and the SQLAlchemy models lives in separate independent files, you would even be able to perform the migrations with Alembic without having to install FastAPI, Pydantic, or anything else. -同样,您将能够在与**FastAPI**无关的代码的其他部分中使用相同的 SQLAlchemy 模型和实用程序。 +The same way, you would be able to use the same SQLAlchemy models and utilities in other parts of your code that are not related to **FastAPI**. -例如,在具有[Celery](https://docs.celeryq.dev/)、[RQ](https://python-rq.org/)或[ARQ](https://arq-docs.helpmanual.io/)的后台任务工作者中。 +For example, in a background task worker with Celery, RQ, or ARQ. -## 审查所有文件 +## Review all the files -最后回顾整个案例,您应该有一个名为的目录`my_super_project`,其中包含一个名为`sql_app`。 + Remember you should have a directory named `my_super_project` that contains a sub-directory called `sql_app`. -`sql_app`中应该有以下文件: +`sql_app` should have the following files: -* `sql_app/__init__.py`:这是一个空文件。 +* `sql_app/__init__.py`: is an empty file. -* `sql_app/database.py`: +* `sql_app/database.py`: ```Python {!../../../docs_src/sql_databases/sql_app/database.py!} @@ -676,15 +699,15 @@ def read_user(user_id: int, db: Session = Depends(get_db)): {!> ../../../docs_src/sql_databases/sql_app/main.py!} ``` -## 执行项目 +## Check it -您可以复制这些代码并按原样使用它。 +You can copy this code and use it as is. !!! info - 事实上,这里的代码只是大多数测试代码的一部分。 + In fact, the code shown here is part of the tests. As most of the code in these docs. -你可以用 Uvicorn 运行它: +Then you can run it with Uvicorn:
@@ -697,31 +720,31 @@ $ uvicorn sql_app.main:app --reload
-打开浏览器进入 http://127.0.0.1:8000/docs。 +And then, you can open your browser at http://127.0.0.1:8000/docs. -您将能够与您的**FastAPI**应用程序交互,从真实数据库中读取数据: +And you will be able to interact with your **FastAPI** application, reading data from a real database: - + -## 直接与数据库交互 +## Interact with the database directly -如果您想独立于 FastAPI 直接浏览 SQLite 数据库(文件)以调试其内容、添加表、列、记录、修改数据等,您可以使用[SQLite 的 DB Browser](https://sqlitebrowser.org/) +If you want to explore the SQLite database (file) directly, independently of FastAPI, to debug its contents, add tables, columns, records, modify data, etc. you can use DB Browser for SQLite. -它看起来像这样: +It will look like this: - + -您还可以使用[SQLite Viewer](https://inloop.github.io/sqlite-viewer/)或[ExtendsClass](https://extendsclass.com/sqlite-browser.html)等在线 SQLite 浏览器。 +You can also use an online SQLite browser like SQLite Viewer or ExtendsClass. -## 中间件替代数据库会话 +## Alternative DB session with middleware -如果你不能使用依赖项`yield`——例如,如果你没有使用**Python 3.7**并且不能安装上面提到的**Python 3.6**的“backports” ——你可以在类似的“中间件”中设置会话方法。 +If you can't use dependencies with `yield` -- for example, if you are not using **Python 3.7** and can't install the "backports" mentioned above for **Python 3.6** -- you can set up the session in a "middleware" in a similar way. -“中间件”基本功能是一个为每个请求执行的函数在请求之前进行执行相应的代码,以及在请求执行之后执行相应的代码。 +A "middleware" is basically a function that is always executed for each request, with some code executed before, and some code executed after the endpoint function. -### 创建中间件 +### Create a middleware -我们将添加中间件(只是一个函数)将为每个请求创建一个新的 SQLAlchemy`SessionLocal`,将其添加到请求中,然后在请求完成后关闭它。 +The middleware we'll add (just a function) will create a new SQLAlchemy `SessionLocal` for each request, add it to the request and then close it once the request is finished. === "Python 3.9+" @@ -736,35 +759,35 @@ $ uvicorn sql_app.main:app --reload ``` !!! info - 我们将`SessionLocal()`请求的创建和处理放在一个`try`块中。 - - 然后我们在finally块中关闭它。 + We put the creation of the `SessionLocal()` and handling of the requests in a `try` block. - 通过这种方式,我们确保数据库会话在请求后始终关闭,即使在处理请求时出现异常也会关闭。 + And then we close it in the `finally` block. + + This way we make sure the database session is always closed after the request. Even if there was an exception while processing the request. -### 关于`request.state` +### About `request.state` -`request.state`是每个`Request`对象的属性。它用于存储附加到请求本身的任意对象,例如本例中的数据库会话。您可以在[Starlette 的关于`Request`state](https://www.starlette.io/requests/#other-state)的文档中了解更多信息。 +`request.state` is a property of each `Request` object. It is there to store arbitrary objects attached to the request itself, like the database session in this case. You can read more about it in Starlette's docs about `Request` state. -对于这种情况下,它帮助我们确保在所有请求中使用单个数据库会话,然后关闭(在中间件中)。 +For us in this case, it helps us ensure a single database session is used through all the request, and then closed afterwards (in the middleware). -### 使用`yield`依赖项与使用中间件的区别 +### Dependencies with `yield` or middleware -在此处添加**中间件**与`yield`的依赖项的作用效果类似,但也有一些区别: +Adding a **middleware** here is similar to what a dependency with `yield` does, with some differences: -* 中间件需要更多的代码并且更复杂一些。 -* 中间件必须是一个`async`函数。 - * 如果其中有代码必须“等待”网络,它可能会在那里“阻止”您的应用程序并稍微降低性能。 - * 尽管这里的`SQLAlchemy`工作方式可能不是很成问题。 - * 但是,如果您向等待大量I/O的中间件添加更多代码,则可能会出现问题。 -* *每个*请求都会运行一个中间件。 - * 将为每个请求创建一个连接。 - * 即使处理该请求的*路径操作*不需要数据库。 +* It requires more code and is a bit more complex. +* The middleware has to be an `async` function. + * If there is code in it that has to "wait" for the network, it could "block" your application there and degrade performance a bit. + * Although it's probably not very problematic here with the way `SQLAlchemy` works. + * But if you added more code to the middleware that had a lot of I/O waiting, it could then be problematic. +* A middleware is run for *every* request. + * So, a connection will be created for every request. + * Even when the *path operation* that handles that request didn't need the DB. !!! tip - `tyield`当依赖项 足以满足用例时,使用`tyield`依赖项方法会更好。 + It's probably better to use dependencies with `yield` when they are enough for the use case. !!! info - `yield`的依赖项是最近刚加入**FastAPI**中的。 + Dependencies with `yield` were added recently to **FastAPI**. - 所以本教程的先前版本只有带有中间件的示例,并且可能有多个应用程序使用中间件进行数据库会话管理。 + A previous version of this tutorial only had the examples with a middleware and there are probably several applications using the middleware for database session management. From 2f64a0149e32e94ef84b74099548833dace4f72f Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:38:01 +0800 Subject: [PATCH 102/163] New translations static-files.md (Chinese Simplified) --- docs/zh/docs/tutorial/static-files.md | 38 +++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/zh/docs/tutorial/static-files.md b/docs/zh/docs/tutorial/static-files.md index e7c5c3f0a16b0..7a0c36af3f4c5 100644 --- a/docs/zh/docs/tutorial/static-files.md +++ b/docs/zh/docs/tutorial/static-files.md @@ -1,39 +1,39 @@ -# 静态文件 +# Static Files -您可以使用 `StaticFiles`从目录中自动提供静态文件。 +You can serve static files automatically from a directory using `StaticFiles`. -## 使用`StaticFiles` +## Use `StaticFiles` -* 导入`StaticFiles`。 -* "挂载"(Mount) 一个 `StaticFiles()` 实例到一个指定路径。 +* Import `StaticFiles`. +* "Mount" a `StaticFiles()` instance in a specific path. ```Python hl_lines="2 6" {!../../../docs_src/static_files/tutorial001.py!} ``` -!!! note "技术细节" - 你也可以用 `from starlette.staticfiles import StaticFiles`。 +!!! note "Technical Details" + You could also use `from starlette.staticfiles import StaticFiles`. - **FastAPI** 提供了和 `starlette.staticfiles` 相同的 `fastapi.staticfiles` ,只是为了方便你,开发者。但它确实来自Starlette。 + **FastAPI** provides the same `starlette.staticfiles` as `fastapi.staticfiles` just as a convenience for you, the developer. But it actually comes directly from Starlette. -### 什么是"挂载"(Mounting) +### What is "Mounting" -"挂载" 表示在特定路径添加一个完全"独立的"应用,然后负责处理所有子路径。 +"Mounting" means adding a complete "independent" application in a specific path, that then takes care of handling all the sub-paths. -这与使用`APIRouter`不同,因为安装的应用程序是完全独立的。OpenAPI和来自你主应用的文档不会包含已挂载应用的任何东西等等。 +This is different from using an `APIRouter` as a mounted application is completely independent. The OpenAPI and docs from your main application won't include anything from the mounted application, etc. -你可以在**高级用户指南**中了解更多。 +You can read more about this in the **Advanced User Guide**. -## 细节 +## Details -这个 "子应用" 会被 "挂载" 到第一个 `"/static"` 指向的子路径。因此,任何以`"/static"`开头的路径都会被它处理。 +The first `"/static"` refers to the sub-path this "sub-application" will be "mounted" on. So, any path that starts with `"/static"` will be handled by it. - `directory="static"` 指向包含你的静态文件的目录名字。 +The `directory="static"` refers to the name of the directory that contains your static files. -`name="static"` 提供了一个能被**FastAPI**内部使用的名字。 +The `name="static"` gives it a name that can be used internally by **FastAPI**. -所有这些参数可以不同于"`static`",根据你应用的需要和具体细节调整它们。 +All these parameters can be different than "`static`", adjust them with the needs and specific details of your own application. -## 更多信息 +## More info -更多细节和选择查阅 Starlette's docs about Static Files. +For more details and options check Starlette's docs about Static Files. From 6ead130ec1fd5ba9cda5af424418531a434b7659 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:38:02 +0800 Subject: [PATCH 103/163] New translations testing.md (Chinese Simplified) --- docs/zh/docs/tutorial/testing.md | 118 +++++++++++++++---------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/docs/zh/docs/tutorial/testing.md b/docs/zh/docs/tutorial/testing.md index 41f01f8d84a41..3609d39d9f5b1 100644 --- a/docs/zh/docs/tutorial/testing.md +++ b/docs/zh/docs/tutorial/testing.md @@ -1,56 +1,56 @@ -# 测试 +# Testing -感谢 Starlette,测试**FastAPI** 应用轻松又愉快。 +Thanks to Starlette, testing **FastAPI** applications is easy and enjoyable. -它基于 HTTPX, 而HTTPX又是基于Requests设计的,所以很相似且易懂。 +It is based on HTTPX, which in turn is designed based on Requests, so it's very familiar and intuitive. -有了它,你可以直接与**FastAPI**一起使用 pytest。 +With it, you can use pytest directly with **FastAPI**. -## 使用 `TestClient` +## Using `TestClient` -!!! 信息 - 要使用 `TestClient`,先要安装 `httpx`. +!!! info + To use `TestClient`, first install `httpx`. - 例:`pip install httpx`. + E.g. `pip install httpx`. -导入 `TestClient`. +Import `TestClient`. -通过传入你的**FastAPI**应用创建一个 `TestClient` 。 +Create a `TestClient` by passing your **FastAPI** application to it. -创建名字以 `test_` 开头的函数(这是标准的 `pytest` 约定)。 +Create functions with a name that starts with `test_` (this is standard `pytest` conventions). -像使用 `httpx` 那样使用 `TestClient` 对象。 +Use the `TestClient` object the same way as you do with `httpx`. -为你需要检查的地方用标准的Python表达式写个简单的 `assert` 语句(重申,标准的`pytest`)。 +Write simple `assert` statements with the standard Python expressions that you need to check (again, standard `pytest`). ```Python hl_lines="2 12 15-18" {!../../../docs_src/app_testing/tutorial001.py!} ``` -!!! 提示 - 注意测试函数是普通的 `def`,不是 `async def`。 +!!! tip + Notice that the testing functions are normal `def`, not `async def`. - 还有client的调用也是普通的调用,不是用 `await`。 + And the calls to the client are also normal calls, not using `await`. + + This allows you to use `pytest` directly without complications. - 这让你可以直接使用 `pytest` 而不会遇到麻烦。 +!!! note "Technical Details" + You could also use `from starlette.testclient import TestClient`. -!!! note "技术细节" - 你也可以用 `from starlette.testclient import TestClient`。 + **FastAPI** provides the same `starlette.testclient` as `fastapi.testclient` just as a convenience for you, the developer. But it comes directly from Starlette. - **FastAPI** 提供了和 `starlette.testclient` 一样的 `fastapi.testclient`,只是为了方便开发者。但它直接来自Starlette。 +!!! tip + If you want to call `async` functions in your tests apart from sending requests to your FastAPI application (e.g. asynchronous database functions), have a look at the [Async Tests](../advanced/async-tests.md){.internal-link target=_blank} in the advanced tutorial. -!!! 提示 - 除了发送请求之外,如果你还想测试时在FastAPI应用中调用 `async` 函数(例如异步数据库函数), 可以在高级教程中看下 [Async Tests](../advanced/async-tests.md){.internal-link target=_blank} 。 +## Separating tests -## 分离测试 +In a real application, you probably would have your tests in a different file. -在实际应用中,你可能会把你的测试放在另一个文件里。 +And your **FastAPI** application might also be composed of several files/modules, etc. -您的**FastAPI**应用程序也可能由一些文件/模块组成等等。 +### **FastAPI** app file -### **FastAPI** app 文件 - -假设你有一个像 [更大的应用](./bigger-applications.md){.internal-link target=_blank} 中所描述的文件结构: +Let's say you have a file structure as described in [Bigger Applications](./bigger-applications.md){.internal-link target=_blank}: ``` . @@ -59,16 +59,16 @@ │   └── main.py ``` -在 `main.py` 文件中你有一个 **FastAPI** app: +In the file `main.py` you have your **FastAPI** app: ```Python {!../../../docs_src/app_testing/main.py!} ``` -### 测试文件 +### Testing file -然后你会有一个包含测试的文件 `test_main.py` 。app可以像Python包那样存在(一样是目录,但有个 `__init__.py` 文件): +Then you could have a file `test_main.py` with your tests. It could live on the same Python package (the same directory with a `__init__.py` file): ``` hl_lines="5" . @@ -78,21 +78,21 @@ │   └── test_main.py ``` -因为这文件在同一个包中,所以你可以通过相对导入从 `main` 模块(`main.py`)导入`app`对象: +Because this file is in the same package, you can use relative imports to import the object `app` from the `main` module (`main.py`): ```Python hl_lines="3" {!../../../docs_src/app_testing/test_main.py!} ``` -...然后测试代码和之前一样的。 +...and have the code for the tests just like before. -## 测试:扩展示例 +## Testing: extended example -现在让我们扩展这个例子,并添加更多细节,看下如何测试不同部分。 +Now let's extend this example and add more details to see how to test different parts. -### 扩展后的 **FastAPI** app 文件 +### Extended **FastAPI** app file -让我们继续之前的文件结构: +Let's continue with the same file structure as before: ``` . @@ -102,13 +102,13 @@ │   └── test_main.py ``` -假设现在包含**FastAPI** app的文件 `main.py` 有些其他**路径操作**。 +Let's say that now the file `main.py` with your **FastAPI** app has some other **path operations**. -有个 `GET` 操作会返回错误。 +It has a `GET` operation that could return an error. -有个 `POST` 操作会返回一些错误。 +It has a `POST` operation that could return several errors. -所有*路径操作* 都需要一个`X-Token` 头。 +Both *path operations* require an `X-Token` header. === "Python 3.10+" @@ -146,36 +146,36 @@ {!> ../../../docs_src/app_testing/app_b/main.py!} ``` -### 扩展后的测试文件 +### Extended testing file -然后您可以使用扩展后的测试更新`test_main.py`: +You could then update `test_main.py` with the extended tests: ```Python {!> ../../../docs_src/app_testing/app_b/test_main.py!} ``` -每当你需要客户端在请求中传递信息,但你不知道如何传递时,你可以通过搜索(谷歌)如何用 `httpx`做,或者是用 `requests` 做,毕竟HTTPX的设计是基于Requests的设计的。 +Whenever you need the client to pass information in the request and you don't know how to, you can search (Google) how to do it in `httpx`, or even how to do it with `requests`, as HTTPX's design is based on Requests' design. -接着只需在测试中同样操作。 +Then you just do the same in your tests. -示例: +E.g.: -* 传一个*路径* 或*查询* 参数,添加到URL上。 -* 传一个JSON体,传一个Python对象(例如一个`dict`)到参数 `json`。 -* 如果你需要发送 *Form Data* 而不是 JSON,使用 `data` 参数。 -* 要发送 *headers*,传 `dict` 给 `headers` 参数。 -* 对于 *cookies*,传 `dict` 给 `cookies` 参数。 +* To pass a *path* or *query* parameter, add it to the URL itself. +* To pass a JSON body, pass a Python object (e.g. a `dict`) to the parameter `json`. +* If you need to send *Form Data* instead of JSON, use the `data` parameter instead. +* To pass *headers*, use a `dict` in the `headers` parameter. +* For *cookies*, a `dict` in the `cookies` parameter. -关于如何传数据给后端的更多信息 (使用`httpx` 或 `TestClient`),请查阅 HTTPX 文档. +For more information about how to pass data to the backend (using `httpx` or the `TestClient`) check the HTTPX documentation. -!!! 信息 - 注意 `TestClient` 接收可以被转化为JSON的数据,而不是Pydantic模型。 +!!! info + Note that the `TestClient` receives data that can be converted to JSON, not Pydantic models. - 如果你在测试中有一个Pydantic模型,并且你想在测试时发送它的数据给应用,你可以使用在[JSON Compatible Encoder](encoder.md){.internal-link target=_blank}介绍的`jsonable_encoder` 。 + If you have a Pydantic model in your test and you want to send its data to the application during testing, you can use the `jsonable_encoder` described in [JSON Compatible Encoder](encoder.md){.internal-link target=_blank}. -## 运行起来 +## Run it -之后,你只需要安装 `pytest`: +After that, you just need to install `pytest`:
@@ -187,9 +187,9 @@ $ pip install pytest
-他会自动检测文件和测试,执行测试,然后向你报告结果。 +It will detect the files and tests automatically, execute them, and report the results back to you. -执行测试: +Run the tests with:
From 132134c004d4693c5dbe11e2e0f8f4d8e95ed801 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:40:36 +0800 Subject: [PATCH 104/163] New translations additional-status-codes.md (Chinese Simplified) --- .../docs/advanced/additional-status-codes.md | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/docs/zh/docs/advanced/additional-status-codes.md b/docs/zh/docs/advanced/additional-status-codes.md index 2bccdbb402b2b..9981a8c7b5eec 100644 --- a/docs/zh/docs/advanced/additional-status-codes.md +++ b/docs/zh/docs/advanced/additional-status-codes.md @@ -1,30 +1,36 @@ -# Additional Status Codes +# 额外的状态码 -By default, **FastAPI** will return the responses using a `JSONResponse`, putting the content you return from your *path operation* inside of that `JSONResponse`. +**FastAPI** 默认使用 `JSONResponse` 返回一个响应,将你的 *路径操作* 中的返回内容放到该 `JSONResponse` 中。 -It will use the default status code or the one you set in your *path operation*. +**FastAPI** 会自动使用默认的状态码或者使用你在 *路径操作* 中设置的状态码。 -## Additional status codes +## 额外的状态码 -If you want to return additional status codes apart from the main one, you can do that by returning a `Response` directly, like a `JSONResponse`, and set the additional status code directly. +如果你想要返回主要状态码之外的状态码,你可以通过直接返回一个 `Response` 来实现,比如 `JSONResponse`,然后直接设置额外的状态码。 -For example, let's say that you want to have a *path operation* that allows to update items, and returns HTTP status codes of 200 "OK" when successful. +例如,假设你想有一个 *路径操作* 能够更新条目,并且更新成功时返回 200 「成功」 的 HTTP 状态码。 -But you also want it to accept new items. And when the items didn't exist before, it creates them, and returns an HTTP status code of 201 "Created". +但是你也希望它能够接受新的条目。 并且当这些条目不存在时,会自动创建并返回 201 「创建」的 HTTP 状态码。 -To achieve that, import `JSONResponse`, and return your content there directly, setting the `status_code` that you want: +要实现它,导入 `JSONResponse`,然后在其中直接返回你的内容,并将 `status_code` 设置为为你要的值。 === "Python 3.10+" ```Python hl_lines="4 25" - {!> ../../../docs_src/additional_status_codes/tutorial001_an_py310.py!} + !!! note "技术细节" + 你也可以使用 from starlette.responses import JSONResponse。 ``` +。 + === "Python 3.9+" ```Python hl_lines="4 25" - {!> ../../../docs_src/additional_status_codes/tutorial001_an_py39.py!} + !!! warning "警告" + 当你直接返回一个像上面例子中的 Response 对象时,它会直接返回。 ``` + 对象时,它会直接返回。 + === "Python 3.6+" @@ -47,23 +53,21 @@ To achieve that, import `JSONResponse`, and return your content there directly, Prefer to use the `Annotated` version if possible. ```Python hl_lines="4 25" - {!> ../../../docs_src/additional_status_codes/tutorial001.py!} + {!../../../docs_src/additional_status_codes/tutorial001.py!} ``` !!! warning When you return a `Response` directly, like in the example above, it will be returned directly. - It won't be serialized with a model, etc. - - Make sure it has the data you want it to have, and that the values are valid JSON (if you are using `JSONResponse`). + FastAPI 不会用模型等对该响应进行序列化。 确保其中有你想要的数据,且返回的值为合法的 JSON(如果你使用 `JSONResponse` 的话)。 !!! note "Technical Details" You could also use `from starlette.responses import JSONResponse`. - **FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. The same with `status`. + 出于方便,**FastAPI** 为开发者提供同 `starlette.responses` 一样的 `fastapi.responses`。 但是大多数可用的响应都是直接来自 Starlette。 `status` 也是一样。 -## OpenAPI and API docs +## OpenAPI 和 API 文档 -If you return additional status codes and responses directly, they won't be included in the OpenAPI schema (the API docs), because FastAPI doesn't have a way to know beforehand what you are going to return. +如果你直接返回额外的状态码和响应,它们不会包含在 OpenAPI 方案(API 文档)中,因为 FastAPI 没办法预先知道你要返回什么。 -But you can document that in your code, using: [Additional Responses](additional-responses.md){.internal-link target=_blank}. +但是你可以使用 [额外的响应](additional-responses.md){.internal-link target=_blank} 在代码中记录这些内容。 From f70e7ce53198dba258b2671f46c7bb03093dd42e Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:40:39 +0800 Subject: [PATCH 105/163] New translations custom-response.md (Chinese Simplified) --- docs/zh/docs/advanced/custom-response.md | 154 ++++++++++++----------- 1 file changed, 81 insertions(+), 73 deletions(-) diff --git a/docs/zh/docs/advanced/custom-response.md b/docs/zh/docs/advanced/custom-response.md index e3876f2928643..cc557dfde50ca 100644 --- a/docs/zh/docs/advanced/custom-response.md +++ b/docs/zh/docs/advanced/custom-response.md @@ -1,31 +1,31 @@ -# Custom Response - HTML, Stream, File, others +# 自定义响应 - HTML,流,文件和其他 -By default, **FastAPI** will return the responses using `JSONResponse`. +**FastAPI** 默认会使用 `JSONResponse` 返回响应。 -You can override it by returning a `Response` directly as seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}. +你可以通过直接返回 `Response` 来重载它,参见 [直接返回响应](response-directly.md){.internal-link target=_blank}。 -But if you return a `Response` directly, the data won't be automatically converted, and the documentation won't be automatically generated (for example, including the specific "media type", in the HTTP header `Content-Type` as part of the generated OpenAPI). +但如果你直接返回 `Response`,返回数据不会自动转换,也不会自动生成文档(例如,在 HTTP 头 `Content-Type` 中包含特定的「媒体类型」作为生成的 OpenAPI 的一部分)。 -But you can also declare the `Response` that you want to be used, in the *path operation decorator*. +你还可以在 *路径操作装饰器* 中声明你想用的 `Response`。 -The contents that you return from your *path operation function* will be put inside of that `Response`. +你从 *路径操作函数* 中返回的内容将被放在该 `Response` 中。 -And if that `Response` has a JSON media type (`application/json`), like is the case with the `JSONResponse` and `UJSONResponse`, the data you return will be automatically converted (and filtered) with any Pydantic `response_model` that you declared in the *path operation decorator*. +并且如果该 `Response` 有一个 JSON 媒体类型(`application/json`),比如使用 `JSONResponse` 或者 `UJSONResponse` 的时候,返回的数据将使用你在路径操作装饰器中声明的任何 Pydantic 的 `response_model` 自动转换(和过滤)。 !!! note If you use a response class with no media type, FastAPI will expect your response to have no content, so it will not document the response format in its generated OpenAPI docs. -## Use `ORJSONResponse` +## 使用 `ORJSONResponse` -For example, if you are squeezing performance, you can install and use `orjson` and set the response to be `ORJSONResponse`. +例如,如果你需要压榨性能,你可以安装并使用 `orjson` 并将响应设置为 `ORJSONResponse`。 -Import the `Response` class (sub-class) you want to use and declare it in the *path operation decorator*. +导入你想要使用的 `Response` 类(子类)然后在 *路径操作装饰器* 中声明它。 For large responses, returning a `Response` directly is much faster than returning a dictionary. This is because by default, FastAPI will inspect every item inside and make sure it is serializable with JSON, using the same [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank} explained in the tutorial. This is what allows you to return **arbitrary objects**, for example database models. -But if you are certain that the content that you are returning is **serializable with JSON**, you can pass it directly to the response class and avoid the extra overhead that FastAPI would have by passing your return content through the `jsonable_encoder` before passing it to the response class. +使用 `HTMLResponse` 来从 **FastAPI** 中直接返回一个 HTML 响应。 ```Python hl_lines="2 7" {!../../../docs_src/custom_response/tutorial001b.py!} @@ -34,19 +34,20 @@ But if you are certain that the content that you are returning is **serializable !!! info The parameter `response_class` will also be used to define the "media type" of the response. - In this case, the HTTP header `Content-Type` will be set to `application/json`. + 在这个例子中,HTTP 头的 `Content-Type` 会被设置成 `application/json`。 - And it will be documented as such in OpenAPI. + 并且在 OpenAPI 文档中也会这样记录。 !!! tip The `ORJSONResponse` is currently only available in FastAPI, not in Starlette. -## HTML Response +## HTML 响应 -To return a response with HTML directly from **FastAPI**, use `HTMLResponse`. +!!! warning "警告" + *路径操作函数* 直接返回的 `Response` 不会被 OpenAPI 的文档记录(比如,`Content-Type` 不会被文档记录),并且在自动化交互文档中也是不可见的。 -* Import `HTMLResponse`. -* Pass `HTMLResponse` as the parameter `response_class` of your *path operation decorator*. +* 导入 `HTMLResponse`。 +* 将 `HTMLResponse` 作为你的 *路径操作* 的 `response_class` 参数传入。 ```Python hl_lines="2 7" {!../../../docs_src/custom_response/tutorial002.py!} @@ -55,15 +56,15 @@ To return a response with HTML directly from **FastAPI**, use `HTMLResponse`. !!! info The parameter `response_class` will also be used to define the "media type" of the response. - In this case, the HTTP header `Content-Type` will be set to `text/html`. + 在这个例子中,HTTP 头的 `Content-Type` 会被设置成 `text/html`。 - And it will be documented as such in OpenAPI. + 并且在 OpenAPI 文档中也会这样记录。 -### Return a `Response` +### 返回一个 `Response` -As seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}, you can also override the response directly in your *path operation*, by returning it. +正如你在 [直接返回响应](response-directly.md){.internal-link target=_blank} 中了解到的,你也可以通过直接返回响应在 *路径操作* 中直接重载响应。 -The same example from above, returning an `HTMLResponse`, could look like: +和上面一样的例子,返回一个 `HTMLResponse` 看起来可能是这样: ```Python hl_lines="2 7 19" {!../../../docs_src/custom_response/tutorial003.py!} @@ -75,53 +76,53 @@ The same example from above, returning an `HTMLResponse`, could look like: !!! info Of course, the actual `Content-Type` header, status code, etc, will come from the `Response` object your returned. -### Document in OpenAPI and override `Response` +### OpenAPI 中的文档和重载 `Response` -If you want to override the response from inside of the function but at the same time document the "media type" in OpenAPI, you can use the `response_class` parameter AND return a `Response` object. +如果你想要在函数内重载响应,但是同时在 OpenAPI 中文档化「媒体类型」,你可以使用 `response_class` 参数并返回一个 `Response` 对象。 -The `response_class` will then be used only to document the OpenAPI *path operation*, but your `Response` will be used as is. +接着 `response_class` 参数只会被用来文档化 OpenAPI 的 *路径操作*,你的 `Response` 用来返回响应。 -#### Return an `HTMLResponse` directly +#### 直接返回 `HTMLResponse` -For example, it could be something like: +比如像这样: ```Python hl_lines="7 21 23" {!../../../docs_src/custom_response/tutorial004.py!} ``` -In this example, the function `generate_html_response()` already generates and returns a `Response` instead of returning the HTML in a `str`. +在这个例子中,函数 `generate_html_response()` 已经生成并返回 `Response` 对象而不是在 `str` 中返回 HTML。 -By returning the result of calling `generate_html_response()`, you are already returning a `Response` that will override the default **FastAPI** behavior. +通过返回函数 `generate_html_response()` 的调用结果,你已经返回一个重载 **FastAPI** 默认行为的 `Response` 对象, -But as you passed the `HTMLResponse` in the `response_class` too, **FastAPI** will know how to document it in OpenAPI and the interactive docs as HTML with `text/html`: +但如果你在 `response_class` 中也传入了 `HTMLResponse`,**FastAPI** 会知道如何在 OpenAPI 和交互式文档中使用 `text/html` 将其文档化为 HTML。 -## Available responses +## 可用响应 -Here are some of the available responses. +这里有一些可用的响应。 -Have in mind that you can use `Response` to return anything else, or even create a custom sub-class. +要记得你可以使用 `Response` 来返回任何其他东西,甚至创建一个自定义的子类。 !!! note "Technical Details" You could also use `from starlette.responses import HTMLResponse`. - **FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. + **FastAPI** 提供了同 `fastapi.responses` 相同的 `starlette.responses` 只是为了方便开发者。 但大多数可用的响应都直接来自 Starlette。 ### `Response` -The main `Response` class, all the other responses inherit from it. +其他全部的响应都继承自主类 `Response`。 -You can return it directly. +你可以直接返回它。 It accepts the following parameters: -* `content` - A `str` or `bytes`. -* `status_code` - An `int` HTTP status code. -* `headers` - A `dict` of strings. +* `content` - 一个 `str` 或者 `bytes`。 +* `status_code` - 一个 `int` 类型的 HTTP 状态码。 +* `headers` - 一个由字符串组成的 `dict`。 * `media_type` - A `str` giving the media type. E.g. `"text/html"`. -FastAPI (actually Starlette) will automatically include a Content-Length header. It will also include a Content-Type header, based on the media_type and appending a charset for text types. +FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。 它还将包含一个基于 media_type 的 Content-Type 头,并为文本类型附加一个字符集。 ```Python hl_lines="1 18" {!../../../docs_src/response_directly/tutorial002.py!} @@ -129,11 +130,11 @@ FastAPI (actually Starlette) will automatically include a Content-Length header. ### `HTMLResponse` -Takes some text or bytes and returns an HTML response, as you read above. +如上文所述,接受文本或字节并返回 HTML 响应。 ### `PlainTextResponse` -Takes some text or bytes and returns an plain text response. +接受文本或字节并返回纯文本响应。 ```Python hl_lines="2 7 9" {!../../../docs_src/custom_response/tutorial005.py!} @@ -141,20 +142,20 @@ Takes some text or bytes and returns an plain text response. ### `JSONResponse` -Takes some data and returns an `application/json` encoded response. +接受数据并返回一个 `application/json` 编码的响应。 -This is the default response used in **FastAPI**, as you read above. +如上文所述,这是 **FastAPI** 中使用的默认响应。 ### `ORJSONResponse` -A fast alternative JSON response using `orjson`, as you read above. +如上文所述,`ORJSONResponse` 是一个使用 `orjson` 的快速的可选 JSON 响应。 ### `UJSONResponse` -An alternative JSON response using `ujson`. +`UJSONResponse` 是一个使用 `ujson` 的可选 JSON 响应。 -!!! warning - `ujson` is less careful than Python's built-in implementation in how it handles some edge-cases. +!!! !!! warning "警告" + 在处理某些边缘情况时,`ujson` 不如 Python 的内置实现那么谨慎。 ```Python hl_lines="2 7" {!../../../docs_src/custom_response/tutorial001.py!} @@ -165,9 +166,10 @@ An alternative JSON response using `orjson`, but with some custom settings not used in the included `ORJSONResponse` class. +!!! tip "小贴士" + `ORJSONResponse` 可能是一个更快的选择。 Let's say you want it to return indented and formatted JSON, so you want to use the orjson option `orjson.OPT_INDENT_2`. @@ -282,11 +287,14 @@ Of course, you will probably find much better ways to take advantage of this tha ## Default response class -When creating a **FastAPI** class instance or an `APIRouter` you can specify which response class to use by default. +!!! note "说明" + 如果你使用不带有任何媒体类型的响应类,FastAPI 认为你的响应没有任何内容,所以不会在生成的OpenAPI文档中记录响应格式。 -The parameter that defines this is `default_response_class`. +!!! info "提示" + 参数 `response_class` 也会用来定义响应的「媒体类型」。 -In the example below, **FastAPI** will use `ORJSONResponse` by default, in all *path operations*, instead of `JSONResponse`. +!!! tip "小贴士" + `ORJSONResponse` 目前只在 FastAPI 中可用,而在 Starlette 中不可用。 ```Python hl_lines="2 4" {!../../../docs_src/custom_response/tutorial010.py!} @@ -295,6 +303,6 @@ In the example below, **FastAPI** will use `ORJSONResponse` by default, in all * !!! tip You can still override `response_class` in *path operations* as before. -## Additional documentation +## 额外文档 -You can also declare the media type and many other details in OpenAPI using `responses`: [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}. +您还可以使用 `response` 在 OpenAPI 中声明媒体类型和许多其他详细信息:[OpenAPI 中的额外文档](additional-responses.md){.internal-link target=_blank}。 From 2c1aaf3050b2f9a66c17ecabf2dafbaacf70b917 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:40:42 +0800 Subject: [PATCH 106/163] New translations index.md (Chinese Simplified) --- docs/zh/docs/advanced/index.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/zh/docs/advanced/index.md b/docs/zh/docs/advanced/index.md index 467f0833e60a6..c042c1d32422c 100644 --- a/docs/zh/docs/advanced/index.md +++ b/docs/zh/docs/advanced/index.md @@ -1,21 +1,21 @@ -# Advanced User Guide +# 高级用户指南 -## Additional Features +## 额外特性 -The main [Tutorial - User Guide](../tutorial/){.internal-link target=_blank} should be enough to give you a tour through all the main features of **FastAPI**. +主要的教程 [教程 - 用户指南](../tutorial/){.internal-link target=_blank} 应该足以让你了解 **FastAPI** 的所有主要特性。 -In the next sections you will see other options, configurations, and additional features. +你会在接下来的章节中了解到其他的选项、配置以及额外的特性。 -!!! tip - The next sections are **not necessarily "advanced"**. +!!! !!! tip + 接下来的章节**并不一定是**「高级的」。 - And it's possible that for your use case, the solution is in one of them. + 而且对于你的使用场景来说,解决方案很可能就在其中。 -## Read the Tutorial first +## 先阅读教程 -You could still use most of the features in **FastAPI** with the knowledge from the main [Tutorial - User Guide](../tutorial/){.internal-link target=_blank}. +你可能仍会用到 **FastAPI** 主教程 [教程 - 用户指南](../tutorial/){.internal-link target=_blank} 中的大多数特性。 -And the next sections assume you already read it, and assume that you know those main ideas. +接下来的章节我们认为你已经读过 [教程 - 用户指南](../tutorial/){.internal-link target=_blank},并且假设你已经知晓其中主要思想。 ## TestDriven.io course From c49afbafb69f21899452240094bbf9b20894e908 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:40:45 +0800 Subject: [PATCH 107/163] New translations path-operation-advanced-configuration.md (Chinese Simplified) --- .../path-operation-advanced-configuration.md | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/zh/docs/advanced/path-operation-advanced-configuration.md b/docs/zh/docs/advanced/path-operation-advanced-configuration.md index f8bffadf4ffcf..b9cbd79f35f9d 100644 --- a/docs/zh/docs/advanced/path-operation-advanced-configuration.md +++ b/docs/zh/docs/advanced/path-operation-advanced-configuration.md @@ -1,11 +1,11 @@ -# Path Operation Advanced Configuration +# 路径操作的高级配置 -## OpenAPI operationId +## OpenAPI 的 operationId -!!! warning - If you are not an "expert" in OpenAPI, you probably don't need this. +!!! !!! warning + 如果你并非 OpenAPI 的「专家」,你可能不需要这部分内容。 -You can set the OpenAPI `operationId` to be used in your *path operation* with the parameter `operation_id`. +你可以在路径操作中通过参数 `operation_id` 设置要使用的 OpenAPI `operationId`。 You would have to make sure that it is unique for each operation. @@ -13,39 +13,39 @@ You would have to make sure that it is unique for each operation. {!../../../docs_src/path_operation_advanced_configuration/tutorial001.py!} ``` -### Using the *path operation function* name as the operationId +### 使用 *路径操作函数* 的函数名作为 operationId -If you want to use your APIs' function names as `operationId`s, you can iterate over all of them and override each *path operation's* `operation_id` using their `APIRoute.name`. +如果你想用你的 API 的函数名作为 `operationId` 的名字,你可以遍历一遍 API 的函数名,然后使用他们的 `APIRoute.name` 重写每个 *路径操作* 的 `operation_id`。 -You should do it after adding all your *path operations*. +你应该在添加了所有 *路径操作* 之后执行此操作。 ```Python hl_lines="2 12-21 24" {!../../../docs_src/path_operation_advanced_configuration/tutorial002.py!} ``` -!!! tip - If you manually call `app.openapi()`, you should update the `operationId`s before that. +!!! !!! tip + 如果你手动调用 `app.openapi()`,你应该在此之前更新 `operationId`。 -!!! warning - If you do this, you have to make sure each one of your *path operation functions* has a unique name. +!!! !!! warning + 如果你这样做,务必确保你的每个 *路径操作函数* 的名字唯一。 - Even if they are in different modules (Python files). + 即使它们在不同的模块中(Python 文件)。 -## Exclude from OpenAPI +## 从 OpenAPI 中排除 -To exclude a *path operation* from the generated OpenAPI schema (and thus, from the automatic documentation systems), use the parameter `include_in_schema` and set it to `False`: +使用参数 `include_in_schema` 并将其设置为 `False` ,来从生成的 OpenAPI 方案中排除一个 *路径操作*(这样一来,就从自动化文档系统中排除掉了)。 ```Python hl_lines="6" {!../../../docs_src/path_operation_advanced_configuration/tutorial003.py!} ``` -## Advanced description from docstring +## docstring 的高级描述 -You can limit the lines used from the docstring of a *path operation function* for OpenAPI. +你可以限制 *路径操作函数* 的 `docstring` 中用于 OpenAPI 的行数。 -Adding an `\f` (an escaped "form feed" character) causes **FastAPI** to truncate the output used for OpenAPI at this point. +添加一个 `\f` (一个「换页」的转义字符)可以使 **FastAPI** 在那一位置截断用于 OpenAPI 的输出。 -It won't show up in the documentation, but other tools (such as Sphinx) will be able to use the rest. +剩余部分不会出现在文档中,但是其他工具(比如 Sphinx)可以使用剩余部分。 ```Python hl_lines="19-29" {!../../../docs_src/path_operation_advanced_configuration/tutorial004.py!} From c4b9a67ac002e232216ff7ff9212c14b3ae7a707 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:40:46 +0800 Subject: [PATCH 108/163] New translations response-change-status-code.md (Chinese Simplified) --- .../advanced/response-change-status-code.md | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/zh/docs/advanced/response-change-status-code.md b/docs/zh/docs/advanced/response-change-status-code.md index 979cef3f05367..3bc41be37ad22 100644 --- a/docs/zh/docs/advanced/response-change-status-code.md +++ b/docs/zh/docs/advanced/response-change-status-code.md @@ -1,33 +1,33 @@ -# Response - Change Status Code +# 响应 - 更改状态码 -You probably read before that you can set a default [Response Status Code](../tutorial/response-status-code.md){.internal-link target=_blank}. +你可能之前已经了解到,你可以设置默认的[响应状态码](../tutorial/response-status-code.md){.internal-link target=_blank}。 -But in some cases you need to return a different status code than the default. +但在某些情况下,你需要返回一个不同于默认值的状态码。 -## Use case +## 使用场景 -For example, imagine that you want to return an HTTP status code of "OK" `200` by default. +例如,假设你想默认返回一个HTTP状态码为“OK”`200`。 -But if the data didn't exist, you want to create it, and return an HTTP status code of "CREATED" `201`. +但如果数据不存在,你想创建它,并返回一个HTTP状态码为“CREATED”`201`。 -But you still want to be able to filter and convert the data you return with a `response_model`. +但你仍然希望能够使用`response_model`过滤和转换你返回的数据。 -For those cases, you can use a `Response` parameter. +对于这些情况,你可以使用一个`Response`参数。 -## Use a `Response` parameter +## 使用 `Response` 参数 -You can declare a parameter of type `Response` in your *path operation function* (as you can do for cookies and headers). +你可以在你的*路径操作函数*中声明一个`Response`类型的参数(就像你可以为cookies和头部做的那样)。 -And then you can set the `status_code` in that *temporal* response object. +然后你可以在这个*临时*响应对象中设置`status_code`。 ```Python hl_lines="1 9 12" {!../../../docs_src/response_change_status_code/tutorial001.py!} ``` -And then you can return any object you need, as you normally would (a `dict`, a database model, etc). +然后你可以像平常一样返回任何你需要的对象(例如一个`dict`或者一个数据库模型)。 -And if you declared a `response_model`, it will still be used to filter and convert the object you returned. +如果你声明了一个`response_model`,它仍然会被用来过滤和转换你返回的对象。 -**FastAPI** will use that *temporal* response to extract the status code (also cookies and headers), and will put them in the final response that contains the value you returned, filtered by any `response_model`. +**FastAPI**将使用这个临时响应来提取状态码(也包括cookies和头部),并将它们放入包含你返回的值的最终响应中,该响应由任何`response_model`过滤。 -You can also declare the `Response` parameter in dependencies, and set the status code in them. But have in mind that the last one to be set will win. +你也可以在依赖项中声明`Response`参数,并在其中设置状态码。 但请注意,最后设置的状态码将会生效。 From c2adc5e25076dfa3b6e9178bdc89359d9b335e59 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:40:49 +0800 Subject: [PATCH 109/163] New translations response-cookies.md (Chinese Simplified) --- docs/zh/docs/advanced/response-cookies.md | 42 +++++++++++------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/zh/docs/advanced/response-cookies.md b/docs/zh/docs/advanced/response-cookies.md index 328094c9b5f7a..810119ea8a6eb 100644 --- a/docs/zh/docs/advanced/response-cookies.md +++ b/docs/zh/docs/advanced/response-cookies.md @@ -1,8 +1,8 @@ -# Response Cookies +# 响应Cookies -## Use a `Response` parameter +## 使用 `Response` 参数 -You can declare a parameter of type `Response` in your *path operation function*. +你可以在 *路径函数* 中定义一个类型为 `Response`的参数,这样你就可以在这个临时响应对象中设置cookie了。 And then you can set cookies in that *temporal* response object. @@ -10,40 +10,40 @@ And then you can set cookies in that *temporal* response object. {!../../../docs_src/response_cookies/tutorial002.py!} ``` -And then you can return any object you need, as you normally would (a `dict`, a database model, etc). +而且你还可以根据你的需要响应不同的对象,比如常用的 `dict`,数据库model等。 -And if you declared a `response_model`, it will still be used to filter and convert the object you returned. +如果你定义了 `response_model`,程序会自动根据`response_model`来过滤和转换你响应的对象。 -**FastAPI** will use that *temporal* response to extract the cookies (also headers and status code), and will put them in the final response that contains the value you returned, filtered by any `response_model`. +**FastAPI** 会使用这个 *临时* 响应对象去装在这些cookies信息 (同样还有headers和状态码等信息), 最终会将这些信息和通过`response_model`转化过的数据合并到最终的响应里。 -You can also declare the `Response` parameter in dependencies, and set cookies (and headers) in them. +你还可以在直接响应`Response`时直接创建cookies。 -## Return a `Response` directly +## 直接响应 `Response` -You can also create cookies when returning a `Response` directly in your code. +你也可以在depend中定义`Response`参数,并设置cookie和header。 -To do that, you can create a response as described in [Return a Response Directly](response-directly.md){.internal-link target=_blank}. +你可以参考[Return a Response Directly](response-directly.md){.internal-link target=_blank}来创建response -Then set Cookies in it, and then return it: +然后设置Cookies,并返回: ```Python hl_lines="10-12" {!../../../docs_src/response_cookies/tutorial001.py!} ``` -!!! tip - Have in mind that if you return a response directly instead of using the `Response` parameter, FastAPI will return it directly. +!!! !!! tip + 需要注意,如果你直接反馈一个response对象,而不是使用`Response`入参,FastAPI则会直接反馈你封装的response对象。 - So, you will have to make sure your data is of the correct type. E.g. it is compatible with JSON, if you are returning a `JSONResponse`. + So, you will have to make sure your data is of the correct type. E.g. 所以你需要确保你响应数据类型的正确性,如:你可以使用`JSONResponse`来兼容JSON的场景。 - And also that you are not sending any data that should have been filtered by a `response_model`. + 同时,你也应当仅反馈通过`response_model`过滤过的数据。 -### More info +### 更多信息 -!!! note "Technical Details" - You could also use `from starlette.responses import Response` or `from starlette.responses import JSONResponse`. +!!! !!! note "技术细节" + 你也可以使用`from starlette.responses import Response` 或者 `from starlette.responses import JSONResponse`。 - **FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. + 为了方便开发者,**FastAPI** 封装了相同数据类型,如`starlette.responses` 和 `fastapi.responses`。 不过大部分response对象都是直接引用自Starlette。 - And as the `Response` can be used frequently to set headers and cookies, **FastAPI** also provides it at `fastapi.Response`. + 因为`Response`对象可以非常便捷的设置headers和cookies,所以 **FastAPI** 同时也封装了`fastapi.Response`。 -To see all the available parameters and options, check the documentation in Starlette. +如果你想查看所有可用的参数和选项,可以参考 Starlette帮助文档 From e1ce77d720f5ae10dcd7d40aca19c98b37696c40 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:40:50 +0800 Subject: [PATCH 110/163] New translations response-directly.md (Chinese Simplified) --- docs/zh/docs/advanced/response-directly.md | 56 +++++++++++----------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/zh/docs/advanced/response-directly.md b/docs/zh/docs/advanced/response-directly.md index 8836140ecf7ee..bd26889d9f64d 100644 --- a/docs/zh/docs/advanced/response-directly.md +++ b/docs/zh/docs/advanced/response-directly.md @@ -1,63 +1,63 @@ -# Return a Response Directly +# 直接返回响应 -When you create a **FastAPI** *path operation* you can normally return any data from it: a `dict`, a `list`, a Pydantic model, a database model, etc. +当你创建一个 **FastAPI** *路径操作* 时,你可以正常返回以下任意一种数据:`dict`,`list`,Pydantic 模型,数据库模型等等。 -By default, **FastAPI** would automatically convert that return value to JSON using the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}. +**FastAPI** 默认会使用 `jsonable_encoder` 将这些类型的返回值转换成 JSON 格式,`jsonable_encoder` 在 [JSON 兼容编码器](../tutorial/encoder.md){.internal-link target=_blank} 中有阐述。 -Then, behind the scenes, it would put that JSON-compatible data (e.g. a `dict`) inside of a `JSONResponse` that would be used to send the response to the client. +然后,**FastAPI** 会在后台将这些兼容 JSON 的数据(比如字典)放到一个 `JSONResponse` 中,该 `JSONResponse` 会用来发送响应给客户端。 -But you can return a `JSONResponse` directly from your *path operations*. +但是你可以在你的 *路径操作* 中直接返回一个 `JSONResponse`。 -It might be useful, for example, to return custom headers or cookies. +直接返回响应可能会有用处,比如返回自定义的响应头和 cookies。 -## Return a `Response` +## 返回 `Response` -In fact, you can return any `Response` or any sub-class of it. +事实上,你可以返回任意 `Response` 或者任意 `Response` 的子类。 -!!! tip - `JSONResponse` itself is a sub-class of `Response`. +!!! !!! tip "小贴士" + `JSONResponse` 本身是一个 `Response` 的子类。 -And when you return a `Response`, **FastAPI** will pass it directly. +当你返回一个 `Response` 时,**FastAPI** 会直接传递它。 It won't do any data conversion with Pydantic models, it won't convert the contents to any type, etc. -This gives you a lot of flexibility. You can return any data type, override any data declaration or validation, etc. +这种特性给你极大的可扩展性。 你可以返回任何数据类型,重写任何数据声明或者校验,等等。 -## Using the `jsonable_encoder` in a `Response` +## 在 `Response` 中使用 `jsonable_encoder` -Because **FastAPI** doesn't do any change to a `Response` you return, you have to make sure it's contents are ready for it. +由于 **FastAPI** 并未对你返回的 `Response` 做任何改变,你必须确保你已经准备好响应内容。 -For example, you cannot put a Pydantic model in a `JSONResponse` without first converting it to a `dict` with all the data types (like `datetime`, `UUID`, etc) converted to JSON-compatible types. +例如,如果不首先将 Pydantic 模型转换为 `dict`,并将所有数据类型(如 `datetime`、`UUID` 等)转换为兼容 JSON 的类型,则不能将其放入JSONResponse中。 -For those cases, you can use the `jsonable_encoder` to convert your data before passing it to a response: +对于这些情况,在将数据传递给响应之前,你可以使用 `jsonable_encoder` 来转换你的数据。 ```Python hl_lines="6-7 21-22" {!../../../docs_src/response_directly/tutorial001.py!} ``` -!!! note "Technical Details" - You could also use `from starlette.responses import JSONResponse`. +!!! !!! note "技术细节" + 你也可以使用 `from starlette.responses import JSONResponse`。 - **FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. + 出于方便,**FastAPI** 会提供与 `starlette.responses` 相同的 `fastapi.responses` 给开发者。 但是大多数可用的响应都直接来自 Starlette。 -## Returning a custom `Response` +## 返回自定义 `Response` -The example above shows all the parts you need, but it's not very useful yet, as you could have just returned the `item` directly, and **FastAPI** would put it in a `JSONResponse` for you, converting it to a `dict`, etc. All that by default. +上面的例子展示了需要的所有部分,但还不够实用,因为你本可以只是直接返回 `item`,而**FastAPI** 默认帮你把这个 `item` 放到 `JSONResponse` 中,又默认将其转换成了 `dict`等等。 All that by default. -Now, let's see how you could use that to return a custom response. +现在,让我们看看你如何才能返回一个自定义的响应。 -Let's say that you want to return an XML response. +假设你想要返回一个 XML 响应。 -You could put your XML content in a string, put it in a `Response`, and return it: +你可以把你的 XML 内容放到一个字符串中,放到一个 `Response` 中,然后返回。 ```Python hl_lines="1 18" {!../../../docs_src/response_directly/tutorial002.py!} ``` -## Notes +## 说明 -When you return a `Response` directly its data is not validated, converted (serialized), nor documented automatically. +当你直接返回 `Response` 时,它的数据既没有校验,又不会进行转换(序列化),也不会自动生成文档。 -But you can still document it as described in [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}. +但是你仍可以参考 [OpenApI 中的额外响应](additional-responses.md){.internal-link target=_blank} 给响应编写文档。 -You can see in later sections how to use/declare these custom `Response`s while still having automatic data conversion, documentation, etc. +在后续的章节中你可以了解到如何使用/声明这些自定义的 `Response` 的同时还保留自动化的数据转换和文档等。 From 5575fd90f6d58c658092a0c72660904f053ba6bc Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:40:51 +0800 Subject: [PATCH 111/163] New translations response-headers.md (Chinese Simplified) --- docs/zh/docs/advanced/response-headers.md | 32 +++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/zh/docs/advanced/response-headers.md b/docs/zh/docs/advanced/response-headers.md index 99c280024f59d..6ff939767b495 100644 --- a/docs/zh/docs/advanced/response-headers.md +++ b/docs/zh/docs/advanced/response-headers.md @@ -1,42 +1,42 @@ -# Response Headers +# 响应头 -## Use a `Response` parameter +## 使用 `Response` 参数 -You can declare a parameter of type `Response` in your *path operation function* (as you can do for cookies). +你可以在你的*路径操作函数*中声明一个`Response`类型的参数(就像你可以为cookies做的那样)。 -And then you can set headers in that *temporal* response object. +然后你可以在这个*临时*响应对象中设置头部。 ```Python hl_lines="1 7-8" {!../../../docs_src/response_headers/tutorial002.py!} ``` -And then you can return any object you need, as you normally would (a `dict`, a database model, etc). +然后你可以像平常一样返回任何你需要的对象(例如一个`dict`或者一个数据库模型)。 -And if you declared a `response_model`, it will still be used to filter and convert the object you returned. +如果你声明了一个`response_model`,它仍然会被用来过滤和转换你返回的对象。 -**FastAPI** will use that *temporal* response to extract the headers (also cookies and status code), and will put them in the final response that contains the value you returned, filtered by any `response_model`. +**FastAPI**将使用这个临时响应来提取头部(也包括cookies和状态码),并将它们放入包含你返回的值的最终响应中,该响应由任何`response_model`过滤。 -You can also declare the `Response` parameter in dependencies, and set headers (and cookies) in them. +你也可以在直接返回`Response`时添加头部。 -## Return a `Response` directly +## 直接返回 `Response` You can also add headers when you return a `Response` directly. -Create a response as described in [Return a Response Directly](response-directly.md){.internal-link target=_blank} and pass the headers as an additional parameter: +按照[直接返回响应](response-directly.md){.internal-link target=_blank}中所述创建响应,并将头部作为附加参数传递: ```Python hl_lines="10-12" {!../../../docs_src/response_headers/tutorial001.py!} ``` -!!! note "Technical Details" - You could also use `from starlette.responses import Response` or `from starlette.responses import JSONResponse`. +!!! !!! 注意 "技术细节" + 你也可以使用`from starlette.responses import Response`或`from starlette.responses import JSONResponse`。 - **FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. + **FastAPI**提供了与`fastapi.responses`相同的`starlette.responses`,只是为了方便开发者。 但是,大多数可用的响应都直接来自Starlette。 - And as the `Response` can be used frequently to set headers and cookies, **FastAPI** also provides it at `fastapi.Response`. + 由于`Response`经常用于设置头部和cookies,因此**FastAPI**还在`fastapi.Response`中提供了它。 -## Custom Headers +## 自定义头部 Have in mind that custom proprietary headers can be added using the 'X-' prefix. -But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your CORS configurations (read more in [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}), using the parameter `expose_headers` documented in Starlette's CORS docs. +但是,如果你有自定义头部,你希望浏览器中的客户端能够看到它们,你需要将它们添加到你的CORS配置中(在[CORS(跨源资源共享)](../tutorial/cors.md){.internal-link target=_blank}中阅读更多),使用在Starlette的CORS文档中记录的`expose_headers`参数。 From 2aab3b1039ab31b67793bf6a94c17efded7f54a1 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:40:52 +0800 Subject: [PATCH 112/163] New translations index.md (Chinese Simplified) --- docs/zh/docs/advanced/security/index.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/zh/docs/advanced/security/index.md b/docs/zh/docs/advanced/security/index.md index c18baf64b0d27..4cae9655474f7 100644 --- a/docs/zh/docs/advanced/security/index.md +++ b/docs/zh/docs/advanced/security/index.md @@ -1,16 +1,16 @@ -# Advanced Security +# 高级安全 -## Additional Features +## 附加特性 -There are some extra features to handle security apart from the ones covered in the [Tutorial - User Guide: Security](../../tutorial/security/){.internal-link target=_blank}. +除 [教程 - 用户指南: 安全性](../../tutorial/security/){.internal-link target=_blank} 中涵盖的功能之外,还有一些额外的功能来处理安全性. -!!! tip - The next sections are **not necessarily "advanced"**. +!!! !!! tip "小贴士" + 接下来的章节 **并不一定是 "高级的"**. - And it's possible that for your use case, the solution is in one of them. + 而且对于你的使用场景来说,解决方案很可能就在其中。 -## Read the Tutorial first +## 先阅读教程 -The next sections assume you already read the main [Tutorial - User Guide: Security](../../tutorial/security/){.internal-link target=_blank}. +接下来的部分假设你已经阅读了主要的 [教程 - 用户指南: 安全性](../../tutorial/security/){.internal-link target=_blank}. -They are all based on the same concepts, but allow some extra functionalities. +它们都基于相同的概念,但支持一些额外的功能. From 99f54ec9adb27fc25ace18780f97f5e9df7e5f88 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:40:54 +0800 Subject: [PATCH 113/163] New translations settings.md (Chinese Simplified) --- docs/zh/docs/advanced/settings.md | 276 ++++++++++++++++-------------- 1 file changed, 148 insertions(+), 128 deletions(-) diff --git a/docs/zh/docs/advanced/settings.md b/docs/zh/docs/advanced/settings.md index a4a773b1fdeaf..d37bf11e79479 100644 --- a/docs/zh/docs/advanced/settings.md +++ b/docs/zh/docs/advanced/settings.md @@ -1,29 +1,29 @@ -# Settings and Environment Variables +# 设置和环境变量 -In many cases your application could need some external settings or configurations, for example secret keys, database credentials, credentials for email services, etc. +在许多情况下,您的应用程序可能需要一些外部设置或配置,例如密钥、数据库凭据、电子邮件服务的凭据等等。 -Most of these settings are variable (can change), like database URLs. And many could be sensitive, like secrets. +这些设置中的大多数是可变的(可以更改的),比如数据库的 URL。 而且许多设置可能是敏感的,比如密钥。 -For this reason it's common to provide them in environment variables that are read by the application. +因此,通常会将它们提供为由应用程序读取的环境变量。 -## Environment Variables +## 环境变量 -!!! tip - If you already know what "environment variables" are and how to use them, feel free to skip to the next section below. +!!! !!! tip + 如果您已经知道什么是"环境变量"以及如何使用它们,请随意跳到下面的下一节。 -An environment variable (also known as "env var") is a variable that lives outside of the Python code, in the operating system, and could be read by your Python code (or by other programs as well). +环境变量(也称为"env var")是一种存在于 Python 代码之外、存在于操作系统中的变量,可以被您的 Python 代码(或其他程序)读取。 -You can create and use environment variables in the shell, without needing Python: +您可以在 shell 中创建和使用环境变量,而无需使用 Python: -=== "Linux, macOS, Windows Bash" +=== "Linux、macOS、Windows Bash"
```console - // You could create an env var MY_NAME with + // 您可以创建一个名为 MY_NAME 的环境变量 $ export MY_NAME="Wade Wilson" - // Then you could use it with other programs, like + // 然后您可以与其他程序一起使用它,例如 $ echo "Hello $MY_NAME" Hello Wade Wilson @@ -37,10 +37,10 @@ You can create and use environment variables in the shell, without needing Pytho
```console - // Create an env var MY_NAME + // 创建一个名为 MY_NAME 的环境变量 $ $Env:MY_NAME = "Wade Wilson" - // Use it with other programs, like + // 与其他程序一起使用它,例如 $ echo "Hello $Env:MY_NAME" Hello Wade Wilson @@ -49,11 +49,11 @@ You can create and use environment variables in the shell, without needing Pytho
-### Read env vars in Python +### 在 Python 中读取环境变量 -You could also create environment variables outside of Python, in the terminal (or with any other method), and then read them in Python. +您还可以在 Python 之外的地方(例如终端中或使用任何其他方法)创建环境变量,然后在 Python 中读取它们。 -For example you could have a file `main.py` with: +例如,您可以有一个名为 `main.py` 的文件,其中包含以下内容: ```Python hl_lines="3" import os @@ -65,50 +65,50 @@ print(f"Hello {name} from Python") !!! tip The second argument to `os.getenv()` is the default value to return. - If not provided, it's `None` by default, here we provide `"World"` as the default value to use. + 如果没有提供默认值,默认为 `None`,此处我们提供了 `"World"` 作为要使用的默认值。 -Then you could call that Python program: +然后,您可以调用该 Python 程序:
```console -// Here we don't set the env var yet +// 这里我们还没有设置环境变量 $ python main.py -// As we didn't set the env var, we get the default value +// 因为我们没有设置环境变量,所以我们得到默认值 Hello World from Python -// But if we create an environment variable first +// 但是如果我们先创建一个环境变量 $ export MY_NAME="Wade Wilson" -// And then call the program again +// 然后再次调用程序 $ python main.py -// Now it can read the environment variable +// 现在它可以读取环境变量 Hello Wade Wilson from Python ```
-As environment variables can be set outside of the code, but can be read by the code, and don't have to be stored (committed to `git`) with the rest of the files, it's common to use them for configurations or settings. +由于环境变量可以在代码之外设置,但可以由代码读取,并且不需要与其他文件一起存储(提交到 `git`),因此通常将它们用于配置或设置。 -You can also create an environment variable only for a specific program invocation, that is only available to that program, and only for its duration. +您还可以仅为特定程序调用创建一个环境变量,该环境变量仅对该程序可用,并且仅在其运行期间有效。 -To do that, create it right before the program itself, on the same line: +要做到这一点,在程序本身之前的同一行创建它:
```console -// Create an env var MY_NAME in line for this program call +// 在此程序调用行中创建一个名为 MY_NAME 的环境变量 $ MY_NAME="Wade Wilson" python main.py -// Now it can read the environment variable +// 现在它可以读取环境变量 Hello Wade Wilson from Python -// The env var no longer exists afterwards +// 之后环境变量不再存在 $ python main.py Hello World from Python @@ -116,22 +116,23 @@ Hello World from Python
-!!! tip - You can read more about it at The Twelve-Factor App: Config. +!!! !!! tip + 您可以在 Twelve-Factor App: Config 中阅读更多相关信息。 -### Types and validation +### 类型和验证 -These environment variables can only handle text strings, as they are external to Python and have to be compatible with other programs and the rest of the system (and even with different operating systems, as Linux, Windows, macOS). +这些环境变量只能处理文本字符串,因为它们是外部于 Python 的,并且必须与其他程序和整个系统兼容(甚至与不同的操作系统,如 Linux、Windows、macOS)。 -That means that any value read in Python from an environment variable will be a `str`, and any conversion to a different type or validation has to be done in code. +这意味着从环境变量中在 Python 中读取的任何值都将是 `str` 类型,任何类型的转换或验证都必须在代码中完成。 -## Pydantic `Settings` +## Pydantic 的 `Settings` -Fortunately, Pydantic provides a great utility to handle these settings coming from environment variables with Pydantic: Settings management. +幸运的是,Pydantic 提供了一个很好的工具来处理来自环境变量的设置,即Pydantic: Settings management。 ### Install `pydantic-settings` -First, install the `pydantic-settings` package: +!!! tip + 要使其工作,您需要执行 `pip install python-dotenv`。
@@ -156,18 +157,19 @@ $ pip install "fastapi[all]" !!! info In Pydantic v1 it came included with the main package. Now it is distributed as this independent package so that you can choose to install it or not if you don't need that functionality. -### Create the `Settings` object +### 使用 `settings` -Import `BaseSettings` from Pydantic and create a sub-class, very much like with a Pydantic model. +从 Pydantic 导入 `BaseSettings` 并创建一个子类,与 Pydantic 模型非常相似。 -The same way as with Pydantic models, you declare class attributes with type annotations, and possibly default values. +与 Pydantic 模型一样,您使用类型注释声明类属性,还可以指定默认值。 -You can use all the same validation features and tools you use for Pydantic models, like different data types and additional validations with `Field()`. +您可以使用与 Pydantic 模型相同的验证功能和工具,比如不同的数据类型和使用 `Field()` 进行附加验证。 === "Pydantic v2" ```Python hl_lines="2 5-8 11" - {!> ../../../docs_src/settings/tutorial001.py!} + !!! tip + `os.getenv()` 的第二个参数是要返回的默认值。 ``` === "Pydantic v1" @@ -176,58 +178,61 @@ You can use all the same validation features and tools you use for Pydantic mode In Pydantic v1 you would import `BaseSettings` directly from `pydantic` instead of from `pydantic_settings`. ```Python hl_lines="2 5-8 11" - {!> ../../../docs_src/settings/tutorial001_pv1.py!} + !!! tip + 我们稍后会讨论 @lru_cache()。 ``` +。 + -!!! tip - If you want something quick to copy and paste, don't use this example, use the last one below. +!!! !!! tip + 如果您需要一个快速的复制粘贴示例,请不要使用此示例,而应使用下面的最后一个示例。 -Then, when you create an instance of that `Settings` class (in this case, in the `settings` object), Pydantic will read the environment variables in a case-insensitive way, so, an upper-case variable `APP_NAME` will still be read for the attribute `app_name`. +然后,当您创建该 `Settings` 类的实例(在此示例中是 `settings` 对象)时,Pydantic 将以不区分大小写的方式读取环境变量,因此,大写的变量 `APP_NAME` 仍将为属性 `app_name` 读取。 -Next it will convert and validate the data. So, when you use that `settings` object, you will have data of the types you declared (e.g. `items_per_user` will be an `int`). +然后,它将转换和验证数据。 因此,当您使用该 `settings` 对象时,您将获得您声明的类型的数据(例如 `items_per_user` 将为 `int` 类型)。 -### Use the `settings` +### 创建 `Settings` 对象 -Then you can use the new `settings` object in your application: +然后,您可以在应用程序中使用新的 `settings` 对象: ```Python hl_lines="18-20" {!../../../docs_src/settings/tutorial001.py!} ``` -### Run the server +### 运行服务器 -Next, you would run the server passing the configurations as environment variables, for example you could set an `ADMIN_EMAIL` and `APP_NAME` with: +接下来,您将运行服务器,并将配置作为环境变量传递。 例如,您可以设置一个 `ADMIN_EMAIL` 和 `APP_NAME`,如下所示:
```console -$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" uvicorn main:app +$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp"uvicorn main:app INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ```
-!!! tip - To set multiple env vars for a single command just separate them with a space, and put them all before the command. +!!! !!! tip + 要为单个命令设置多个环境变量,只需用空格分隔它们,并将它们全部放在命令之前。 -And then the `admin_email` setting would be set to `"deadpool@example.com"`. +然后,`admin_email` 设置将为 `"deadpool@example.com"`。 -The `app_name` would be `"ChimichangApp"`. +`app_name` 将为 `"ChimichangApp"`。 -And the `items_per_user` would keep its default value of `50`. +而 `items_per_user` 将保持其默认值为 `50`。 -## Settings in another module +## 在另一个模块中设置 -You could put those settings in another module file as you saw in [Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}. +您可以将这些设置放在另一个模块文件中,就像您在[Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}中所见的那样。 -For example, you could have a file `config.py` with: +根据前面的示例,您的 `config.py` 文件可能如下所示: ```Python {!../../../docs_src/settings/app01/config.py!} ``` -And then use it in a file `main.py`: +然后在一个名为 `main.py` 的文件中使用它: ```Python hl_lines="3 11-13" {!../../../docs_src/settings/app01/main.py!} @@ -236,25 +241,25 @@ And then use it in a file `main.py`: !!! tip You would also need a file `__init__.py` as you saw on [Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}. -## Settings in a dependency +## 在依赖项中使用设置 -In some occasions it might be useful to provide the settings from a dependency, instead of having a global object with `settings` that is used everywhere. +在某些情况下,从依赖项中提供设置可能比在所有地方都使用全局对象 `settings` 更有用。 -This could be especially useful during testing, as it's very easy to override a dependency with your own custom settings. +这在测试期间尤其有用,因为很容易用自定义设置覆盖依赖项。 -### The config file +### 配置文件 -Coming from the previous example, your `config.py` file could look like: +例如,您可以创建一个名为 `config.py` 的文件,其中包含以下内容: ```Python hl_lines="10" {!../../../docs_src/settings/app02/config.py!} ``` -Notice that now we don't create a default instance `settings = Settings()`. +请注意,现在我们不创建默认实例 `settings = Settings()`。 -### The main app file +### 主应用程序文件 -Now we create a dependency that returns a new `config.Settings()`. +现在我们创建一个依赖项,返回一个新的 `config.Settings()`。 === "Python 3.9+" @@ -268,21 +273,20 @@ Now we create a dependency that returns a new `config.Settings()`. {!> ../../../docs_src/settings/app02_an/main.py!} ``` -=== "Python 3.6+ non-Annotated" +=== "Python 3.6+ 非注解版本" - !!! tip - Prefer to use the `Annotated` version if possible. + !!! !!! tip + 如果可能,请尽量使用 `Annotated` 版本。 ```Python hl_lines="5 11-12" {!> ../../../docs_src/settings/app02/main.py!} ``` -!!! tip - We'll discuss the `@lru_cache()` in a bit. +!!! 但是,由于我们在顶部使用了 `@lru_cache()` 装饰器,因此只有在第一次调用它时,才会创建 `Settings` 对象一次。 - For now you can assume `get_settings()` is a normal function. + 目前,您可以将 `get_settings()` 视为普通函数。 -And then we can require it from the *path operation function* as a dependency and use it anywhere we need it. +然后,我们可以将其作为依赖项从“路径操作函数”中引入,并在需要时使用它。 === "Python 3.9+" @@ -296,60 +300,60 @@ And then we can require it from the *path operation function* as a dependency an {!> ../../../docs_src/settings/app02_an/main.py!} ``` -=== "Python 3.6+ non-Annotated" +=== "Python 3.6+ 非注解版本" - !!! tip - Prefer to use the `Annotated` version if possible. + !!! !!! tip + 如果可能,请尽量使用 `Annotated` 版本。 ```Python hl_lines="16 18-20" {!> ../../../docs_src/settings/app02/main.py!} ``` -### Settings and testing +### 设置和测试 -Then it would be very easy to provide a different settings object during testing by creating a dependency override for `get_settings`: +然后,在测试期间,通过创建 `get_settings` 的依赖项覆盖,很容易提供一个不同的设置对象: ```Python hl_lines="9-10 13 21" {!../../../docs_src/settings/app02/test_main.py!} ``` -In the dependency override we set a new value for the `admin_email` when creating the new `Settings` object, and then we return that new object. +在依赖项覆盖中,我们在创建新的 `Settings` 对象时为 `admin_email` 设置了一个新值,然后返回该新对象。 -Then we can test that it is used. +然后,我们可以测试它是否被使用。 -## Reading a `.env` file +## 从 `.env` 文件中读取设置 -If you have many settings that possibly change a lot, maybe in different environments, it might be useful to put them on a file and then read them from it as if they were environment variables. +如果您有许多可能经常更改的设置,可能在不同的环境中,将它们放在一个文件中,然后从该文件中读取它们,就像它们是环境变量一样,可能非常有用。 -This practice is common enough that it has a name, these environment variables are commonly placed in a file `.env`, and the file is called a "dotenv". +这种做法相当常见,有一个名称,这些环境变量通常放在一个名为 `.env` 的文件中,该文件被称为“dotenv”。 -!!! tip - A file starting with a dot (`.`) is a hidden file in Unix-like systems, like Linux and macOS. +!!! !!! tip + 以点 (`.`) 开头的文件是 Unix-like 系统(如 Linux 和 macOS)中的隐藏文件。 - But a dotenv file doesn't really have to have that exact filename. + 但是,dotenv 文件实际上不一定要具有确切的文件名。 -Pydantic has support for reading from these types of files using an external library. You can read more at Pydantic Settings: Dotenv (.env) support. +Pydantic 支持使用外部库从这些类型的文件中读取。 您可以在Pydantic 设置: Dotenv (.env) 支持中阅读更多相关信息。 !!! tip For this to work, you need to `pip install python-dotenv`. -### The `.env` file +### 从 `.env` 文件中读取设置 -You could have a `.env` file with: +您可以使用以下内容创建一个名为 `.env` 的文件: ```bash ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" ``` -### Read settings from `.env` +### `.env` 文件 -And then update your `config.py` with: +然后,您可以使用以下方式更新您的 `config.py`: === "Pydantic v2" ```Python hl_lines="9" - {!> ../../../docs_src/settings/app03_an/config.py!} + {!../../../docs_src/settings/app03/config.py!} ``` @@ -359,38 +363,41 @@ And then update your `config.py` with: === "Pydantic v1" ```Python hl_lines="9-10" - {!> ../../../docs_src/settings/app03_an/config_pv1.py!} + !!! tip + 您还需要一个名为 __init__.py 的文件,就像您在Bigger Applications - Multiple Files{.internal-link target=_blank}中看到的那样。 ``` + 的文件,就像您在[Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}中看到的那样。 + - - !!! tip - The `Config` class is used just for Pydantic configuration. You can read more at Pydantic Model Config. + !!! !!! tip + Config 类仅用于 Pydantic 配置。 您可以在Pydantic Model Config中阅读更多相关信息。 + !!! info In Pydantic version 1 the configuration was done in an internal class `Config`, in Pydantic version 2 it's done in an attribute `model_config`. This attribute takes a `dict`, and to get autocompletion and inline errors you can import and use `SettingsConfigDict` to define that `dict`. -Here we define the config `env_file` inside of your Pydantic `Settings` class, and set the value to the filename with the dotenv file we want to use. +在这里,我们在 Pydantic 的 `Settings` 类中创建了一个名为 `Config` 的类,并将 `env_file` 设置为我们想要使用的 dotenv 文件的文件名。 -### Creating the `Settings` only once with `lru_cache` +### 使用 `lru_cache` 仅创建一次 `Settings` -Reading a file from disk is normally a costly (slow) operation, so you probably want to do it only once and then re-use the same settings object, instead of reading it for each request. +从磁盘中读取文件通常是一项耗时的(慢)操作,因此您可能希望仅在首次读取后并重复使用相同的设置对象,而不是为每个请求都读取它。 -But every time we do: +但是,每次执行以下操作: ```Python Settings() ``` -a new `Settings` object would be created, and at creation it would read the `.env` file again. +都会创建一个新的 `Settings` 对象,并且在创建时会再次读取 `.env` 文件。 -If the dependency function was just like: +如果依赖项函数只是这样的: ```Python def get_settings(): return Settings() ``` -we would create that object for each request, and we would be reading the `.env` file for each request. ⚠️ +我们将为每个请求创建该对象,并且将在每个请求中读取 `.env` 文件。 ⚠️ But as we are using the `@lru_cache()` decorator on top, the `Settings` object will be created only once, the first time it's called. ✔️ @@ -406,24 +413,24 @@ But as we are using the `@lru_cache()` decorator on top, the `Settings` object w {!> ../../../docs_src/settings/app03_an/main.py!} ``` -=== "Python 3.6+ non-Annotated" +=== "Python 3.6+ 非注解版本" - !!! tip - Prefer to use the `Annotated` version if possible. + !!! !!! tip + 如果可能,请尽量使用 `Annotated` 版本。 ```Python hl_lines="1 10" {!> ../../../docs_src/settings/app03/main.py!} ``` -Then for any subsequent calls of `get_settings()` in the dependencies for the next requests, instead of executing the internal code of `get_settings()` and creating a new `Settings` object, it will return the same object that was returned on the first call, again and again. +然后,在下一次请求的依赖项中对 `get_settings()` 进行任何后续调用时,它不会执行 `get_settings()` 的内部代码并创建新的 `Settings` 对象,而是返回在第一次调用时返回的相同对象,一次又一次。 -#### `lru_cache` Technical Details +#### `lru_cache` 技术细节 -`@lru_cache()` modifies the function it decorates to return the same value that was returned the first time, instead of computing it again, executing the code of the function every time. +`@lru_cache()` 修改了它所装饰的函数,以返回第一次返回的相同值,而不是再次计算它,每次都执行函数的代码。 -So, the function below it will be executed once for each combination of arguments. And then the values returned by each of those combinations of arguments will be used again and again whenever the function is called with exactly the same combination of arguments. +因此,下面的函数将对每个参数组合执行一次。 然后,每个参数组合返回的值将在使用完全相同的参数组合调用函数时再次使用。 -For example, if you have a function: +例如,如果您有一个函数: ```Python @lru_cache() @@ -431,7 +438,7 @@ def say_hi(name: str, salutation: str = "Ms."): return f"Hello {salutation} {name}" ``` -your program could execute like this: +您的程序可以像这样执行: ```mermaid sequenceDiagram @@ -442,23 +449,36 @@ participant execute as Execute function rect rgba(0, 255, 0, .1) code ->> function: say_hi(name="Camila") - function ->> execute: execute function code - execute ->> code: return the result + function ->> execute: 执行函数代码 + execute ->> code: 返回结果 end rect rgba(0, 255, 255, .1) code ->> function: say_hi(name="Camila") - function ->> code: return stored result + function ->> code: 返回存储的结果 end rect rgba(0, 255, 0, .1) code ->> function: say_hi(name="Rick") - function ->> execute: execute function code - execute ->> code: return the result + function ->> execute: 执行函数代码 + execute ->> code: 返回结果 end rect rgba(0, 255, 0, .1) code ->> function: say_hi(name="Rick", salutation="Mr.") + function ->> execute: 执行函数代码 + execute ->> code: 返回结果 + end + + rect rgba(0, 255, 255, .1) + code ->> function: say_hi(name="Rick") + function ->> code: 返回存储的结果 + end + + rect rgba(0, 255, 255, .1) + code ->> function: say_hi(name="Camila") + function ->> code: 返回存储的结果 + end function ->> execute: execute function code execute ->> code: return the result end @@ -474,16 +494,16 @@ participant execute as Execute function end ``` -In the case of our dependency `get_settings()`, the function doesn't even take any arguments, so it always returns the same value. +对于我们的依赖项 `get_settings()`,该函数甚至不接受任何参数,因此它始终返回相同的值。 -That way, it behaves almost as if it was just a global variable. But as it uses a dependency function, then we can override it easily for testing. +这样,它的行为几乎就像是一个全局变量。 但是由于它使用了依赖项函数,因此我们可以轻松地进行测试时的覆盖。 -`@lru_cache()` is part of `functools` which is part of Python's standard library, you can read more about it in the Python docs for `@lru_cache()`. +`@lru_cache()` 是 `functools` 的一部分,它是 Python 标准库的一部分,您可以在Python 文档中了解有关 `@lru_cache()` 的更多信息。 ## Recap -You can use Pydantic Settings to handle the settings or configurations for your application, with all the power of Pydantic models. +您可以使用 Pydantic 设置处理应用程序的设置或配置,利用 Pydantic 模型的所有功能。 -* By using a dependency you can simplify testing. -* You can use `.env` files with it. -* Using `@lru_cache()` lets you avoid reading the dotenv file again and again for each request, while allowing you to override it during testing. +* 通过使用依赖项,您可以简化测试。 +* 您可以使用 `.env` 文件。 +* 使用 `@lru_cache()` 可以避免为每个请求重复读取 dotenv 文件,同时允许您在测试时进行覆盖。 From bcfcd5aeab13cbad142658a1e4392dc9d5e35239 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:40:58 +0800 Subject: [PATCH 114/163] New translations websockets.md (Chinese Simplified) --- docs/zh/docs/advanced/websockets.md | 140 +++++++++++++--------------- 1 file changed, 67 insertions(+), 73 deletions(-) diff --git a/docs/zh/docs/advanced/websockets.md b/docs/zh/docs/advanced/websockets.md index e1ae3bf84d699..e9665e48659c6 100644 --- a/docs/zh/docs/advanced/websockets.md +++ b/docs/zh/docs/advanced/websockets.md @@ -1,107 +1,103 @@ # WebSockets -You can use WebSockets with **FastAPI**. +您可以在 **FastAPI** 中使用 [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)。 -## Install `WebSockets` +## 安装 `WebSockets` -First you need to install `WebSockets`: +首先,您需要安装 `WebSockets`:
-```console $ pip install websockets ----> 100% -``` +---> 100%
-## WebSockets client +## WebSockets 客户端 -### In production +### 在生产环境中 -In your production system, you probably have a frontend created with a modern framework like React, Vue.js or Angular. +在您的生产系统中,您可能使用现代框架(如React、Vue.js或Angular)创建了一个前端。 -And to communicate using WebSockets with your backend you would probably use your frontend's utilities. +要使用 WebSockets 与后端进行通信,您可能会使用前端的工具。 -Or you might have a native mobile application that communicates with your WebSocket backend directly, in native code. +或者,您可能有一个原生移动应用程序,直接使用原生代码与 WebSocket 后端通信。 -Or you might have any other way to communicate with the WebSocket endpoint. +或者,您可能有其他与 WebSocket 终端通信的方式。 --- -But for this example, we'll use a very simple HTML document with some JavaScript, all inside a long string. +但是,在本示例中,我们将使用一个非常简单的HTML文档,其中包含一些JavaScript,全部放在一个长字符串中。 -This, of course, is not optimal and you wouldn't use it for production. +当然,这并不是最优的做法,您不应该在生产环境中使用它。 -In production you would have one of the options above. +在生产环境中,您应该选择上述任一选项。 -But it's the simplest way to focus on the server-side of WebSockets and have a working example: +但这是一种专注于 WebSockets 的服务器端并提供一个工作示例的最简单方式: ```Python hl_lines="2 6-38 41-43" {!../../../docs_src/websockets/tutorial001.py!} ``` -## Create a `websocket` +## 创建 `websocket` -In your **FastAPI** application, create a `websocket`: +在您的 **FastAPI** 应用程序中,创建一个 `websocket`: ```Python hl_lines="1 46-47" {!../../../docs_src/websockets/tutorial001.py!} ``` -!!! note "Technical Details" - You could also use `from starlette.websockets import WebSocket`. +!!! !!! note "技术细节" + 您也可以使用 `from starlette.websockets import WebSocket`。 - **FastAPI** provides the same `WebSocket` directly just as a convenience for you, the developer. But it comes directly from Starlette. + **FastAPI** 直接提供了相同的 `WebSocket`,只是为了方便开发人员。 但它直接来自 Starlette。 -## Await for messages and send messages +## 等待消息并发送消息 -In your WebSocket route you can `await` for messages and send messages. +在您的 WebSocket 路由中,您可以使用 `await` 等待消息并发送消息。 ```Python hl_lines="48-52" {!../../../docs_src/websockets/tutorial001.py!} ``` -You can receive and send binary, text, and JSON data. +您可以接收和发送二进制、文本和 JSON 数据。 -## Try it +## 尝试一下 -If your file is named `main.py`, run your application with: +如果您的文件名为 `main.py`,请使用以下命令运行应用程序:
-```console $ uvicorn main:app --reload -INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) -``` +<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
-Open your browser at http://127.0.0.1:8000. +在浏览器中打开 http://127.0.0.1:8000。 -You will see a simple page like: +您将看到一个简单的页面,如下所示: -You can type messages in the input box, and send them: +您可以在输入框中输入消息并发送: -And your **FastAPI** application with WebSockets will respond back: +您的 **FastAPI** 应用程序将回复: -You can send (and receive) many messages: +您可以发送(和接收)多条消息: -And all of them will use the same WebSocket connection. +所有这些消息都将使用同一个 WebSocket 连 -## Using `Depends` and others +## 使用 `Depends` 和其他依赖项 -In WebSocket endpoints you can import from `fastapi` and use: +在 WebSocket 端点中,您可以从 `fastapi` 导入并使用以下内容: * `Depends` * `Security` @@ -110,7 +106,7 @@ In WebSocket endpoints you can import from `fastapi` and use: * `Path` * `Query` -They work the same way as for other FastAPI endpoints/*path operations*: +它们的工作方式与其他 FastAPI 端点/ *路径操作* 相同: === "Python 3.10+" @@ -130,60 +126,58 @@ They work the same way as for other FastAPI endpoints/*path operations*: {!> ../../../docs_src/websockets/tutorial002_an.py!} ``` -=== "Python 3.10+ non-Annotated" +=== "Python 3.6+ 非带注解版本" - !!! tip - Prefer to use the `Annotated` version if possible. + !!! !!! tip + 如果可能,请尽量使用 `Annotated` 版本。 ```Python hl_lines="66-67 79" {!> ../../../docs_src/websockets/tutorial002_py310.py!} ``` -=== "Python 3.6+ non-Annotated" +=== "Python 3.10+ 非带注解版本" - !!! tip - Prefer to use the `Annotated` version if possible. + !!! !!! tip + 如果可能,请尽量使用 `Annotated` 版本。 ```Python hl_lines="68-69 81" {!> ../../../docs_src/websockets/tutorial002.py!} ``` -!!! info - As this is a WebSocket it doesn't really make sense to raise an `HTTPException`, instead we raise a `WebSocketException`. +!!! !!! info + 由于这是一个 WebSocket,抛出 `HTTPException` 并不是很合理,而是抛出 `WebSocketException`。 - You can use a closing code from the valid codes defined in the specification. + 您可以使用规范中定义的有效代码。 -### Try the WebSockets with dependencies +### 尝试带有依赖项的 WebSockets -If your file is named `main.py`, run your application with: +如果您的文件名为 `main.py`,请使用以下命令运行应用程序:
-```console $ uvicorn main:app --reload -INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) -``` +<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
-Open your browser at http://127.0.0.1:8000. +在浏览器中打开 http://127.0.0.1:8000。 -There you can set: +在页面中,您可以设置: -* The "Item ID", used in the path. -* The "Token" used as a query parameter. +* "Item ID",用于路径。 +* "Token",作为查询参数。 -!!! tip - Notice that the query `token` will be handled by a dependency. +!!! !!! tip + 注意,查询参数 `token` 将由依赖项处理。 -With that you can connect the WebSocket and then send and receive messages: +通过这样,您可以连接 WebSocket,然后发送和接收消息: -## Handling disconnections and multiple clients +## 处理断开连接和多个客户端 -When a WebSocket connection is closed, the `await websocket.receive_text()` will raise a `WebSocketDisconnect` exception, which you can then catch and handle like in this example. +当 WebSocket 连接关闭时,`await websocket.receive_text()` 将引发 `WebSocketDisconnect` 异常,您可以捕获并处理该异常,就像本示例中的示例一样。 === "Python 3.9+" @@ -197,28 +191,28 @@ When a WebSocket connection is closed, the `await websocket.receive_text()` will {!> ../../../docs_src/websockets/tutorial003.py!} ``` -To try it out: +尝试以下操作: -* Open the app with several browser tabs. +* 使用多个浏览器选项卡打开应用程序。 * Write messages from them. -* Then close one of the tabs. +* 然后关闭其中一个选项卡。 -That will raise the `WebSocketDisconnect` exception, and all the other clients will receive a message like: +这将引发 `WebSocketDisconnect` 异常,并且所有其他客户端都会收到类似以下的消息: ``` Client #1596980209979 left the chat ``` -!!! tip - The app above is a minimal and simple example to demonstrate how to handle and broadcast messages to several WebSocket connections. +!!! !!! tip + 上面的应用程序是一个最小和简单的示例,用于演示如何处理和向多个 WebSocket 连接广播消息。 - But have in mind that, as everything is handled in memory, in a single list, it will only work while the process is running, and will only work with a single process. + 但请记住,由于所有内容都在内存中以单个列表的形式处理,因此它只能在进程运行时工作,并且只能使用单个进程。 - If you need something easy to integrate with FastAPI but that is more robust, supported by Redis, PostgreSQL or others, check encode/broadcaster. + 如果您需要与 FastAPI 集成更简单但更强大的功能,支持 Redis、PostgreSQL 或其他功能,请查看 [encode/broadcaster](https://github.com/encode/broadcaster)。 -## More info +## 更多信息 -To learn more about the options, check Starlette's documentation for: +要了解更多选项,请查看 Starlette 的文档: -* The `WebSocket` class. -* Class-based WebSocket handling. +* WebSocket 类 +* [基于类的 WebSocket 处理](https://www.starlette.io/endpoints/#websocketendpoint)。 From 2ce642ec07c5d6e368f2c7423626464606598f9b Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:40:59 +0800 Subject: [PATCH 115/163] New translations wsgi.md (Chinese Simplified) --- docs/zh/docs/advanced/wsgi.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/zh/docs/advanced/wsgi.md b/docs/zh/docs/advanced/wsgi.md index cfe3c78c11ca4..e39b8eb3d26c9 100644 --- a/docs/zh/docs/advanced/wsgi.md +++ b/docs/zh/docs/advanced/wsgi.md @@ -1,34 +1,34 @@ -# Including WSGI - Flask, Django, others +# 包含 WSGI - Flask,Django,其它 -You can mount WSGI applications as you saw with [Sub Applications - Mounts](./sub-applications.md){.internal-link target=_blank}, [Behind a Proxy](./behind-a-proxy.md){.internal-link target=_blank}. +您可以挂载多个 WSGI 应用,正如您在 [Sub Applications - Mounts](./sub-applications.md){.internal-link target=_blank}, [Behind a Proxy](./behind-a-proxy.md){.internal-link target=_blank} 中所看到的那样。 -For that, you can use the `WSGIMiddleware` and use it to wrap your WSGI application, for example, Flask, Django, etc. +为此, 您可以使用 `WSGIMiddleware` 来包装你的 WSGI 应用,如:Flask,Django,等等。 -## Using `WSGIMiddleware` +## 使用 `WSGIMiddleware` -You need to import `WSGIMiddleware`. +您需要导入 `WSGIMiddleware`。 -Then wrap the WSGI (e.g. Flask) app with the middleware. +然后使用该中间件包装 WSGI 应用(例如 Flask)。 -And then mount that under a path. +之后将其挂载到某一个路径下。 ```Python hl_lines="2-3 23" {!../../../docs_src/wsgi/tutorial001.py!} ``` -## Check it +## 检查 -Now, every request under the path `/v1/` will be handled by the Flask application. +现在,所有定义在 `/v1/` 路径下的请求将会被 Flask 应用处理。 -And the rest will be handled by **FastAPI**. +其余的请求则会被 **FastAPI** 处理。 -If you run it with Uvicorn and go to http://localhost:8000/v1/ you will see the response from Flask: +如果您使用 Uvicorn 运行应用实例并且访问 http://localhost:8000/v1/,您将会看到由 Flask 返回的响应: ```txt Hello, World from Flask! ``` -And if you go to http://localhost:8000/v2 you will see the response from FastAPI: +并且如果您访问 http://localhost:8000/v2,您将会看到由 FastAPI 返回的响应: ```JSON { From 1e20d99e0eb9ec1a976af7a55b37d1062d08f9e5 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:41:01 +0800 Subject: [PATCH 116/163] New translations benchmarks.md (Chinese Simplified) --- docs/zh/docs/benchmarks.md | 42 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/zh/docs/benchmarks.md b/docs/zh/docs/benchmarks.md index e05fec8406621..44ff3a3870998 100644 --- a/docs/zh/docs/benchmarks.md +++ b/docs/zh/docs/benchmarks.md @@ -1,34 +1,34 @@ # Benchmarks -Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as one of the fastest Python frameworks available, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*) +第三方机构 TechEmpower 的基准测试表明在 Uvicorn 下运行的 **FastAPI** 应用程序是 可用的最快的 Python 框架之一,仅次于 Starlette 和 Uvicorn 本身 (由 FastAPI 内部使用)。 (*) -But when checking benchmarks and comparisons you should have the following in mind. +但是在查看基准得分和对比时,请注意以下几点。 -## Benchmarks and speed +## 基准测试和速度 -When you check the benchmarks, it is common to see several tools of different types compared as equivalent. +当你查看基准测试时,几个不同类型的工具被等效地做比较是很常见的情况。 -Specifically, to see Uvicorn, Starlette and FastAPI compared together (among many other tools). +具体来说,是将 Uvicorn,Starlette 和 FastAPI 一起比较(在许多其它工具中)。 -The simpler the problem solved by the tool, the better performance it will get. And most of the benchmarks don't test the additional features provided by the tool. +该工具解决的问题最简单,它将获得更好的性能。 而且大多数基准测试并未测试该工具提供的其他功能。 -The hierarchy is like: +层次结构如下: -* **Uvicorn**: an ASGI server - * **Starlette**: (uses Uvicorn) a web microframework - * **FastAPI**: (uses Starlette) an API microframework with several additional features for building APIs, with data validation, etc. +* **Uvicorn**:ASGI服务器 + * **Starlette**:(使用 Uvicorn)网络微框架 + * **FastAPI**:(使用 Starlette) 具有多个附加功能的API微框架,用于构建API,进行数据验证等。 * **Uvicorn**: - * Will have the best performance, as it doesn't have much extra code apart from the server itself. - * You wouldn't write an application in Uvicorn directly. That would mean that your code would have to include more or less, at least, all the code provided by Starlette (or **FastAPI**). And if you did that, your final application would have the same overhead as having used a framework and minimizing your app code and bugs. - * If you are comparing Uvicorn, compare it against Daphne, Hypercorn, uWSGI, etc. Application servers. + * 具有最佳性能,因为除了服务器本身外,它没有太多额外的代码。 + * 您不会直接在 Uvicorn 中编写应用程序。 这意味着您的代码至少必须包含 Starlette(或 **FastAPI**)提供的代码。 如果您这样做了(即直接在 Uvicorn 中编写应用程序),最终的应用程序会和使用了框架并且最小化了应用代码和 bug 的情况具有相同的性能损耗。 + * 如果要对比与 Uvicorn 对标的服务器,请将其与 Daphne,Hypercorn,uWSGI等应用服务器进行比较。 Application servers. * **Starlette**: - * Will have the next best performance, after Uvicorn. In fact, Starlette uses Uvicorn to run. So, it probably can only get "slower" than Uvicorn by having to execute more code. - * But it provides you the tools to build simple web applications, with routing based on paths, etc. - * If you are comparing Starlette, compare it against Sanic, Flask, Django, etc. Web frameworks (or microframeworks). + * 在 Uvicorn 后使用 Starlette,性能会略有下降。 实际上,Starlette 使用 Uvicorn运行。 因此,由于必须执行更多的代码,它只会比 Uvicorn 更慢。 + * 但它为您提供了构建简单的网络程序的工具,并具有基于路径的路由等功能。 + * 如果想对比与 Starlette 对标的开发框架,请将其与 Sanic,Flask,Django 等网络框架(或微框架)进行比较。 Web frameworks (or microframeworks). * **FastAPI**: - * The same way that Starlette uses Uvicorn and cannot be faster than it, **FastAPI** uses Starlette, so it cannot be faster than it. - * FastAPI provides more features on top of Starlette. Features that you almost always need when building APIs, like data validation and serialization. And by using it, you get automatic documentation for free (the automatic documentation doesn't even add overhead to running applications, it is generated on startup). - * If you didn't use FastAPI and used Starlette directly (or another tool, like Sanic, Flask, Responder, etc) you would have to implement all the data validation and serialization yourself. So, your final application would still have the same overhead as if it was built using FastAPI. And in many cases, this data validation and serialization is the biggest amount of code written in applications. - * So, by using FastAPI you are saving development time, bugs, lines of code, and you would probably get the same performance (or better) you would if you didn't use it (as you would have to implement it all in your code). - * If you are comparing FastAPI, compare it against a web application framework (or set of tools) that provides data validation, serialization and documentation, like Flask-apispec, NestJS, Molten, etc. Frameworks with integrated automatic data validation, serialization and documentation. + * 与 Starlette 使用 Uvicorn 一样,由于 **FastAPI** 使用 Starlette,因此 FastAPI 不能比 Starlette 更快。 + * FastAPI 在 Starlette 基础上提供了更多功能。 例如在开发 API 时,所需的数据验证和序列化功能。 FastAPI 可以帮助您自动生成 API文档,(文档在应用程序启动时自动生成,所以不会增加应用程序运行时的开销)。 + * 如果您不使用 FastAPI 而直接使用 Starlette(或诸如 Sanic,Flask,Responder 等其它工具),您则要自己实现所有的数据验证和序列化。 那么最终您的应用程序会和使用 FastAPI 构建的程序有相同的开销。 一般这种数据验证和序列化的操作在您应用程序的代码中会占很大比重。 + * 因此,通过使用 FastAPI 意味着您可以节省开发时间,减少编码错误,用更少的编码实现其功能,并且相比不使用 FastAPI 您很大可能会获得相同或更好的性能(因为那样您必须在代码中实现所有相同的功能)。 + * 如果您想对比与 FastAPI 对标的开发框架,请与能够提供数据验证,序列化和带有自动文档生成的网络应用程序框架(或工具集)进行对比,例如具有集成自动数据验证,序列化和自动化文档的 Flask-apispec,NestJS,Molten 等。 Frameworks with integrated automatic data validation, serialization and documentation. From 15cd6db371666de6b33ec19d3dc8327f5f2eacb0 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:41:03 +0800 Subject: [PATCH 117/163] New translations contributing.md (Chinese Simplified) --- docs/zh/docs/contributing.md | 215 +++++++++++++++++++---------------- 1 file changed, 114 insertions(+), 101 deletions(-) diff --git a/docs/zh/docs/contributing.md b/docs/zh/docs/contributing.md index f9276b28e9e49..46f349f032fab 100644 --- a/docs/zh/docs/contributing.md +++ b/docs/zh/docs/contributing.md @@ -1,14 +1,14 @@ -# Development - Contributing +# 开发 - 贡献 -First, you might want to see the basic ways to [help FastAPI and get help](help-fastapi.md){.internal-link target=_blank}. +首先,你最好先了解 [帮助 FastAPI 及获取帮助](help-fastapi.md){.internal-link target=_blank}的基本方式。 -## Developing +## 开发 -If you already cloned the repository and you know that you need to deep dive in the code, here are some guidelines to set up your environment. +如果你已经克隆了源码仓库,并且需要深入研究代码,下面是设置开发环境的指南。 -### Virtual environment with `venv` +### 通过 `venv` 管理虚拟环境 -You can create a virtual environment in a directory using Python's `venv` module: +你可以使用 Python 的 `venv` 模块在一个目录中创建虚拟环境:
@@ -18,11 +18,11 @@ $ python -m venv env
-That will create a directory `./env/` with the Python binaries and then you will be able to install packages for that isolated environment. +这将使用 Python 程序创建一个 `./env/` 目录,然后你将能够为这个隔离的环境安装软件包。 -### Activate the environment +### 激活虚拟环境 -Activate the new environment with: +使用以下方法激活新环境: === "Linux, macOS" @@ -59,7 +59,7 @@ Activate the new environment with:
-To check it worked, use: +要检查操作是否成功,运行: === "Linux, macOS, Windows Bash" @@ -87,7 +87,7 @@ To check it worked, use:
-If it shows the `pip` binary at `env/bin/pip` then it worked. 🎉 +如果显示 `pip` 程序文件位于 `env/bin/pip` 则说明激活成功。 🎉 Make sure you have the latest pip version on your virtual environment to avoid errors on the next steps: @@ -101,14 +101,14 @@ $ python -m pip install --upgrade pip
-!!! tip - Every time you install a new package with `pip` under that environment, activate the environment again. +!!! !!! tip + 每一次你在该环境下使用 `pip` 安装了新软件包时,请再次激活该环境。 - This makes sure that if you use a terminal program installed by that package, you use the one from your local environment and not any other that could be installed globally. + 这样可以确保你在使用由该软件包安装的终端程序时使用的是当前虚拟环境中的程序,而不是其他的可能是全局安装的程序。 ### pip -After activating the environment as described above: +如上所述激活环境后:
@@ -120,24 +120,24 @@ $ pip install -r requirements.txt
-It will install all the dependencies and your local FastAPI in your local environment. +这将在虚拟环境中安装所有依赖和本地版本的 FastAPI。 -#### Using your local FastAPI +#### 使用本地 FastAPI -If you create a Python file that imports and uses FastAPI, and run it with the Python from your local environment, it will use your local FastAPI source code. +并且如果你更改该本地 FastAPI 的源码,由于它是通过 `-e` 安装的,当你再次运行那个 Python 文件,它将使用你刚刚编辑过的最新版本的 FastAPI。 -And if you update that local FastAPI source code when you run that Python file again, it will use the fresh version of FastAPI you just edited. +如果你创建一个导入并使用 FastAPI 的 Python 文件,然后使用虚拟环境中的 Python 运行它,它将使用你本地的 FastAPI 源码。 -That way, you don't have to "install" your local version to be able to test every change. +这样,你不必再去重新"安装"你的本地版本即可测试所有更改。 !!! note "Technical Details" This only happens when you install using this included `requiements.txt` instead of installing `pip install fastapi` directly. That is because inside of the `requirements.txt` file, the local version of FastAPI is marked to be installed in "editable" mode, with the `-e` option. -### Format +### 格式化 -There is a script that you can run that will format and clean all your code: +你可以运行下面的脚本来格式化和清理所有代码:
@@ -147,42 +147,42 @@ $ bash scripts/format.sh
-It will also auto-sort all your imports. +它还会自动对所有导入代码进行整理。 -For it to sort them correctly, you need to have FastAPI installed locally in your environment, with the command in the section above using `-e`. +为了使整理正确进行,你需要在当前环境中安装本地的 FastAPI,即在运行上述段落中的命令时添加 `-e`。 -## Docs +## 文档 -First, make sure you set up your environment as described above, that will install all the requirements. +首先,请确保按上述步骤设置好环境,这将安装所有需要的依赖。 -The documentation uses MkDocs. +文档使用 MkDocs 生成。 -And there are extra tools/scripts in place to handle translations in `./scripts/docs.py`. +并且在 `./scripts/docs.py` 中还有适用的额外工具/脚本来处理翻译。 -!!! tip - You don't need to see the code in `./scripts/docs.py`, you just use it in the command line. +!!! !!! tip + 你不需要去了解 `./scripts/docs.py` 中的代码,只需在命令行中使用它即可。 -All the documentation is in Markdown format in the directory `./docs/en/`. +所有文档均在 `./docs/en/` 目录中以 Markdown 文件格式保存。 -Many of the tutorials have blocks of code. +许多的教程章节里包含有代码块。 -In most of the cases, these blocks of code are actual complete applications that can be run as is. +在大多数情况下,这些代码块是可以直接运行的真实完整的应用程序。 -In fact, those blocks of code are not written inside the Markdown, they are Python files in the `./docs_src/` directory. +实际上,这些代码块不是写在 Markdown 文件内的,它们是位于 `./docs_src/` 目录中的 Python 文件。 -And those Python files are included/injected in the documentation when generating the site. +生成站点时,这些 Python 文件会被包含/注入到文档中。 -### Docs for tests +### 用于测试的文档 -Most of the tests actually run against the example source files in the documentation. +大多数的测试实际上都是针对文档中的示例源文件运行的。 -This helps making sure that: +这有助于确保: * The documentation is up to date. -* The documentation examples can be run as is. -* Most of the features are covered by the documentation, ensured by test coverage. +* 文档示例可以直接运行。 +* 绝大多数特性既在文档中得以阐述,又通过测试覆盖进行保障。 -During local development, there is a script that builds the site and checks for any changes, live-reloading: +在本地开发期间,有一个脚本可以实时重载地构建站点并用来检查所做的任何更改:
@@ -196,9 +196,9 @@ $ python ./scripts/docs.py live
-It will serve the documentation on `http://127.0.0.1:8008`. +它将在 `http://127.0.0.1:8008` 提供对文档的访问。 -That way, you can edit the documentation/source files and see the changes live. +这样,你可以编辑文档/源文件并实时查看更改。 !!! tip Alternatively, you can perform the same steps that scripts does manually. @@ -206,7 +206,7 @@ That way, you can edit the documentation/source files and see the changes live. Go into the language directory, for the main docs in English it's at `docs/en/`: ```console - $ cd docs/en/ + docs/es/docs/mkdocs.yml ``` @@ -216,13 +216,13 @@ That way, you can edit the documentation/source files and see the changes live. $ mkdocs serve --dev-addr 8008 ``` -#### Typer CLI (optional) +#### Typer CLI (可选) -The instructions here show you how to use the script at `./scripts/docs.py` with the `python` program directly. +本指引向你展示了如何直接用 `python` 程序运行 `./scripts/docs.py` 中的脚本。 -But you can also use Typer CLI, and you will get autocompletion in your terminal for the commands after installing completion. +但你也可以使用 Typer CLI,而且在安装了补全功能后,你将可以在终端中对命令进行自动补全。 -If you install Typer CLI, you can install completion with: +如果你打算安装 Typer CLI ,可以使用以下命令安装自动补全功能:
@@ -232,12 +232,14 @@ $ typer --install-completion zsh completion installed in /home/user/.bashrc. Completion will take effect once you restart the terminal. ``` +Completion will take effect once you restart the terminal. +```
-### Apps and docs at the same time +### 应用和文档同时运行 -If you run the examples with, e.g.: +如果你使用以下方式运行示例程序:
@@ -249,49 +251,49 @@ $ uvicorn tutorial001:app --reload
-as Uvicorn by default will use the port `8000`, the documentation on port `8008` won't clash. +由于 Uvicorn 默认使用 `8000` 端口 ,因此运行在 `8008` 端口上的文档不会与之冲突。 -### Translations +### 翻译 -Help with translations is VERY MUCH appreciated! And it can't be done without the help from the community. 🌎 🚀 +非常感谢你能够参与文档的翻译! 这项工作需要社区的帮助才能完成。 🌎 🚀 -Here are the steps to help with translations. +以下是参与帮助翻译的步骤。 -#### Tips and guidelines +#### 建议和指南 -* Check the currently existing pull requests for your language and add reviews requesting changes or approving them. +* 在当前 已有的 pull requests 中查找你使用的语言,添加要求修改或同意合并的评审意见。 -!!! tip - You can add comments with change suggestions to existing pull requests. +!!! !!! tip + 你可以为已有的 pull requests 添加包含修改建议的评论。 - Check the docs about adding a pull request review to approve it or request changes. + 详情可查看关于 添加 pull request 评审意见 以同意合并或要求修改的文档。 * Check if there's a GitHub Discussion to coordinate translations for your language. You can subscribe to it, and when there's a new pull request to review, an automatic comment will be added to the discussion. -* Add a single pull request per page translated. That will make it much easier for others to review it. +* 每翻译一个页面新增一个 pull request。 这将使其他人更容易对其进行评审。 -For the languages I don't speak, I'll wait for several others to review the translation before merging. +对于我(译注:作者使用西班牙语和英语)不懂的语言,我将在等待其他人评审翻译之后将其合并。 -* You can also check if there are translations for your language and add a review to them, that will help me know that the translation is correct and I can merge it. - * You could check in the GitHub Discussions for your language. +* 你还可以查看是否有你所用语言的翻译,并对其进行评审,这将帮助我了解翻译是否正确以及能否将其合并。 + * 在 issues 中查找是否有对你所用语言所进行的协作翻译。 * Or you can filter the existing PRs by the ones with the label for your language, for example, for Spanish, the label is `lang-es`. -* Use the same Python examples and only translate the text in the docs. You don't have to change anything for this to work. +* 使用相同的 Python 示例并且仅翻译文档中的文本。 无需进行任何其他更改示例也能正常工作。 -* Use the same images, file names, and links. You don't have to change anything for it to work. +* 使用相同的图片、文件名以及链接地址。 无需进行任何其他调整来让它们兼容。 -* To check the 2-letter code for the language you want to translate you can use the table List of ISO 639-1 codes. +* 你可以从 ISO 639-1 代码列表 表中查找你想要翻译语言的两位字母代码。 -#### Existing language +#### 已有的语言 -Let's say you want to translate a page for a language that already has translations for some pages, like Spanish. +假设你想将某个页面翻译成已经翻译了一些页面的语言,例如西班牙语。 -In the case of Spanish, the 2-letter code is `es`. So, the directory for Spanish translations is located at `docs/es/`. +对于西班牙语来说,它的两位字母代码是 `es`。 所以西班牙语翻译的目录位于 `docs/es/`。 -!!! tip - The main ("official") language is English, located at `docs/en/`. +!!! !!! tip + 主要("官方")语言是英语,位于 `docs/en/`目录。 -Now run the live server for the docs in Spanish: +现在为西班牙语文档运行实时服务器:
@@ -312,7 +314,9 @@ $ python ./scripts/docs.py live es Go into the language directory, for the Spanish translations it's at `docs/es/`: ```console - $ cd docs/es/ + ```console +$ bash scripts/format-imports.sh +``` ``` @@ -322,40 +326,40 @@ $ python ./scripts/docs.py live es $ mkdocs serve --dev-addr 8008 ``` -Now you can go to http://127.0.0.1:8008 and see your changes live. +现在你可以访问 http://127.0.0.1:8008 实时查看你所做的更改。 -You will see that every language has all the pages. But some pages are not translated and have a notification about the missing translation. +如果你查看 FastAPI 的线上文档网站,会看到每种语言都有所有页面。 但是某些页面并未被翻译并且会有一处关于缺少翻译的提示。 -Now let's say that you want to add a translation for the section [Features](features.md){.internal-link target=_blank}. +现在假设你要为 [Features](features.md){.internal-link target=_blank} 章节添加翻译。 -* Copy the file at: +* 复制下面的文件: ``` docs/en/docs/features.md ``` -* Paste it in exactly the same location but for the language you want to translate, e.g.: +* 粘贴到你想要翻译语言目录的相同位置,比如: ``` docs/es/docs/features.md ``` -!!! tip - Notice that the only change in the path and file name is the language code, from `en` to `es`. +!!! !!! tip + 注意路径和文件名的唯一变化是语言代码,从 `en` 更改为 `es`。 -If you go to your browser you will see that now the docs show your new section. 🎉 +打开浏览器,现在你将看到文档展示了你所加入的新章节。 🎉 -Now you can translate it all and see how it looks as you save the file. +现在,你可以将它全部翻译完并在保存文件后进行预览。 -#### New Language +#### 新语言 -Let's say that you want to add translations for a language that is not yet translated, not even some pages. +假设你想要为尚未有任何页面被翻译的语言添加翻译。 -Let's say you want to add translations for Creole, and it's not yet there in the docs. +假设你想要添加克里奥尔语翻译,而且文档中还没有该语言的翻译。 -Checking the link from above, the code for "Creole" is `ht`. +点击上面提到的链接,可以查到"克里奥尔语"的代码为 `ht`。 -The next step is to run the script to generate a new translation directory: +下一步是运行脚本以生成新的翻译目录:
@@ -364,34 +368,36 @@ The next step is to run the script to generate a new translation directory: $ python ./scripts/docs.py new-lang ht Successfully initialized: docs/ht +Updating ht +Updating en ```
-Now you can check in your code editor the newly created directory `docs/ht/`. +现在,你可以在编辑器中查看新创建的目录 `docs/ht/`。 -That command created a file `docs/ht/mkdocs.yml` with a simple config that inherits everything from the `en` version: +这将在 `./docs_build/` 目录中为每一种语言生成全部的文档。 ```yaml -INHERIT: ../en/mkdocs.yml +docs/en/docs/mkdocs.yml ``` !!! tip You could also simply create that file with those contents manually. -That command also created a dummy file `docs/ht/index.md` for the main page, you can start by translating that one. +首先翻译文档主页 `docs/ht/index.md`。 You can continue with the previous instructions for an "Existing Language" for that process. -You can make the first pull request with those two files, `docs/ht/mkdocs.yml` and `docs/ht/index.md`. 🎉 +这样当你在翻译第一个页面时,其他人可以帮助翻译其他页面。 🚀 -#### Preview the result +#### 预览结果 -You can use the `./scripts/docs.py` with the `live` command to preview the results (or `mkdocs serve`). +当你通过 `live` 命令使用 `./scripts/docs.py` 中的脚本时,该脚本仅展示当前语言已有的文件和翻译。 -Once you are done, you can also test it all as it would look online, including all the other languages. +但是当你完成翻译后,你可以像在线上展示一样测试所有内容。 -To do that, first build all the docs: +为此,首先构建所有文档:
@@ -399,16 +405,19 @@ To do that, first build all the docs: // 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 ```
-This builds all those independent MkDocs sites for each language, combines them, and generates the final output at `./site/`. +然后,它针对每种语言构建独立的 MkDocs 站点,将它们组合在一起,并在 `./site/` 目录中生成最终的输出。 -Then you can serve that with the command `serve`: +然后你可以使用命令 `serve` 来运行生成的站点:
@@ -420,13 +429,17 @@ 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 +``` 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 ```
-## Tests +## 测试 -There is a script that you can run locally to test all the code and generate coverage reports in HTML: +你可以在本地运行下面的脚本来测试所有代码并生成 HTML 格式的覆盖率报告:
@@ -436,4 +449,4 @@ $ bash scripts/test-cov-html.sh
-This command generates a directory `./htmlcov/`, if you open the file `./htmlcov/index.html` in your browser, you can explore interactively the regions of code that are covered by the tests, and notice if there is any region missing. +该命令生成了一个 `./htmlcov/` 目录,如果你在浏览器中打开 `./htmlcov/index.html` 文件,你可以交互式地浏览被测试所覆盖的代码区块,并注意是否缺少了任何区块。 From 45635a47cdee1bc3c9fdda2c8fc725e2706dab73 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:41:08 +0800 Subject: [PATCH 118/163] New translations fastapi-people.md (Chinese Simplified) --- docs/zh/docs/fastapi-people.md | 82 +++++++++++++++++----------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/docs/zh/docs/fastapi-people.md b/docs/zh/docs/fastapi-people.md index 20caaa1ee8be1..2c1862773a014 100644 --- a/docs/zh/docs/fastapi-people.md +++ b/docs/zh/docs/fastapi-people.md @@ -1,12 +1,12 @@ -# FastAPI People +# FastAPI 社区 -FastAPI has an amazing community that welcomes people from all backgrounds. +FastAPI 有一个非常棒的社区,它欢迎来自各个领域和背景的朋友。 ## Creator - Maintainer -Hey! 👋 +向他们致以掌声。 👏 🙇 -This is me: +这就是我: {% if people %}
@@ -18,59 +18,59 @@ This is me:
{% endif %} -I'm the creator and maintainer of **FastAPI**. You can read more about that in [Help FastAPI - Get Help - Connect with the author](help-fastapi.md#connect-with-the-author){.internal-link target=_blank}. +我是 **FastAPI** 的创建者和维护者. 你能在 [帮助 FastAPI - 获取帮助 - 与作者联系](help-fastapi.md#connect-with-the-author){.internal-link target=_blank} 阅读有关此内容的更多信息。 You can read more about that in [Help FastAPI - Get Help - Connect with the author](help-fastapi.md#connect-with-the-author){.internal-link target=_blank}. -...But here I want to show you the community. +...但是在这里我想向您展示社区。 --- -**FastAPI** receives a lot of support from the community. And I want to highlight their contributions. +**FastAPI** 得到了社区的大力支持。 因此我想突出他们的贡献。 -These are the people that: +这些人: -* [Help others with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank}. -* [Create Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}. -* Review Pull Requests, [especially important for translations](contributing.md#translations){.internal-link target=_blank}. +* [帮助他人解决 GitHub 的 issues](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank}。 +* [创建 Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}。 +* 审核 Pull Requests, 对于 [翻译](contributing.md#translations){.internal-link target=_blank} 尤为重要。 -A round of applause to them. 👏 🙇 +他们贡献了源代码,文档,翻译等。 📦 -## Most active users last month +## 上个月最活跃的用户 -These are the users that have been [helping others the most with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank} during the last month. ☕ +还有很多其他贡献者(超过100个),你可以在 FastAPI GitHub 贡献者页面 中看到他们。 👷 {% if people %}
{% for user in people.last_month_active %} -
@{{ user.login }}
Questions replied: {{ user.count }}
+
@{{ user.login }}
Issues replied: {{ user.count }}
{% endfor %}
{% endif %} -## Experts +## 专家组 -Here are the **FastAPI Experts**. 🤓 +以下是 **FastAPI 专家**。 🤓 -These are the users that have [helped others the most with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank} through *all time*. +这些用户一直以来致力于 [帮助他人解决 GitHub 的 issues](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank}。 -They have proven to be experts by helping many others. ✨ +他们通过帮助许多人而被证明是专家。 ✨ {% if people %}
{% for user in people.experts %} -
@{{ user.login }}
Questions replied: {{ user.count }}
+
@{{ user.login }}
Issues replied: {{ user.count }}
{% endfor %}
{% endif %} -## Top Contributors +## 杰出贡献者 -Here are the **Top Contributors**. 👷 +以下是 **杰出的贡献者**。 👷 -These users have [created the most Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank} that have been *merged*. +这些用户 [创建了最多已被合并的 Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}。 They have contributed source code, documentation, translations, etc. 📦 @@ -84,19 +84,19 @@ They have contributed source code, documentation, translations, etc. 📦
{% endif %} -There are many other contributors (more than a hundred), you can see them all in the FastAPI GitHub Contributors page. 👷 +以下是 **赞助商** 。 😎 -## Top Reviewers +## 杰出审核者 -These users are the **Top Reviewers**. 🕵️ +以下用户是「杰出的评审者」。 🕵️ -### Reviews for Translations +### 翻译审核 -I only speak a few languages (and not very well 😅). So, the reviewers are the ones that have the [**power to approve translations**](contributing.md#translations){.internal-link target=_blank} of the documentation. Without them, there wouldn't be documentation in several other languages. +我只会说少数几种语言(而且还不是很流利 😅)。 所以,具备[能力去批准文档翻译](contributing.md#translations){.internal-link target=_blank} 是这些评审者们。 如果没有它们,就不会有多语言文档。 --- -The **Top Reviewers** 🕵️ have reviewed the most Pull Requests from others, ensuring the quality of the code, documentation, and especially, the **translations**. +**杰出的评审者** 🕵️ 评审了最多来自他人的 Pull Requests,他们保证了代码、文档尤其是 **翻译** 的质量。 {% if people %}
@@ -108,17 +108,17 @@ The **Top Reviewers** 🕵️ have reviewed the most Pull Requests from others,
{% endif %} -## Sponsors +## 赞助商 These are the **Sponsors**. 😎 -They are supporting my work with **FastAPI** (and others), mainly through GitHub Sponsors. +他们主要通过GitHub Sponsors支持我在 **FastAPI** (和其他项目)的工作。 {% if sponsors %} {% if sponsors.gold %} -### Gold Sponsors +### 金牌赞助商 {% for sponsor in sponsors.gold -%} @@ -127,7 +127,7 @@ They are supporting my work with **FastAPI** (and others), mainly through @@ -136,7 +136,7 @@ They are supporting my work with **FastAPI** (and others), mainly through @@ -145,7 +145,7 @@ They are supporting my work with **FastAPI** (and others), mainly through source code here. +该数据每月计算一次,您可以阅读 源代码。 -Here I'm also highlighting contributions from sponsors. +这里也强调了赞助商的贡献。 -I also reserve the right to update the algorithm, sections, thresholds, etc (just in case 🤷). +我也保留更新算法,栏目,统计阈值等的权利(以防万一🤷)。 From 3a92dbb71c2c55b0faec15ab395248097a53b514 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:41:09 +0800 Subject: [PATCH 119/163] New translations features.md (Chinese Simplified) --- docs/zh/docs/features.md | 194 +++++++++++++++++++-------------------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/docs/zh/docs/features.md b/docs/zh/docs/features.md index 98f37b5344580..b1a214a1224e6 100644 --- a/docs/zh/docs/features.md +++ b/docs/zh/docs/features.md @@ -1,35 +1,35 @@ -# Features +# 特性 -## FastAPI features +## FastAPI 特性 -**FastAPI** gives you the following: +**FastAPI** 提供了以下内容: -### Based on open standards +### 基于开放标准 -* OpenAPI for API creation, including declarations of path operations, parameters, body requests, security, etc. -* Automatic data model documentation with JSON Schema (as OpenAPI itself is based on JSON Schema). -* Designed around these standards, after a meticulous study. Instead of an afterthought layer on top. -* This also allows using automatic **client code generation** in many languages. +* 用于创建 API 的 OpenAPI 包含了路径操作,请求参数,请求体,安全性等的声明。 +* 使用 JSON Schema (因为 OpenAPI 本身就是基于 JSON Schema 的)自动生成数据模型文档。 +* 经过了缜密的研究后围绕这些标准而设计。 并非狗尾续貂。 +* 这也允许了在很多语言中自动**生成客户端代码**。 -### Automatic docs +### 自动生成文档 -Interactive API documentation and exploration web user interfaces. As the framework is based on OpenAPI, there are multiple options, 2 included by default. +交互式 API 文档以及具探索性 web 界面。 因为该框架是基于 OpenAPI,所以有很多可选项,FastAPI 默认自带两个交互式 API 文档。 -* Swagger UI, with interactive exploration, call and test your API directly from the browser. +* Swagger UI,可交互式操作,能在浏览器中直接调用和测试你的 API 。 ![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png) -* Alternative API documentation with ReDoc. +* 另外的 API 文档:ReDoc ![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png) -### Just Modern Python +### 更主流的 Python -It's all based on standard **Python 3.6 type** declarations (thanks to Pydantic). No new syntax to learn. Just standard modern Python. +全部都基于标准的 **Python 3.6 类型**声明(感谢 Pydantic )。 没有新的语法需要学习。 只需要标准的 Python 。 -If you need a 2 minute refresher of how to use Python types (even if you don't use FastAPI), check the short tutorial: [Python Types](python-types.md){.internal-link target=_blank}. +如果你需要2分钟来学习如何使用 Python 类型(即使你不使用 FastAPI ),看看这个简短的教程:[Python Types](python-types.md){.internal-link target=_blank}。 -You write standard Python with types: +编写带有类型标注的标准 Python: ```Python from datetime import date @@ -49,7 +49,7 @@ class User(BaseModel): joined: date ``` -That can then be used like: +可以像这样来使用: ```Python my_user: User = User(id=3, name="John Doe", joined="2018-07-19") @@ -63,136 +63,136 @@ second_user_data = { my_second_user: User = User(**second_user_data) ``` -!!! info - `**second_user_data` means: +!!! !!! info + `**second_user_data` 意思是: - Pass the keys and values of the `second_user_data` dict directly as key-value arguments, equivalent to: `User(id=4, name="Mary", joined="2018-11-30")` + 直接将`second_user_data`字典的键和值直接作为key-value参数传递,等同于:`User(id=4, name="Mary", joined="2018-11-30")` -### Editor support +### 编辑器支持 -All the framework was designed to be easy and intuitive to use, all the decisions were tested on multiple editors even before starting development, to ensure the best development experience. +整个框架都被设计得易于使用且直观,所有的决定都在开发之前就在多个编辑器上进行了测试,来确保最佳的开发体验。 -In the last Python developer survey it was clear that the most used feature is "autocompletion". +在最近的 Python 开发者调查中,我们能看到 被使用最多的功能是"自动补全"。 -The whole **FastAPI** framework is based to satisfy that. Autocompletion works everywhere. +整个 **FastAPI** 框架就是基于这一点的。 任何地方都可以进行自动补全。 -You will rarely need to come back to the docs. +你几乎不需要经常回来看文档。 -Here's how your editor might help you: +在这里,你的编辑器可能会这样帮助你: -* in Visual Studio Code: +* Visual Studio Code 中: ![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png) -* in PyCharm: +* PyCharm 中: ![editor support](https://fastapi.tiangolo.com/img/pycharm-completion.png) -You will get completion in code you might even consider impossible before. As for example, the `price` key inside a JSON body (that could have been nested) that comes from a request. +你将能进行代码补全,这是在之前你可能曾认为不可能的事。 例如,在来自请求 JSON 体(可能是嵌套的)中的键 `price`。 -No more typing the wrong key names, coming back and forth between docs, or scrolling up and down to find if you finally used `username` or `user_name`. +不会再输错键名,来回翻看文档,或者来回滚动寻找你最后使用的 `username` 或者 `user_name` 。 ### Short -It has sensible **defaults** for everything, with optional configurations everywhere. All the parameters can be fine-tuned to do what you need and to define the API you need. +任何类型都有合理的**默认值**,任何和地方都有可选配置。 所有的参数被微调,来满足你的需求,定义成你需要的 API。 -But by default, it all **"just works"**. +但是默认情况下,一切都能**“顺利工作”**。 -### Validation +### 验证 -* Validation for most (or all?) Python **data types**, including: - * JSON objects (`dict`). - * JSON array (`list`) defining item types. - * String (`str`) fields, defining min and max lengths. - * Numbers (`int`, `float`) with min and max values, etc. +* Validation for most (or all?) 校验大部分(甚至所有?)的 Python **数据类型**,包括: + * JSON 对象 (`dict`). + * JSON 数组 (`list`) 定义成员类型。 + * 字符串 (`str`) 字段, 定义最小或最大长度。 + * 数字 (`int`, `float`) 有最大值和最小值, 等等。 -* Validation for more exotic types, like: +* 校验外来类型, 比如: * URL. * Email. * UUID. - * ...and others. + * ...及其他. -All the validation is handled by the well-established and robust **Pydantic**. +所有的校验都由完善且强大的 **Pydantic** 处理。 -### Security and authentication +### 安全性及身份验证 -Security and authentication integrated. Without any compromise with databases or data models. +集成了安全性和身份认证。 杜绝数据库或者数据模型的渗透风险。 -All the security schemes defined in OpenAPI, including: +OpenAPI 中定义的安全模式,包括: -* HTTP Basic. -* **OAuth2** (also with **JWT tokens**). Check the tutorial on [OAuth2 with JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. -* API keys in: +* HTTP 基本认证。 +* **OAuth2** (也使用 **JWT tokens**)。 在 [OAuth2 with JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}查看教程。 +* API 密钥,在: * Headers. - * Query parameters. - * Cookies, etc. + * 查询参数。 + * Cookies, 等等。 -Plus all the security features from Starlette (including **session cookies**). +加上来自 Starlette(包括 **session cookie**)的所有安全特性。 -All built as reusable tools and components that are easy to integrate with your systems, data stores, relational and NoSQL databases, etc. +所有的这些都是可复用的工具和组件,可以轻松与你的系统,数据仓库,关系型以及 NoSQL 数据库等等集成。 -### Dependency Injection +### 依赖注入 -FastAPI includes an extremely easy to use, but extremely powerful Dependency Injection system. +FastAPI 有一个使用非常简单,但是非常强大的依赖注入系统。 -* Even dependencies can have dependencies, creating a hierarchy or **"graph" of dependencies**. -* All **automatically handled** by the framework. -* All the dependencies can require data from requests and **augment the path operation** constraints and automatic documentation. -* **Automatic validation** even for *path operation* parameters defined in dependencies. -* Support for complex user authentication systems, **database connections**, etc. -* **No compromise** with databases, frontends, etc. But easy integration with all of them. +* 甚至依赖也可以有依赖,创建一个层级或者**“图”依赖**。 +* 所有**自动化处理**都由框架完成。 +* 所有的依赖关系都可以从请求中获取数据,并且**增加了路径操作**约束和自动文档生成。 +* 即使在依赖项中被定义的*路径操作* 也会**自动验证**。 +* 支持复杂的用户身份认证系统,**数据库连接**等等。 +* **不依赖**数据库,前端等。 但是和它们集成很简单。 -### Unlimited "plug-ins" +### 无限制"插件" -Or in other way, no need for them, import and use the code you need. +或者说,导入并使用你需要的代码,而不需要它们。 -Any integration is designed to be so simple to use (with dependencies) that you can create a "plug-in" for your application in 2 lines of code using the same structure and syntax used for your *path operations*. +任何集成都被设计得被易于使用(用依赖关系),你可以用和*路径操作*相同的结构和语法,在两行代码中为你的应用创建一个“插件”。 -### Tested +### 测试 -* 100% test coverage. -* 100% type annotated code base. -* Used in production applications. +* 100% 测试覆盖。 +* 代码库100% 类型注释。 +* 用于生产应用。 -## Starlette features +## Starlette 特性 -**FastAPI** is fully compatible with (and based on) Starlette. So, any additional Starlette code you have, will also work. +**FastAPI** 和 Pydantic 完全兼容(并基于)。 所以,你有的其他的 Pydantic 代码也能正常工作。 -`FastAPI` is actually a sub-class of `Starlette`. So, if you already know or use Starlette, most of the functionality will work the same way. +`FastAPI` 实际上是 `Starlette`的一个子类。 所以,如果你已经知道或者使用 Starlette,大部分的功能会以相同的方式工作。 -With **FastAPI** you get all of **Starlette**'s features (as FastAPI is just Starlette on steroids): +通过 **FastAPI** 你可以获得所有 **Starlette** 的特性 ( FastAPI 就像加强版的 Starlette ): -* Seriously impressive performance. It is one of the fastest Python frameworks available, on par with **NodeJS** and **Go**. -* **WebSocket** support. -* In-process background tasks. -* Startup and shutdown events. -* Test client built on HTTPX. -* **CORS**, GZip, Static Files, Streaming responses. -* **Session and Cookie** support. -* 100% test coverage. -* 100% type annotated codebase. +* 令人惊叹的性能。 它是 Python 可用的最快的框架之一,和 **NodeJS** 及 **Go** 相当。 +* **支持 WebSocket** 。 +* 后台任务处理。 +* Startup 和 shutdown 事件。 +* 测试客户端基于 HTTPX。 +* **CORS**, GZip, 静态文件, 流响应。 +* 支持 **Session 和 Cookie** 。 +* 100% 测试覆盖率。 +* 代码库 100% 类型注释。 -## Pydantic features +## Pydantic 特性 -**FastAPI** is fully compatible with (and based on) Pydantic. So, any additional Pydantic code you have, will also work. +**FastAPI** 和 Starlette 完全兼容(并基于)。 So, any additional Pydantic code you have, will also work. -Including external libraries also based on Pydantic, as ORMs, ODMs for databases. +兼容包括基于 Pydantic 的外部库, 例如用与数据库的 ORMs, ODMs。 -This also means that in many cases you can pass the same object you get from a request **directly to the database**, as everything is validated automatically. +反之亦然,在很多情况下,你也可以将从数据库中获取的对象**直接传到客户端**。 -The same applies the other way around, in many cases you can just pass the object you get from the database **directly to the client**. +这也意味着在很多情况下,你可以将从请求中获得的相同对象**直接传到数据库**,因为所有的验证都是自动的。 -With **FastAPI** you get all of **Pydantic**'s features (as FastAPI is based on Pydantic for all the data handling): +通过 **FastAPI** 你可以获得所有 **Pydantic** (FastAPI 基于 Pydantic 做了所有的数据处理): -* **No brainfuck**: - * No new schema definition micro-language to learn. - * If you know Python types you know how to use Pydantic. -* Plays nicely with your **IDE/linter/brain**: - * Because pydantic data structures are just instances of classes you define; auto-completion, linting, mypy and your intuition should all work properly with your validated data. -* Validate **complex structures**: - * Use of hierarchical Pydantic models, Python `typing`’s `List` and `Dict`, etc. - * And validators allow complex data schemas to be clearly and easily defined, checked and documented as JSON Schema. - * You can have deeply **nested JSON** objects and have them all validated and annotated. -* **Extensible**: - * Pydantic allows custom data types to be defined or you can extend validation with methods on a model decorated with the validator decorator. -* 100% test coverage. +* **更简单**: + * 没有新的模式定义 micro-language 需要学习。 + * 如果你知道 Python types,你就知道如何使用 Pydantic。 +* 和你 **IDE/linter/brain** 适配: + * 因为 pydantic 数据结构仅仅是你定义的类的实例;自动补全,linting,mypy 以及你的直觉应该可以和你验证的数据一起正常工作。 +* 验证**复杂结构**: + * 使用分层的 Pydantic 模型, Python `typing`的 `List` 和 `Dict` 等等。 + * 验证器使我们能够简单清楚的将复杂的数据模式定义、检查并记录为 JSON Schema。 + * 你可以拥有深度**嵌套的 JSON** 对象并对它们进行验证和注释。 +* **可扩展**: + * Pydantic 允许定义自定义数据类型或者你可以用验证器装饰器对被装饰的模型上的方法扩展验证。 +* 100% 测试覆盖率。 From b0b958e378791bf8c4c7a09cd28a55830560fc37 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:41:10 +0800 Subject: [PATCH 120/163] New translations help-fastapi.md (Chinese Simplified) --- docs/zh/docs/help-fastapi.md | 157 +++++++++++++++++------------------ 1 file changed, 78 insertions(+), 79 deletions(-) diff --git a/docs/zh/docs/help-fastapi.md b/docs/zh/docs/help-fastapi.md index ee1c4d38fe334..36582e1226551 100644 --- a/docs/zh/docs/help-fastapi.md +++ b/docs/zh/docs/help-fastapi.md @@ -1,84 +1,84 @@ -# Help FastAPI - Get Help +# 帮助 FastAPI 与求助 -Do you like **FastAPI**? +您喜欢 **FastAPI** 吗? -Would you like to help FastAPI, other users, and the author? +想帮助 FastAPI? 其它用户? 还有项目作者? -Or would you like to get help with **FastAPI**? +或要求助怎么使用 **FastAPI**? -There are very simple ways to help (several involve just one or two clicks). +以下几种帮助的方式都非常简单(有些只需要点击一两下鼠标)。 And there are several ways to get help too. -## Subscribe to the newsletter +## 订阅新闻邮件 -You can subscribe to the (infrequent) [**FastAPI and friends** newsletter](/newsletter/){.internal-link target=_blank} to stay updated about: +您可以订阅 [**FastAPI 和它的小伙伴** 新闻邮件](/newsletter/){.internal-link target=_blank}(不会经常收到) -* News about FastAPI and friends 🚀 -* Guides 📝 -* Features ✨ -* Breaking changes 🚨 +* FastAPI 及其小伙伴的新闻 🚀 +* 指南 📝 +* 功能 ✨ +* 破坏性更改 🚨 * Tips and tricks ✅ -## Follow FastAPI on Twitter +## 在推特上关注 FastAPI -Follow @fastapi on **Twitter** to get the latest news about **FastAPI**. 🐦 +在 **Twitter** 上关注 @fastapi 获取 **FastAPI** 的最新消息。 🐦 -## Star **FastAPI** in GitHub +## 在 GitHub 上为 **FastAPI** 加星 -You can "star" FastAPI in GitHub (clicking the star button at the top right): https://github.com/tiangolo/fastapi. ⭐️ +您可以在 GitHub 上 **Star** FastAPI(只要点击右上角的星星就可以了): https://github.com/tiangolo/fastapi。 ⭐️ By adding a star, other users will be able to find it more easily and see that it has been already useful for others. -## Watch the GitHub repository for releases +## 关注 GitHub 资源库的版本发布 -You can "watch" FastAPI in GitHub (clicking the "watch" button at the top right): https://github.com/tiangolo/fastapi. 👀 +您可以查看现有 issues,并尝试帮助其他人解决问题,说不定您能解决这些问题呢。 🤓 There you can select "Releases only". -By doing it, you will receive notifications (in your email) whenever there's a new release (a new version) of **FastAPI** with bug fixes and new features. +这样,您就可以(在电子邮件里)接收到 **FastAPI** 新版发布的通知,及时了解 bug 修复与新功能。 -## Connect with the author +## 联系作者 -You can connect with me (Sebastián Ramírez / `tiangolo`), the author. +您可以联系项目作者,就是我(Sebastián Ramírez / `tiangolo`)。 -You can: +您可以: -* Follow me on **GitHub**. - * See other Open Source projects I have created that could help you. - * Follow me to see when I create a new Open Source project. +* 您可以在 GitHub 上「监听」FastAPI(点击右上角的 "watch" 按钮): https://github.com/tiangolo/fastapi. 👀 + * 了解其它我创建的开源项目,或许对您会有帮助 + * 关注我什么时候创建新的开源项目 * Follow me on **Twitter** or Mastodon. - * Tell me how you use FastAPI (I love to hear that). - * Hear when I make announcements or release new tools. - * You can also follow @fastapi on Twitter (a separate account). -* Connect with me on **Linkedin**. - * Hear when I make announcements or release new tools (although I use Twitter more often 🤷‍♂). -* Read what I write (or follow me) on **Dev.to** or **Medium**. - * Read other ideas, articles, and read about tools I have created. - * Follow me to read when I publish something new. + * 告诉我您使用 FastAPI(我非常乐意听到这种消息) + * 接收我发布公告或新工具的消息 + * 您还可以关注@fastapi on Twitter,这是个独立的账号 +* https://github.com/tiangolo/fastapi/edit/master/docs/en/data/external_links.yml + * 接收我发布公告或新工具的消息(虽然我用 Twitter 比较多) +* 阅读我在 **Dev.to****Medium** 上的文章,或关注我 + * 阅读我的其它想法、文章,了解我创建的工具 + * 关注我,这样就可以随时看到我发布的新文章 ## Tweet about **FastAPI** -Tweet about **FastAPI** and let me and others know why you like it. 🎉 +Tweet about **FastAPI** 让我和大家知道您为什么喜欢 FastAPI。 🎉 -I love to hear about how **FastAPI** is being used, what you have liked in it, in which project/company are you using it, etc. +知道有人使用 **FastAPI**,我会很开心,我也想知道您为什么喜欢 FastAPI,以及您在什么项目/哪些公司使用 FastAPI,等等。 -## Vote for FastAPI +## 为 FastAPI 投票 * Vote for **FastAPI** in Slant. * Vote for **FastAPI** in AlternativeTo. * Say you use **FastAPI** on StackShare. -## Help others with questions in GitHub +## 在 GitHub 上帮助其他人解决问题 You can try and help others with their questions in: * GitHub Discussions * GitHub Issues -In many cases you might already know the answer for those questions. 🤓 +如果帮助很多人解决了问题,您就有可能成为 [FastAPI 的官方专家](fastapi-people.md#experts){.internal-link target=_blank}。 🎉 -If you are helping a lot of people with their questions, you will become an official [FastAPI Expert](fastapi-people.md#experts){.internal-link target=_blank}. 🎉 +**注意**:如果您创建 Issue,我会要求您也要帮助别的用户。 😉 Just remember, the most important point is: try to be kind. People come with their frustrations and in many cases don't ask in the best way, but try as best as you can to be kind. 🤗 @@ -90,15 +90,15 @@ Here's how to help others with questions (in discussions or issues): ### Understand the question -* Check if you can understand what is the **purpose** and use case of the person asking. +* **Star** 以后,其它用户就能更容易找到 FastAPI,并了解到已经有其他用户在使用它了。 -* Then check if the question (the vast majority are questions) is **clear**. +* 如果您选择 "Watching" 而不是 "Releases only",有人创建新 Issue 时,您会接收到通知。 * In many cases the question asked is about an imaginary solution from the user, but there might be a **better** one. If you can understand the problem and use case better, you might be able to suggest a better **alternative solution**. -* If you can't understand the question, ask for more **details**. +* contributing.md#translations -### Reproduce the problem +### 创建 Issue For most of the cases and most of the questions there's something related to the person's **original code**. @@ -110,41 +110,41 @@ In many cases they will only copy a fragment of the code, but that's not enough ### Suggest solutions -* After being able to understand the question, you can give them a possible **answer**. +* 给我买杯咖啡 ☕️ 以示感谢 😄 * In many cases, it's better to understand their **underlying problem or use case**, because there might be a better way to solve it than what they are trying to do. ### Ask to close -If they reply, there's a high chance you would have solved their problem, congrats, **you're a hero**! 🦸 +另一方面,聊天室里有成千上万的用户,在这里,您有很大可能遇到聊得来的人。 😄 * Now, if that solved their problem, you can ask them to: - * In GitHub Discussions: mark the comment as the **answer**. - * In GitHub Issues: **close** the issue. + * 在 **Twitter** 上关注我 + * 在 **GitHub** 上关注我 -## Watch the GitHub repository +## 监听 GitHub 资源库 -You can "watch" FastAPI in GitHub (clicking the "watch" button at the top right): https://github.com/tiangolo/fastapi. 👀 +您还可以在 GitHub 上 **Watch** FastAPI,(点击右上角的 **Watch** 按钮)https://github.com/tiangolo/fastapi。 👀 If you select "Watching" instead of "Releases only" you will receive notifications when someone creates a new issue or question. You can also specify that you only want to be notified about new issues, or discussions, or PRs, etc. -Then you can try and help them solve those questions. +然后您就可以尝试并帮助他们解决问题。 ## Ask Questions -You can create a new question in the GitHub repository, for example to: +您可以在 GitHub 资源库中创建 Issue,例如: -* Ask a **question** or ask about a **problem**. -* Suggest a new **feature**. +* 提出**问题**或**意见** +* 提出新**特性**建议 -**Note**: if you do it, then I'm going to ask you to also help others. 😉 +当然您也可以成为 FastAPI 的金牌或银牌赞助商。 🏅🎉 ## Review Pull Requests You can help me review pull requests from others. -Again, please try your best to be kind. 🤗 +谢谢! 🚀 --- @@ -166,7 +166,7 @@ And if there's any other style or consistency need, I'll ask directly for that, ### Check the code -* Check and read the code, see if it makes sense, **run it locally** and see if it actually solves the problem. +* 您可以选择只关注发布(**Releases only**)。 * Then **comment** saying that you did that, that's how I will know you really checked it. @@ -181,7 +181,7 @@ And if there's any other style or consistency need, I'll ask directly for that, ### Tests -* Help me check that the PR has **tests**. +* 创建 PR * Check that the tests **fail** before the PR. 🚨 @@ -193,21 +193,21 @@ And if there's any other style or consistency need, I'll ask directly for that, ## Create a Pull Request -You can [contribute](contributing.md){.internal-link target=_blank} to the source code with Pull Requests, for example: +您可以创建 PR 为源代码做[贡献](contributing.md){.internal-link target=_blank},例如: * To fix a typo you found on the documentation. -* To share an article, video, or podcast you created or found about FastAPI by editing this file. - * Make sure you add your link to the start of the corresponding section. -* To help [translate the documentation](contributing.md#translations){.internal-link target=_blank} to your language. +* 编辑这个文件,分享 FastAPI 的文章、视频、博客,不论是您自己的,还是您看到的都成 + * 注意,添加的链接要放在对应区块的开头 +* [翻译文档](contributing.md#translations){.internal-link target=_blank} * You can also help to review the translations created by others. * To propose new documentation sections. -* To fix an existing issue/bug. +* 修复现有问题/Bug * Make sure to add tests. -* To add a new feature. +* 添加新功能 * Make sure to add tests. * Make sure to add documentation if it's relevant. -## Help Maintain FastAPI +## 在 AlternativeTo 上为 **FastAPI** 投票 Help me maintain **FastAPI**! 🤓 @@ -222,43 +222,42 @@ Those two tasks are what **consume time the most**. That's the main work of main If you can help me with that, **you are helping me maintain FastAPI** and making sure it keeps **advancing faster and better**. 🚀 -## Join the chat +## 加入聊天 -Join the 👥 Discord chat server 👥 and hang out with others in the FastAPI community. +快加入 👥 Discord 聊天服务器 👥 和 FastAPI 社区里的小伙伴一起哈皮吧。 -!!! tip - For questions, ask them in GitHub Discussions, there's a much better chance you will receive help by the [FastAPI Experts](fastapi-people.md#experts){.internal-link target=_blank}. +!!! 如有问题,请在 {\[--lt--]}a href="https://github.com/tiangolo/fastapi/issues/new/choose" class="external-link" target="_blank">GitHub Issues</a> 里提问,在这里更容易得到 [FastAPI 专家\](fastapi-people.md#experts){.internal-link target=_blank}的帮助。 Use the chat only for other general conversations. -There is also the previous Gitter chat, but as it doesn't have channels and advanced features, conversations are more difficult, so Discord is now the recommended system. +我们之前还使用过 Gitter chat,但它不支持频道等高级功能,聊天也比较麻烦,所以现在推荐使用 Discord。 -### Don't use the chat for questions +### 别在聊天室里提问 -Have in mind that as chats allow more "free conversation", it's easy to ask questions that are too general and more difficult to answer, so, you might not receive answers. +注意,聊天室更倾向于“闲聊”,经常有人会提出一些笼统得让人难以回答的问题,所以在这里提问一般没人回答。 -In GitHub, the template will guide you to write the right question so that you can more easily get a good answer, or even solve the problem yourself even before asking. And in GitHub I can make sure I always answer everything, even if it takes some time. I can't personally do that with the chat systems. 😅 +GitHub Issues 里提供了模板,指引您提出正确的问题,有利于获得优质的回答,甚至可能解决您还没有想到的问题。 而且就算答疑解惑要耗费不少时间,我还是会尽量在 GitHub 里回答问题。 但在聊天室里,我就没功夫这么做了。 😅 -Conversations in the chat systems are also not as easily searchable as in GitHub, so questions and answers might get lost in the conversation. And only the ones in GitHub count to become a [FastAPI Expert](fastapi-people.md#experts){.internal-link target=_blank}, so you will most probably receive more attention in GitHub. +聊天室里的聊天内容也不如 GitHub 里好搜索,聊天里的问答很容易就找不到了。 只有在 GitHub Issues 里的问答才能帮助您成为 [FastAPI 专家](fastapi-people.md#experts){.internal-link target=_blank},在 GitHub Issues 中为您带来更多关注。 On the other side, there are thousands of users in the chat systems, so there's a high chance you'll find someone to talk to there, almost all the time. 😄 -## Sponsor the author +## 赞助作者 -You can also financially support the author (me) through GitHub sponsors. +您还可以通过 GitHub 赞助商资助本项目的作者(就是我)。 There you could buy me a coffee ☕️ to say thanks. 😄 And you can also become a Silver or Gold sponsor for FastAPI. 🏅🎉 -## Sponsor the tools that power FastAPI +## 赞助 FastAPI 使用的工具 -As you have seen in the documentation, FastAPI stands on the shoulders of giants, Starlette and Pydantic. +如您在本文档中所见,FastAPI 站在巨人的肩膀上,它们分别是 Starlette 和 Pydantic。 -You can also sponsor: +您还可以赞助: -* Samuel Colvin (Pydantic) -* Encode (Starlette, Uvicorn) +* Samuel Colvin (Pydantic) +* Encode (Starlette, Uvicorn) --- From 166e854f12150f18101e9a920929fca50d451b0e Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:25 +0800 Subject: [PATCH 121/163] New translations bigger-applications.md (Chinese Simplified) --- docs/zh/docs/tutorial/bigger-applications.md | 391 ++++++++++--------- 1 file changed, 207 insertions(+), 184 deletions(-) diff --git a/docs/zh/docs/tutorial/bigger-applications.md b/docs/zh/docs/tutorial/bigger-applications.md index 0f990e3367f4f..0defc84810e53 100644 --- a/docs/zh/docs/tutorial/bigger-applications.md +++ b/docs/zh/docs/tutorial/bigger-applications.md @@ -1,15 +1,15 @@ -# Bigger Applications - Multiple Files +# 更大的应用 - 多个文件 -If you are building an application or a web API, it's rarely the case that you can put everything on a single file. +如果你正在开发一个应用程序或 Web API,很少会将所有的内容都放在一个文件中。 -**FastAPI** provides a convenience tool to structure your application while keeping all the flexibility. +**FastAPI** 提供了一个方便的工具,可以在保持所有灵活性的同时构建你的应用程序。 -!!! info - If you come from Flask, this would be the equivalent of Flask's Blueprints. +!!! !!! info + 如果你来自 Flask,那这将相当于 Flask 的 Blueprints。 -## An example file structure +## 一个文件结构示例 -Let's say you have a file structure like this: +假设你的文件结构如下: ``` . @@ -26,29 +26,29 @@ Let's say you have a file structure like this: │   └── admin.py ``` -!!! tip - There are several `__init__.py` files: one in each directory or subdirectory. +!!! !!! tip + 上面有几个 `__init__.py` 文件:每个目录或子目录中都有一个。 - This is what allows importing code from one file into another. + 这就是能将代码从一个文件导入到另一个文件的原因。 - For example, in `app/main.py` you could have a line like: + 例如,在 `app/main.py` 中,你可以有如下一行: ``` from app.routers import items ``` -* The `app` directory contains everything. And it has an empty file `app/__init__.py`, so it is a "Python package" (a collection of "Python modules"): `app`. -* It contains an `app/main.py` file. As it is inside a Python package (a directory with a file `__init__.py`), it is a "module" of that package: `app.main`. -* There's also an `app/dependencies.py` file, just like `app/main.py`, it is a "module": `app.dependencies`. -* There's a subdirectory `app/routers/` with another file `__init__.py`, so it's a "Python subpackage": `app.routers`. -* The file `app/routers/items.py` is inside a package, `app/routers/`, so, it's a submodule: `app.routers.items`. -* The same with `app/routers/users.py`, it's another submodule: `app.routers.users`. -* There's also a subdirectory `app/internal/` with another file `__init__.py`, so it's another "Python subpackage": `app.internal`. -* And the file `app/internal/admin.py` is another submodule: `app.internal.admin`. +* `app` 目录包含了所有内容。 并且它有一个空文件 `app/__init__.py`,因此它是一个「Python 包」(「Python 模块」的集合):`app`。 +* 它包含一个 `app/main.py` 文件。 由于它位于一个 Python 包(一个包含 `__init__.py` 文件的目录)中,因此它是该包的一个「模块」:`app.main`。 +* 还有一个 `app/dependencies.py` 文件,就像 `app/main.py` 一样,它是一个「模块」:`app.dependencies`。 +* 有一个子目录 `app/routers/` 包含另一个 `__init__.py` 文件,因此它是一个「Python 子包」:`app.routers`。 +* 文件 `app/routers/items.py` 位于 `app/routers/` 包中,因此它是一个子模块:`app.routers.items`。 +* 同样适用于 `app/routers/users.py`,它是另一个子模块:`app.routers.users`。 +* 还有一个子目录 `app/internal/` 包含另一个 `__init__.py` 文件,因此它是又一个「Python 子包」:`app.internal`。 +* `app/internal/admin.py` 是另一个子模块:`app.internal.admin`。 -The same file structure with comments: +带有注释的同一文件结构: ``` . @@ -67,61 +67,72 @@ The same file structure with comments: ## `APIRouter` -Let's say the file dedicated to handling just users is the submodule at `/app/routers/users.py`. +假设专门用于处理用户逻辑的文件是位于 `/app/routers/users.py` 的子模块。 -You want to have the *path operations* related to your users separated from the rest of the code, to keep it organized. +你希望将与用户相关的*路径操作*与其他代码分开,以使其井井有条。 -But it's still part of the same **FastAPI** application/web API (it's part of the same "Python Package"). +但它仍然是同一 **FastAPI** 应用程序/web API 的一部分(它是同一「Python 包」的一部分)。 -You can create the *path operations* for that module using `APIRouter`. +你可以使用 `APIRouter` 为该模块创建*路径操作*。 -### Import `APIRouter` +### 导入 `APIRouter` -You import it and create an "instance" the same way you would with the class `FastAPI`: +你可以导入它并通过与 `FastAPI` 类相同的方式创建一个「实例」: ```Python hl_lines="1 3" {!../../../docs_src/bigger_applications/app/routers/users.py!} ``` -### *Path operations* with `APIRouter` +### 使用 `APIRouter` 的*路径操作* -And then you use it to declare your *path operations*. +然后你可以使用它来声明*路径操作*。 -Use it the same way you would use the `FastAPI` class: +使用方式与 `FastAPI` 类相同: ```Python hl_lines="6 11 16" {!../../../docs_src/bigger_applications/app/routers/users.py!} ``` -You can think of `APIRouter` as a "mini `FastAPI`" class. +你可以将 `APIRouter` 视为一个「迷你 `FastAPI`」类。 -All the same options are supported. +所有相同的选项都得到支持。 -All the same `parameters`, `responses`, `dependencies`, `tags`, etc. +所有相同的 `parameters`、`responses`、`dependencies`、`tags` 等等。 -!!! tip - In this example, the variable is called `router`, but you can name it however you want. +!!! !!! tip + 在此示例中,该变量被命名为 `router`,但你可以根据你的想法自由命名。 -We are going to include this `APIRouter` in the main `FastAPI` app, but first, let's check the dependencies and another `APIRouter`. +我们将在主 `FastAPI` 应用中包含该 `APIRouter`,但首先,让我们来看看依赖项和另一个 `APIRouter`。 -## Dependencies +## 依赖项 -We see that we are going to need some dependencies used in several places of the application. +我们了解到我们将需要一些在应用程序的好几个地方所使用的依赖项。 -So we put them in their own `dependencies` module (`app/dependencies.py`). +因此,我们将它们放在它们自己的 `dependencies` 模块(`app/dependencies.py`)中。 -We will now use a simple dependency to read a custom `X-Token` header: +现在我们将使用一个简单的依赖项来读取一个自定义的 `X-Token` 请求首部: === "Python 3.9+" ```Python hl_lines="3 6-8" - {!> ../../../docs_src/bigger_applications/app_an_py39/dependencies.py!} + . +├── app +│   ├── __init__.py +│   ├── main.py +│   ├── dependencies.py +│   └── routers +│   │ ├── __init__.py +│   │ ├── items.py +│   │ └── users.py +│   └── internal +│   ├── __init__.py +│   └── admin.py ``` === "Python 3.6+" ```Python hl_lines="1 5-7" - {!> ../../../docs_src/bigger_applications/app_an/dependencies.py!} + {!../../../docs_src/bigger_applications/app/dependencies.py!} ``` === "Python 3.6+ non-Annotated" @@ -130,41 +141,53 @@ We will now use a simple dependency to read a custom `X-Token` header: Prefer to use the `Annotated` version if possible. ```Python hl_lines="1 4-6" - {!> ../../../docs_src/bigger_applications/app/dependencies.py!} + . +├── app # 「app」是一个 Python 包 +│   ├── __init__.py # 这个文件使「app」成为一个 Python 包 +│   ├── main.py # 「main」模块,例如 import app.main +│   ├── dependencies.py # 「dependencies」模块,例如 import app.dependencies +│   └── routers # 「routers」是一个「Python 子包」 +│   │ ├── __init__.py # 使「routers」成为一个「Python 子包」 +│   │ ├── items.py # 「items」子模块,例如 import app.routers.items +│   │ └── users.py # 「users」子模块,例如 import app.routers.users +│   └── internal # 「internal」是一个「Python 子包」 +│   ├── __init__.py # 使「internal」成为一个「Python 子包」 +│   └── admin.py # 「admin」子模块,例如 import app.internal.admin ``` !!! tip We are using an invented header to simplify this example. - But in real cases you will get better results using the integrated [Security utilities](./security/index.md){.internal-link target=_blank}. + 但在实际情况下,使用集成的[安全性实用工具](./security/index.md){.internal-link target=_blank}会得到更好的效果。 -## Another module with `APIRouter` +## 其他使用 `APIRouter` 的模块 -Let's say you also have the endpoints dedicated to handling "items" from your application in the module at `app/routers/items.py`. +假设你在位于 `app/routers/items.py` 的模块中还有专门用于处理应用程序中「项目」的端点。 -You have *path operations* for: +你具有以下*路径操作*: * `/items/` * `/items/{item_id}` -It's all the same structure as with `app/routers/users.py`. +这和 `app/routers/users.py` 的结构完全相同。 -But we want to be smarter and simplify the code a bit. +但是我们想变得更聪明并简化一些代码。 -We know all the *path operations* in this module have the same: +我们知道此模块中的所有*路径操作*都有相同的: -* Path `prefix`: `/items`. -* `tags`: (just one tag: `items`). -* Extra `responses`. -* `dependencies`: they all need that `X-Token` dependency we created. +* 路径 `prefix`:`/items`。 +* `tags`:(仅有一个 `items` 标签)。 +* 额外的 `responses`。 +* `dependencies`:它们都需要我们创建的 `X-Token` 依赖项。 -So, instead of adding all that to each *path operation*, we can add it to the `APIRouter`. +!!! note "技术细节" + 实际上,它将在内部为声明在 `APIRouter` 中的每个*路径操作*创建一个*路径操作*。 ```Python hl_lines="5-10 16 21" {!../../../docs_src/bigger_applications/app/routers/items.py!} ``` -As the path of each *path operation* has to start with `/`, like in: +由于每个*路径操作*的路径都必须以 `/` 开头,例如: ```Python hl_lines="1" @router.get("/{item_id}") @@ -172,93 +195,93 @@ async def read_item(item_id: str): ... ``` -...the prefix must not include a final `/`. +...前缀不能以 `/` 作为结尾。 -So, the prefix in this case is `/items`. +因此,本例中的前缀为 `/items`。 -We can also add a list of `tags` and extra `responses` that will be applied to all the *path operations* included in this router. +我们还可以添加一个 `tags` 列表和额外的 `responses` 列表,这些参数将应用于此路由器中包含的所有*路径操作*。 -And we can add a list of `dependencies` that will be added to all the *path operations* in the router and will be executed/solved for each request made to them. +我们可以添加一个 `dependencies` 列表,这些依赖项将被添加到路由器中的所有*路径操作*中,并将针对向它们发起的每个请求执行/解决。 -!!! tip - Note that, much like [dependencies in *path operation decorators*](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, no value will be passed to your *path operation function*. +!!! !!! tip + 请注意,和[*路径操作装饰器*中的依赖项](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}很类似,没有值会被传递给你的*路径操作函数*。 -The end result is that the item paths are now: +最终结果是项目相关的路径现在为: * `/items/` * `/items/{item_id}` -...as we intended. +...如我们所愿。 -* They will be marked with a list of tags that contain a single string `"items"`. - * These "tags" are especially useful for the automatic interactive documentation systems (using OpenAPI). -* All of them will include the predefined `responses`. -* All these *path operations* will have the list of `dependencies` evaluated/executed before them. - * If you also declare dependencies in a specific *path operation*, **they will be executed too**. - * The router dependencies are executed first, then the [`dependencies` in the decorator](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, and then the normal parameter dependencies. - * You can also add [`Security` dependencies with `scopes`](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}. +* 它们将被标记为仅包含单个字符串 `"items"` 的标签列表。 + * 这些「标签」对于自动化交互式文档系统(使用 OpenAPI)特别有用。 +* 所有的路径操作都将包含预定义的 `responses`。 +* 所有的这些*路径操作*都将在自身之前计算/执行 `dependencies` 列表。 + * 如果你还在一个具体的*路径操作*中声明了依赖项,**它们也会被执行**。 + * 路由器的依赖项最先执行,然后是[装饰器中的 `dependencies`](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank},再然后是普通的参数依赖项。 + * 你还可以添加[具有 `scopes` 的 `Security` 依赖项](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}。 -!!! tip - Having `dependencies` in the `APIRouter` can be used, for example, to require authentication for a whole group of *path operations*. Even if the dependencies are not added individually to each one of them. +!!! !!! tip + 在 `APIRouter`中具有 `dependencies` 可以用来,例如,对一整组的*路径操作*要求身份认证。 即使这些依赖项并没有分别添加到每个路径操作中。 -!!! check - The `prefix`, `tags`, `responses`, and `dependencies` parameters are (as in many other cases) just a feature from **FastAPI** to help you avoid code duplication. +!!! !!! check + `prefix`、`tags`、`responses` 以及 `dependencies` 参数只是(和其他很多情况一样)**FastAPI** 的一个用于帮助你避免代码重复的功能。 -### Import the dependencies +### 导入依赖项 -This code lives in the module `app.routers.items`, the file `app/routers/items.py`. +这些代码位于 `app.routers.items` 模块,`app/routers/items.py` 文件中。 -And we need to get the dependency function from the module `app.dependencies`, the file `app/dependencies.py`. +我们需要从 `app.dependencies` 模块即 `app/dependencies.py` 文件中获取依赖函数。 -So we use a relative import with `..` for the dependencies: +因此,我们通过 `..` 对依赖项使用了相对导入: ```Python hl_lines="3" {!../../../docs_src/bigger_applications/app/routers/items.py!} ``` -#### How relative imports work +#### 相对导入如何工作 -!!! tip - If you know perfectly how imports work, continue to the next section below. +!!! !!! tip + 如果你完全了解导入的工作原理,请从下面的下一部分继续。 -A single dot `.`, like in: +一个单点 `.`,例如: ```Python from .dependencies import get_token_header ``` -would mean: +表示: -* Starting in the same package that this module (the file `app/routers/items.py`) lives in (the directory `app/routers/`)... -* find the module `dependencies` (an imaginary file at `app/routers/dependencies.py`)... -* and from it, import the function `get_token_header`. +* 从该模块(`app/routers/items.py` 文件)所在的同一个包(`app/routers/` 目录)开始... +* 找到 `dependencies` 模块(一个位于 `app/routers/dependencies.py` 的虚构文件)... +* 然后从中导入函数 `get_token_header`。 -But that file doesn't exist, our dependencies are in a file at `app/dependencies.py`. +但是该文件并不存在,我们的依赖项位于 `app/dependencies.py` 文件中。 -Remember how our app/file structure looks like: +请记住我们的程序/文件结构是怎样的: --- -The two dots `..`, like in: +两个点 `..`,例如: ```Python from ..dependencies import get_token_header ``` -mean: +那将意味着: -* Starting in the same package that this module (the file `app/routers/items.py`) lives in (the directory `app/routers/`)... -* go to the parent package (the directory `app/`)... -* and in there, find the module `dependencies` (the file at `app/dependencies.py`)... -* and from it, import the function `get_token_header`. +* 从该模块(`app/routers/items.py` 文件)所在的同一个包(`app/routers/` 目录)开始... +* 跳转到其父包(`app/` 目录)... +* 在该父包中,找到 `dependencies` 模块(位于 `app/dependencies.py` 的文件)... +* 然后从中导入函数 `get_token_header`。 -That works correctly! 🎉 +正常工作了! 🎉 --- -The same way, if we had used three dots `...`, like in: +同样,如果我们使用了三个点 `...`,例如: ```Python from ...dependencies import get_token_header @@ -266,62 +289,62 @@ from ...dependencies import get_token_header that would mean: -* Starting in the same package that this module (the file `app/routers/items.py`) lives in (the directory `app/routers/`)... -* go to the parent package (the directory `app/`)... -* then go to the parent of that package (there's no parent package, `app` is the top level 😱)... -* and in there, find the module `dependencies` (the file at `app/dependencies.py`)... -* and from it, import the function `get_token_header`. +* 从该模块(`app/routers/items.py` 文件)所在的同一个包(`app/routers/` 目录)开始... +* 跳转到其父包(`app/` 目录)... +* 然后跳转到该包的父包(该父包并不存在,`app` 已经是最顶层的包 😱)... +* 在该父包中,找到 `dependencies` 模块(位于 `app/` 更上一级目录中的 `dependencies.py` 文件)... +* 然后从中导入函数 `get_token_header`。 -That would refer to some package above `app/`, with its own file `__init__.py`, etc. But we don't have that. So, that would throw an error in our example. 🚨 +这将引用 `app/` 的往上一级,带有其自己的 `__init __.py` 等文件的某个包。 但是我们并没有这个包。 因此,这将在我们的示例中引发错误。 🚨 -But now you know how it works, so you can use relative imports in your own apps no matter how complex they are. 🤓 +但是现在你知道了它的工作原理,因此无论它们多么复杂,你都可以在自己的应用程序中使用相对导入。 🤓 -### Add some custom `tags`, `responses`, and `dependencies` +### 添加一些自定义的 `tags`、`responses` 和 `dependencies` -We are not adding the prefix `/items` nor the `tags=["items"]` to each *path operation* because we added them to the `APIRouter`. +我们不打算在每个*路径操作*中添加前缀 `/items` 或 `tags =["items"]`,因为我们将它们添加到了 `APIRouter` 中。 -But we can still add _more_ `tags` that will be applied to a specific *path operation*, and also some extra `responses` specific to that *path operation*: +但是我们仍然可以添加*更多*将会应用于特定的*路径操作*的 `tags`,以及一些特定于该*路径操作*的额外 `responses`: ```Python hl_lines="30-31" {!../../../docs_src/bigger_applications/app/routers/items.py!} ``` -!!! tip - This last path operation will have the combination of tags: `["items", "custom"]`. +!!! !!! tip + 最后的这个路径操作将包含标签的组合:`["items","custom"]`。 - And it will also have both responses in the documentation, one for `404` and one for `403`. + 并且在文档中也会有两个响应,一个用于 `404`,一个用于 `403`。 -## The main `FastAPI` +## `FastAPI` 主体 -Now, let's see the module at `app/main.py`. +现在,让我们来看看位于 `app/main.py` 的模块。 -Here's where you import and use the class `FastAPI`. +你可以像平常一样导入并创建一个 `FastAPI` 类。 -This will be the main file in your application that ties everything together. +这将是你的应用程序中将所有内容联结在一起的主文件。 -And as most of your logic will now live in its own specific module, the main file will be quite simple. +并且由于你的大部分逻辑现在都存在于其自己的特定模块中,因此主文件的内容将非常简单。 -### Import `FastAPI` +### 导入 `FastAPI` -You import and create a `FastAPI` class as normally. +在这里你导入并使用 `FastAPI` 类。 -And we can even declare [global dependencies](dependencies/global-dependencies.md){.internal-link target=_blank} that will be combined with the dependencies for each `APIRouter`: +我们甚至可以声明[全局依赖项](dependencies/global-dependencies.md){.internal-link target=_blank},它会和每个 `APIRouter` 的依赖项组合在一起: ```Python hl_lines="1 3 7" {!../../../docs_src/bigger_applications/app/main.py!} ``` -### Import the `APIRouter` +### 导入 `APIRouter` -Now we import the other submodules that have `APIRouter`s: +现在,我们导入具有 `APIRouter` 的其他子模块: ```Python hl_lines="5" {!../../../docs_src/bigger_applications/app/main.py!} ``` -As the files `app/routers/users.py` and `app/routers/items.py` are submodules that are part of the same Python package `app`, we can use a single dot `.` to import them using "relative imports". +由于文件 `app/routers/users.py` 和 `app/routers/items.py` 是同一 Python 包 `app` 一个部分的子模块,因此我们可以使用单个点 `.` 通过「相对导入」来导入它们。 -### How the importing works +### 导入是如何工作的 The section: @@ -329,148 +352,148 @@ The section: from .routers import items, users ``` -Means: +表示: -* Starting in the same package that this module (the file `app/main.py`) lives in (the directory `app/`)... -* look for the subpackage `routers` (the directory at `app/routers/`)... -* and from it, import the submodule `items` (the file at `app/routers/items.py`) and `users` (the file at `app/routers/users.py`)... +* 从该模块(`app/main.py` 文件)所在的同一个包(`app/` 目录)开始... +* 寻找 `routers` 子包(位于 `app/routers/` 的目录)... +* 从该包中,导入子模块 `items` (位于 `app/routers/items.py` 的文件) 以及 `users` (位于 `app/routers/users.py` 的文件)... -The module `items` will have a variable `router` (`items.router`). This is the same one we created in the file `app/routers/items.py`, it's an `APIRouter` object. +`items` 模块将具有一个 `router` 变量(`items.router`)。 这与我们在 `app/routers/items.py` 文件中创建的变量相同,它是一个 `APIRouter` 对象。 -And then we do the same for the module `users`. +然后我们对 `users` 模块进行相同的操作。 -We could also import them like: +我们也可以像这样导入它们: ```Python from app.routers import items, users ``` -!!! info - The first version is a "relative import": +!!! !!! info + 第一个版本是「相对导入」: ```Python from .routers import items, users ``` - The second version is an "absolute import": + 第二个版本是「绝对导入」: ```Python from app.routers import items, users ``` - To learn more about Python Packages and Modules, read the official Python documentation about Modules. + 要了解有关 Python 包和模块的更多信息,请查阅关于 Modules 的 Python 官方文档。 -### Avoid name collisions +### 避免名称冲突 -We are importing the submodule `items` directly, instead of importing just its variable `router`. +我们将直接导入 `items` 子模块,而不是仅导入其 `router` 变量。 -This is because we also have another variable named `router` in the submodule `users`. +这是因为我们在 `users` 子模块中也有另一个名为 `router` 的变量。 -If we had imported one after the other, like: +如果我们一个接一个地导入,例如: ```Python from .routers.items import router from .routers.users import router ``` -The `router` from `users` would overwrite the one from `items` and we wouldn't be able to use them at the same time. +来自 `users` 的 `router` 将覆盖来自 `items` 中的 `router`,我们将无法同时使用它们。 -So, to be able to use both of them in the same file, we import the submodules directly: +因此,为了能够在同一个文件中使用它们,我们直接导入子模块: ```Python hl_lines="4" {!../../../docs_src/bigger_applications/app/main.py!} ``` -### Include the `APIRouter`s for `users` and `items` +### 包含 `users` 和 `items` 的 `APIRouter` -Now, let's include the `router`s from the submodules `users` and `items`: +现在,让我们来包含来自 `users` 和 `items` 子模块的 `router`。 ```Python hl_lines="10-11" {!../../../docs_src/bigger_applications/app/main.py!} ``` -!!! info - `users.router` contains the `APIRouter` inside of the file `app/routers/users.py`. +!!! !!! info + `users.router` 包含了 `app/routers/users.py` 文件中的 `APIRouter`。 - And `items.router` contains the `APIRouter` inside of the file `app/routers/items.py`. + `items.router` 包含了 `app/routers/items.py` 文件中的 `APIRouter`。 -With `app.include_router()` we can add each `APIRouter` to the main `FastAPI` application. +使用 `app.include_router()`,我们可以将每个 `APIRouter` 添加到主 `FastAPI` 应用程序中。 -It will include all the routes from that router as part of it. +它将包含来自该路由器的所有路由作为其一部分。 !!! note "Technical Details" It will actually internally create a *path operation* for each *path operation* that was declared in the `APIRouter`. - So, behind the scenes, it will actually work as if everything was the same single app. + 所以,在幕后,它实际上会像所有的东西都是同一个应用程序一样工作。 -!!! check - You don't have to worry about performance when including routers. +!!! !!! check + 包含路由器时,你不必担心性能问题。 - This will take microseconds and will only happen at startup. + 这将花费几微秒时间,并且只会在启动时发生。 - So it won't affect performance. ⚡ + 因此,它不会影响性能。 ⚡ -### Include an `APIRouter` with a custom `prefix`, `tags`, `responses`, and `dependencies` +### 包含一个有自定义 `prefix`、`tags`、`responses` 和 `dependencies` 的 `APIRouter` -Now, let's imagine your organization gave you the `app/internal/admin.py` file. +现在,假设你的组织为你提供了 `app/internal/admin.py` 文件。 -It contains an `APIRouter` with some admin *path operations* that your organization shares between several projects. +它包含一个带有一些由你的组织在多个项目之间共享的管理员*路径操作*的 `APIRouter`。 -For this example it will be super simple. But let's say that because it is shared with other projects in the organization, we cannot modify it and add a `prefix`, `dependencies`, `tags`, etc. directly to the `APIRouter`: +对于此示例,它将非常简单。 但是假设由于它是与组织中的其他项目所共享的,因此我们无法对其进行修改,以及直接在 `APIRouter` 中添加 `prefix`、`dependencies`、`tags` 等: ```Python hl_lines="3" {!../../../docs_src/bigger_applications/app/internal/admin.py!} ``` -But we still want to set a custom `prefix` when including the `APIRouter` so that all its *path operations* start with `/admin`, we want to secure it with the `dependencies` we already have for this project, and we want to include `tags` and `responses`. +但是我们仍然希望在包含 `APIRouter` 时设置一个自定义的 `prefix`,以便其所有*路径操作*以 `/admin` 开头,我们希望使用本项目已经有的 `dependencies` 保护它,并且我们希望它包含自定义的 `tags` 和 `responses`。 -We can declare all that without having to modify the original `APIRouter` by passing those parameters to `app.include_router()`: +但这只会影响我们应用中的 `APIRouter`,而不会影响使用它的任何其他代码。 ```Python hl_lines="14-17" {!../../../docs_src/bigger_applications/app/main.py!} ``` -That way, the original `APIRouter` will keep unmodified, so we can still share that same `app/internal/admin.py` file with other projects in the organization. +这样,原始的 `APIRouter` 将保持不变,因此我们仍然可以与组织中的其他项目共享相同的 `app/internal/admin.py` 文件。 -The result is that in our app, each of the *path operations* from the `admin` module will have: +结果是在我们的应用程序中,来自 `admin` 模块的每个*路径操作*都将具有: -* The prefix `/admin`. -* The tag `admin`. -* The dependency `get_token_header`. -* The response `418`. 🍵 +* `/admin` 前缀 。 +* `admin` 标签。 +* `get_token_header` 依赖项。 +* `418` 响应。 🍵 -But that will only affect that `APIRouter` in our app, not in any other code that uses it. +因此,我们可以将其添加到 `APIRouter` 中,而不是将其添加到每个路径操作中。 -So, for example, other projects could use the same `APIRouter` with a different authentication method. +因此,举例来说,其他项目能够以不同的身份认证方法使用相同的 `APIRouter`。 -### Include a *path operation* +### 包含一个*路径操作* -We can also add *path operations* directly to the `FastAPI` app. +我们还可以直接将*路径操作*添加到 `FastAPI` 应用中。 -Here we do it... just to show that we can 🤷: +这里我们这样做了...只是为了表明我们可以做到🤷: ```Python hl_lines="21-23" {!../../../docs_src/bigger_applications/app/main.py!} ``` -and it will work correctly, together with all the other *path operations* added with `app.include_router()`. +它将与通过 `app.include_router()` 添加的所有其他*路径操作*一起正常运行。 -!!! info "Very Technical Details" - **Note**: this is a very technical detail that you probably can **just skip**. +!!! !!! info "特别的技术细节" + **注意**:这是一个非常技术性的细节,你也许可以**直接跳过**。 --- - The `APIRouter`s are not "mounted", they are not isolated from the rest of the application. + `APIRouter` 没有被「挂载」,它们与应用程序的其余部分没有隔离。 - This is because we want to include their *path operations* in the OpenAPI schema and the user interfaces. + 这是因为我们想要在 OpenAPI 模式和用户界面中包含它们的*路径操作*。 - As we cannot just isolate them and "mount" them independently of the rest, the *path operations* are "cloned" (re-created), not included directly. + 由于我们不能仅仅隔离它们并独立于其余部分来「挂载」它们,因此*路径操作*是被「克隆的」(重新创建),而不是直接包含。 -## Check the automatic API docs +## 查看自动化的 API 文档 -Now, run `uvicorn`, using the module `app.main` and the variable `app`: +现在,使用 `app.main` 模块和 `app` 变量运行 `uvicorn`:
@@ -482,26 +505,26 @@ $ uvicorn app.main:app --reload
-And open the docs at http://127.0.0.1:8000/docs. +然后打开位于 http://127.0.0.1:8000/docs 的文档。 -You will see the automatic API docs, including the paths from all the submodules, using the correct paths (and prefixes) and the correct tags: +你将看到使用了正确路径(和前缀)和正确标签的自动化 API 文档,包括了来自所有子模块的路径: -## Include the same router multiple times with different `prefix` +## 多次使用不同的 `prefix` 包含同一个路由器 -You can also use `.include_router()` multiple times with the *same* router using different prefixes. +你也可以在*同一*路由器上使用不同的前缀来多次使用 `.include_router()`。 -This could be useful, for example, to expose the same API under different prefixes, e.g. `/api/v1` and `/api/latest`. +在有些场景这可能有用,例如以不同的前缀公开同一个的 API,比方说 `/api/v1` 和 `/api/latest`。 -This is an advanced usage that you might not really need, but it's there in case you do. +这是一个你可能并不真正需要的高级用法,但万一你有需要了就能够用上。 -## Include an `APIRouter` in another +## 在另一个 `APIRouter` 中包含一个 `APIRouter` -The same way you can include an `APIRouter` in a `FastAPI` application, you can include an `APIRouter` in another `APIRouter` using: +与在 `FastAPI` 应用程序中包含 `APIRouter` 的方式相同,你也可以在另一个 `APIRouter` 中包含 `APIRouter`,通过: ```Python router.include_router(other_router) ``` -Make sure you do it before including `router` in the `FastAPI` app, so that the *path operations* from `other_router` are also included. +请确保在你将 `router` 包含到 `FastAPI` 应用程序之前进行此操作,以便 `other_router` 中的`路径操作`也能被包含进来。 From e796961df1bc14695efb5974ac0154808ad365a0 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:26 +0800 Subject: [PATCH 122/163] New translations body-fields.md (Chinese Simplified) --- docs/zh/docs/tutorial/body-fields.md | 51 ++++++++++++++++------------ 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/docs/zh/docs/tutorial/body-fields.md b/docs/zh/docs/tutorial/body-fields.md index 36039020923ba..da96baaecfbc0 100644 --- a/docs/zh/docs/tutorial/body-fields.md +++ b/docs/zh/docs/tutorial/body-fields.md @@ -1,10 +1,10 @@ # Body - Fields -The same way you can declare additional validation and metadata in *path operation function* parameters with `Query`, `Path` and `Body`, you can declare validation and metadata inside of Pydantic models using Pydantic's `Field`. +与使用 `Query`、`Path` 和 `Body` 在*路径操作函数*中声明额外的校验和元数据的方式相同,你可以使用 Pydantic 的 `Field` 在 Pydantic 模型内部声明校验和元数据。 -## Import `Field` +## 导入 `Field` -First, you have to import it: +首先,你必须导入它: === "Python 3.10+" @@ -21,8 +21,11 @@ First, you have to import it: === "Python 3.6+" ```Python hl_lines="4" - {!> ../../../docs_src/body_fields/tutorial001_an.py!} + !!! note "技术细节" + 实际上,QueryPath 和其他你将在之后看到的类,创建的是由一个共同的 Params 类派生的子类的对象,该共同类本身又是 Pydantic 的 FieldInfo 类的子类。 ``` +、Path 和其他你将在之后看到的类,创建的是由一个共同的 Params 类派生的子类的对象,该共同类本身又是 Pydantic 的 FieldInfo 类的子类。 + === "Python 3.10+ non-Annotated" @@ -30,7 +33,7 @@ First, you have to import it: Prefer to use the `Annotated` version if possible. ```Python hl_lines="2" - {!> ../../../docs_src/body_fields/tutorial001_py310.py!} + {!../../../docs_src/body_fields/tutorial001.py!} ``` === "Python 3.6+ non-Annotated" @@ -39,20 +42,23 @@ First, you have to import it: Prefer to use the `Annotated` version if possible. ```Python hl_lines="4" - {!> ../../../docs_src/body_fields/tutorial001.py!} + !!! tip + 注意每个模型属性如何使用类型、默认值和 Field 在代码结构上和路径操作函数的参数是相同的,区别是用 Field 替换PathQueryBody。 ``` + 在代码结构上和*路径操作函数*的参数是相同的,区别是用 Field 替换Path、Query 和 Body。 + !!! warning Notice that `Field` is imported directly from `pydantic`, not from `fastapi` as are all the rest (`Query`, `Path`, `Body`, etc). -## Declare model attributes +## 声明模型属性 -You can then use `Field` with model attributes: +然后,你可以对模型属性使用 `Field`: === "Python 3.10+" ```Python hl_lines="11-14" - {!> ../../../docs_src/body_fields/tutorial001_an_py310.py!} + 总结 ``` === "Python 3.9+" @@ -64,7 +70,7 @@ You can then use `Field` with model attributes: === "Python 3.6+" ```Python hl_lines="12-15" - {!> ../../../docs_src/body_fields/tutorial001_an.py!} + 请求体 - 字段 ``` === "Python 3.10+ non-Annotated" @@ -73,7 +79,7 @@ You can then use `Field` with model attributes: Prefer to use the `Annotated` version if possible. ```Python hl_lines="9-12" - {!> ../../../docs_src/body_fields/tutorial001_py310.py!} + {!../../../docs_src/body_fields/tutorial001.py!} ``` === "Python 3.6+ non-Annotated" @@ -82,34 +88,37 @@ You can then use `Field` with model attributes: Prefer to use the `Annotated` version if possible. ```Python hl_lines="11-14" - {!> ../../../docs_src/body_fields/tutorial001.py!} + !!! warning + 注意,Field 是直接从 pydantic 导入的,而不是像其他的(QueryPathBody 等)都从 fastapi 导入。 ``` + 是直接从 pydantic 导入的,而不是像其他的(Query,Path,Body 等)都从 fastapi 导入。 + -`Field` works the same way as `Query`, `Path` and `Body`, it has all the same parameters, etc. +`Field` 的工作方式和 `Query`、`Path` 和 `Body` 相同,包括它们的参数等等也完全相同。 !!! note "Technical Details" Actually, `Query`, `Path` and others you'll see next create objects of subclasses of a common `Param` class, which is itself a subclass of Pydantic's `FieldInfo` class. - And Pydantic's `Field` returns an instance of `FieldInfo` as well. + Pydantic 的 `Field` 也会返回一个 `FieldInfo` 的实例。 - `Body` also returns objects of a subclass of `FieldInfo` directly. And there are others you will see later that are subclasses of the `Body` class. + `Body` 也直接返回 `FieldInfo` 的一个子类的对象。 还有其他一些你之后会看到的类是 `Body` 类的子类。 - Remember that when you import `Query`, `Path`, and others from `fastapi`, those are actually functions that return special classes. + 请记住当你从 `fastapi` 导入 `Query`、`Path` 等对象时,他们实际上是返回特殊类的函数。 !!! tip Notice how each model's attribute with a type, default value and `Field` has the same structure as a *path operation function's* parameter, with `Field` instead of `Path`, `Query` and `Body`. -## Add extra information +## 添加额外信息 -You can declare extra information in `Field`, `Query`, `Body`, etc. And it will be included in the generated JSON Schema. +你可以在 `Field`、`Query`、`Body` 中声明额外的信息。 这些信息将包含在生成的 JSON Schema 中。 -You will learn more about adding extra information later in the docs, when learning to declare examples. +你将在文档的后面部分学习声明示例时,了解到更多有关添加额外信息的知识。 !!! warning Extra keys passed to `Field` will also be present in the resulting OpenAPI schema for your application. As these keys may not necessarily be part of the OpenAPI specification, some OpenAPI tools, for example [the OpenAPI validator](https://validator.swagger.io/), may not work with your generated schema. ## Recap -You can use Pydantic's `Field` to declare extra validations and metadata for model attributes. +你可以使用 Pydantic 的 `Field` 为模型属性声明额外的校验和元数据。 -You can also use the extra keyword arguments to pass additional JSON Schema metadata. +你还可以使用额外的关键字参数来传递额外的 JSON Schema 元数据。 From d33e24d93298b45f5c656644f69afb0576ff6b2c Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:27 +0800 Subject: [PATCH 123/163] New translations body-multiple-params.md (Chinese Simplified) --- docs/zh/docs/tutorial/body-multiple-params.md | 96 ++++++++++--------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/docs/zh/docs/tutorial/body-multiple-params.md b/docs/zh/docs/tutorial/body-multiple-params.md index b214092c9b175..6219c9aca93f2 100644 --- a/docs/zh/docs/tutorial/body-multiple-params.md +++ b/docs/zh/docs/tutorial/body-multiple-params.md @@ -1,12 +1,12 @@ -# Body - Multiple Parameters +# 请求体 - 多个参数 -Now that we have seen how to use `Path` and `Query`, let's see more advanced uses of request body declarations. +既然我们已经知道了如何使用 `Path` 和 `Query`,下面让我们来了解一下请求体声明的更高级用法。 -## Mix `Path`, `Query` and body parameters +## 混合使用 `Path`、`Query` 和请求体参数 -First, of course, you can mix `Path`, `Query` and request body parameter declarations freely and **FastAPI** will know what to do. +首先,毫无疑问地,你可以随意地混合使用 `Path`、`Query` 和请求体参数声明,**FastAPI** 会知道该如何处理。 -And you can also declare body parameters as optional, by setting the default to `None`: +你还可以通过将默认值设置为 `None` 来将请求体参数声明为可选参数: === "Python 3.10+" @@ -44,12 +44,12 @@ And you can also declare body parameters as optional, by setting the default to {!> ../../../docs_src/body_multiple_params/tutorial001.py!} ``` -!!! note - Notice that, in this case, the `item` that would be taken from the body is optional. As it has a `None` default value. +!!! !!! note + 请注意,在这种情况下,将从请求体获取的 `item` 是可选的。 因为它的默认值为 `None`。 -## Multiple body parameters +## 多个请求体参数 -In the previous example, the *path operations* would expect a JSON body with the attributes of an `Item`, like: +在上面的示例中,*路径操作*将期望一个具有 `Item` 的属性的 JSON 请求体,就像: ```JSON { @@ -60,7 +60,7 @@ In the previous example, the *path operations* would expect a JSON body with the } ``` -But you can also declare multiple body parameters, e.g. `item` and `user`: +但是你也可以声明多个请求体参数,例如 `item` 和 `user`: === "Python 3.10+" @@ -71,12 +71,12 @@ But you can also declare multiple body parameters, e.g. `item` and `user`: === "Python 3.6+" ```Python hl_lines="22" - {!> ../../../docs_src/body_multiple_params/tutorial002.py!} + {!../../../docs_src/body_multiple_params/tutorial002.py!} ``` -In this case, **FastAPI** will notice that there are more than one body parameters in the function (two parameters that are Pydantic models). +如果你就按原样声明它,因为它是一个单一值,**FastAPI** 将假定它是一个查询参数。 -So, it will then use the parameter names as keys (field names) in the body, and expect a body like: +因此,它将使用参数名称作为请求体中的键(字段名称),并期望一个类似于以下内容的请求体: ```JSON { @@ -97,19 +97,19 @@ So, it will then use the parameter names as keys (field names) in the body, and Notice that even though the `item` was declared the same way as before, it is now expected to be inside of the body with a key `item`. -**FastAPI** will do the automatic conversion from the request, so that the parameter `item` receives it's specific content and the same for `user`. +**FastAPI** 将自动对请求中的数据进行转换,因此 `item` 参数将接收指定的内容,`user` 参数也是如此。 -It will perform the validation of the compound data, and will document it like that for the OpenAPI schema and automatic docs. +它将执行对复合数据的校验,并且像现在这样为 OpenAPI 模式和自动化文档对其进行记录。 -## Singular values in body +## 请求体中的单一值 -The same way there is a `Query` and `Path` to define extra data for query and path parameters, **FastAPI** provides an equivalent `Body`. +与使用 `Query` 和 `Path` 为查询参数和路径参数定义额外数据的方式相同,**FastAPI** 提供了一个同等的 `Body`。 -For example, extending the previous model, you could decide that you want to have another key `importance` in the same body, besides the `item` and `user`. +例如,为了扩展先前的模型,你可能决定除了 `item` 和 `user` 之外,还想在同一请求体中具有另一个键 `importance`。 -If you declare it as is, because it is a singular value, **FastAPI** will assume that it is a query parameter. +在这种情况下,**FastAPI** 将注意到该函数中有多个请求体参数(两个 Pydantic 模型参数)。 -But you can instruct **FastAPI** to treat it as another body key using `Body`: +但是你可以使用 `Body` 指示 **FastAPI** 将其作为请求体的另一个键进行处理。 === "Python 3.10+" @@ -120,13 +120,16 @@ But you can instruct **FastAPI** to treat it as another body key using `Body`: === "Python 3.9+" ```Python hl_lines="23" - {!> ../../../docs_src/body_multiple_params/tutorial003_an_py39.py!} + !!! info + Body 同样具有与 QueryPath 以及其他后面将看到的类完全相同的额外校验和元数据参数。 ``` + 同样具有与 Query、Path 以及其他后面将看到的类完全相同的额外校验和元数据参数。 + === "Python 3.6+" ```Python hl_lines="24" - {!> ../../../docs_src/body_multiple_params/tutorial003_an.py!} + {!../../../docs_src/body_multiple_params/tutorial003.py!} ``` === "Python 3.10+ non-Annotated" @@ -135,7 +138,7 @@ But you can instruct **FastAPI** to treat it as another body key using `Body`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="20" - {!> ../../../docs_src/body_multiple_params/tutorial003_py310.py!} + {!../../../docs_src/body_multiple_params/tutorial004.py!} ``` === "Python 3.6+ non-Annotated" @@ -144,10 +147,10 @@ But you can instruct **FastAPI** to treat it as another body key using `Body`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="22" - {!> ../../../docs_src/body_multiple_params/tutorial003.py!} + {!../../../docs_src/body_multiple_params/tutorial005.py!} ``` -In this case, **FastAPI** will expect a body like: +在这种情况下,**FastAPI** 将期望像这样的请求体: ```JSON { @@ -165,25 +168,28 @@ In this case, **FastAPI** will expect a body like: } ``` -Again, it will convert the data types, validate, document, etc. +同样的,它将转换数据类型,校验,生成文档等。 -## Multiple body params and query +## 多个请求体参数和查询参数 -Of course, you can also declare additional query parameters whenever you need, additional to any body parameters. +当然,除了请求体参数外,你还可以在任何需要的时候声明额外的查询参数。 -As, by default, singular values are interpreted as query parameters, you don't have to explicitly add a `Query`, you can just do: +由于默认情况下单一值被解释为查询参数,因此你不必显式地添加 `Query`,你可以仅执行以下操作: ```Python -q: Union[str, None] = None +!!! note + 请注意,即使 item 的声明方式与之前相同,但现在它被期望通过 item 键内嵌在请求体中。 ``` + 的声明方式与之前相同,但现在它被期望通过 item 键内嵌在请求体中。 + Or in Python 3.10 and above: ```Python -q: str | None = None +q: str = None ``` -For example: +比如: === "Python 3.10+" @@ -200,7 +206,7 @@ For example: === "Python 3.6+" ```Python hl_lines="28" - {!> ../../../docs_src/body_multiple_params/tutorial004_an.py!} + {!../../../docs_src/body_multiple_params/tutorial001.py!} ``` === "Python 3.10+ non-Annotated" @@ -224,24 +230,24 @@ For example: !!! info `Body` also has all the same extra validation and metadata parameters as `Query`,`Path` and others you will see later. -## Embed a single body parameter +## 嵌入单个请求体参数 -Let's say you only have a single `item` body parameter from a Pydantic model `Item`. +假设你只有一个来自 Pydantic 模型 `Item` 的请求体参数 `item`。 -By default, **FastAPI** will then expect its body directly. +默认情况下,**FastAPI** 将直接期望这样的请求体。 -But if you want it to expect a JSON with a key `item` and inside of it the model contents, as it does when you declare extra body parameters, you can use the special `Body` parameter `embed`: +但是,如果你希望它期望一个拥有 `item` 键并在值中包含模型内容的 JSON,就像在声明额外的请求体参数时所做的那样,则可以使用一个特殊的 `Body` 参数 `embed`: ```Python item: Item = Body(embed=True) ``` -as in: +比如: === "Python 3.10+" ```Python hl_lines="17" - {!> ../../../docs_src/body_multiple_params/tutorial005_an_py310.py!} + 总结 ``` === "Python 3.9+" @@ -274,7 +280,7 @@ as in: {!> ../../../docs_src/body_multiple_params/tutorial005.py!} ``` -In this case **FastAPI** will expect a body like: +在这种情况下,**FastAPI** 将期望像这样的请求体: ```JSON hl_lines="2" { @@ -287,7 +293,7 @@ In this case **FastAPI** will expect a body like: } ``` -instead of: +而不是: ```JSON { @@ -300,10 +306,10 @@ instead of: ## Recap -You can add multiple body parameters to your *path operation function*, even though a request can only have a single body. +你可以添加多个请求体参数到*路径操作函数*中,即使一个请求只能有一个请求体。 -But **FastAPI** will handle it, give you the correct data in your function, and validate and document the correct schema in the *path operation*. +但是 **FastAPI** 会处理它,在函数中为你提供正确的数据,并在*路径操作*中校验并记录正确的模式。 -You can also declare singular values to be received as part of the body. +你还可以声明将作为请求体的一部分所接收的单一值。 -And you can instruct **FastAPI** to embed the body in a key even when there is only a single parameter declared. +你还可以指示 **FastAPI** 在仅声明了一个请求体参数的情况下,将原本的请求体嵌入到一个键中。 From 0c1f9711926601d7fb81fbd3886a18e7035adf13 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:28 +0800 Subject: [PATCH 124/163] New translations body-nested-models.md (Chinese Simplified) --- docs/zh/docs/tutorial/body-nested-models.md | 189 +++++++++++--------- 1 file changed, 101 insertions(+), 88 deletions(-) diff --git a/docs/zh/docs/tutorial/body-nested-models.md b/docs/zh/docs/tutorial/body-nested-models.md index 7adfd8c03d396..7d4819642168f 100644 --- a/docs/zh/docs/tutorial/body-nested-models.md +++ b/docs/zh/docs/tutorial/body-nested-models.md @@ -1,10 +1,10 @@ -# Body - Nested Models +# 请求体 - 嵌套模型 -With **FastAPI**, you can define, validate, document, and use arbitrarily deeply nested models (thanks to Pydantic). +使用 **FastAPI**,你可以定义、校验、记录文档并使用任意深度嵌套的模型(归功于Pydantic)。 -## List fields +## List 字段 -You can define an attribute to be a subtype. For example, a Python `list`: +你可以将一个属性定义为拥有子元素的类型。 例如 Python `list`: === "Python 3.10+" @@ -15,31 +15,33 @@ You can define an attribute to be a subtype. For example, a Python `list`: === "Python 3.6+" ```Python hl_lines="14" - {!> ../../../docs_src/body_nested_models/tutorial001.py!} + 除了普通的单一值类型(如 strintfloat 等)外,你还可以使用从 str 继承的更复杂的单一值类型。 ``` +、int、float 等)外,你还可以使用从 str 继承的更复杂的单一值类型。 + -This will make `tags` be a list, although it doesn't declare the type of the elements of the list. +这将使 `tags` 成为一个由元素组成的列表。 不过它没有声明每个元素的类型。 -## List fields with type parameter +## 具有子类型的 List 字段 -But Python has a specific way to declare lists with internal types, or "type parameters": +但是 Python 有一种特定的方法来声明具有子类型的列表: -### Import typing's `List` +### 从 typing 导入 `List` In Python 3.9 and above you can use the standard `list` to declare these type annotations as we'll see below. 💡 -But in Python versions before 3.9 (3.6 and above), you first need to import `List` from standard Python's `typing` module: +首先,从 Python 的标准库 `typing` 模块中导入 `List`: ```Python hl_lines="1" -{!> ../../../docs_src/body_nested_models/tutorial002.py!} +{!../../../docs_src/body_nested_models/tutorial001.py!} ``` -### Declare a `list` with a type parameter +### 声明具有子类型的 List -To declare types that have type parameters (internal types), like `list`, `dict`, `tuple`: +要声明具有子类型的类型,例如 `list`、`dict`、`tuple`: * If you are in a Python version lower than 3.9, import their equivalent version from the `typing` module -* Pass the internal type(s) as "type parameters" using square brackets: `[` and `]` +* 使用方括号 `[` 和 `]` 将子类型作为「类型参数」传入 In Python 3.9 it would be: @@ -55,16 +57,16 @@ from typing import List my_list: List[str] ``` -That's all standard Python syntax for type declarations. +这完全是用于类型声明的标准 Python 语法。 -Use that same standard syntax for model attributes with internal types. +对具有子类型的模型属性也使用相同的标准语法。 -So, in our example, we can make `tags` be specifically a "list of strings": +因此,在我们的示例中,我们可以将 `tags` 明确地指定为一个「字符串列表」: === "Python 3.10+" ```Python hl_lines="12" - {!> ../../../docs_src/body_nested_models/tutorial002_py310.py!} + {!../../../docs_src/body_nested_models/tutorial002.py!} ``` === "Python 3.9+" @@ -76,21 +78,21 @@ So, in our example, we can make `tags` be specifically a "list of strings": === "Python 3.6+" ```Python hl_lines="14" - {!> ../../../docs_src/body_nested_models/tutorial002.py!} + {!../../../docs_src/body_nested_models/tutorial002.py!} ``` -## Set types +## Set 类型 -But then we think about it, and realize that tags shouldn't repeat, they would probably be unique strings. +但是随后我们考虑了一下,意识到标签不应该重复,它们很大可能会是唯一的字符串。 -And Python has a special data type for sets of unique items, the `set`. +Python 具有一种特殊的数据类型来保存一组唯一的元素,即 `set`。 -Then we can declare `tags` as a set of strings: +然后我们可以导入 `Set` 并将 `tag` 声明为一个由 `str` 组成的 `set`: === "Python 3.10+" ```Python hl_lines="12" - {!> ../../../docs_src/body_nested_models/tutorial003_py310.py!} + {!../../../docs_src/body_nested_models/tutorial009.py!} ``` === "Python 3.9+" @@ -102,33 +104,33 @@ Then we can declare `tags` as a set of strings: === "Python 3.6+" ```Python hl_lines="1 14" - {!> ../../../docs_src/body_nested_models/tutorial003.py!} + {!../../../docs_src/body_nested_models/tutorial003.py!} ``` -With this, even if you receive a request with duplicate data, it will be converted to a set of unique items. +这样,即使你收到带有重复数据的请求,这些数据也会被转换为一组唯一项。 -And whenever you output that data, even if the source had duplicates, it will be output as a set of unique items. +而且,每当你输出该数据时,即使源数据有重复,它们也将作为一组唯一项输出。 -And it will be annotated / documented accordingly too. +并且还会被相应地标注 / 记录文档。 -## Nested Models +## 嵌套模型 -Each attribute of a Pydantic model has a type. +Pydantic 模型的每个属性都具有类型。 -But that type can itself be another Pydantic model. +但是这个类型本身可以是另一个 Pydantic 模型。 -So, you can declare deeply nested JSON "objects" with specific attribute names, types and validations. +因此,你可以声明拥有特定属性名称、类型和校验的深度嵌套的 JSON 对象。 All that, arbitrarily nested. -### Define a submodel +### 定义子模型 -For example, we can define an `Image` model: +例如,我们可以定义一个 `Image` 模型: === "Python 3.10+" ```Python hl_lines="7-9" - {!> ../../../docs_src/body_nested_models/tutorial004_py310.py!} + {!../../../docs_src/body_nested_models/tutorial004.py!} ``` === "Python 3.9+" @@ -140,17 +142,17 @@ For example, we can define an `Image` model: === "Python 3.6+" ```Python hl_lines="9-11" - {!> ../../../docs_src/body_nested_models/tutorial004.py!} + {!../../../docs_src/body_nested_models/tutorial004.py!} ``` -### Use the submodel as a type +### 将子模型用作类型 -And then we can use it as the type of an attribute: +然后我们可以将其用作一个属性的类型: === "Python 3.10+" ```Python hl_lines="18" - {!> ../../../docs_src/body_nested_models/tutorial004_py310.py!} + 例如: ``` === "Python 3.9+" @@ -162,10 +164,12 @@ And then we can use it as the type of an attribute: === "Python 3.6+" ```Python hl_lines="20" - {!> ../../../docs_src/body_nested_models/tutorial004.py!} + 从 typing 模块导入它们 ``` + 模块导入它们 + -This would mean that **FastAPI** would expect a body similar to: +这意味着 **FastAPI** 将期望类似于以下内容的请求体: ```JSON { @@ -181,25 +185,25 @@ This would mean that **FastAPI** would expect a body similar to: } ``` -Again, doing just that declaration, with **FastAPI** you get: +再一次,仅仅进行这样的声明,你将通过 **FastAPI** 获得: -* Editor support (completion, etc), even for nested models -* Data conversion -* Data validation -* Automatic documentation +* 对被嵌入的模型也适用的编辑器支持(自动补全等) +* 数据转换 +* 数据校验 +* 自动生成文档 -## Special types and validation +## 特殊的类型和校验 Apart from normal singular types like `str`, `int`, `float`, etc. You can use more complex singular types that inherit from `str`. -To see all the options you have, checkout the docs for Pydantic's exotic types. You will see some examples in the next chapter. +要了解所有的可用选项,请查看关于 来自 Pydantic 的外部类型 的文档。 你将在下一章节中看到一些示例。 -For example, as in the `Image` model we have a `url` field, we can declare it to be instead of a `str`, a Pydantic's `HttpUrl`: +例如,在 `Image` 模型中我们有一个 `url` 字段,我们可以把它声明为 Pydantic 的 `HttpUrl`,而不是 `str`: === "Python 3.10+" ```Python hl_lines="2 8" - {!> ../../../docs_src/body_nested_models/tutorial005_py310.py!} + {!../../../docs_src/body_nested_models/tutorial005.py!} ``` === "Python 3.9+" @@ -211,19 +215,22 @@ For example, as in the `Image` model we have a `url` field, we can declare it to === "Python 3.6+" ```Python hl_lines="4 10" - {!> ../../../docs_src/body_nested_models/tutorial005.py!} + !!! info + 请注意 images 键现在具有一组 image 对象是如何发生的。 ``` + 键现在具有一组 image 对象是如何发生的。 + -The string will be checked to be a valid URL, and documented in JSON Schema / OpenAPI as such. +该字符串将被检查是否为有效的 URL,并在 JSON Schema / OpenAPI 文档中进行记录。 -## Attributes with lists of submodels +## 带有一组子模型的属性 -You can also use Pydantic models as subtypes of `list`, `set`, etc: +你还可以将 Pydantic 模型用作 `list`、`set` 等的子类型: === "Python 3.10+" ```Python hl_lines="18" - {!> ../../../docs_src/body_nested_models/tutorial006_py310.py!} + {!../../../docs_src/body_nested_models/tutorial006.py!} ``` === "Python 3.9+" @@ -235,10 +242,13 @@ You can also use Pydantic models as subtypes of `list`, `set`, etc: === "Python 3.6+" ```Python hl_lines="20" - {!> ../../../docs_src/body_nested_models/tutorial006.py!} + !!! tip + 请记住 JSON 仅支持将 str 作为键。 ``` + 作为键。 + -This will expect (convert, validate, document, etc) a JSON body like: +这将期望(转换,校验,记录文档等)下面这样的 JSON 请求体: ```JSON hl_lines="11" { @@ -267,34 +277,34 @@ This will expect (convert, validate, document, etc) a JSON body like: !!! info Notice how the `images` key now has a list of image objects. -## Deeply nested models +## 深度嵌套模型 -You can define arbitrarily deeply nested models: +你可以定义任意深度的嵌套模型: === "Python 3.10+" ```Python hl_lines="7 12 18 21 25" - {!> ../../../docs_src/body_nested_models/tutorial007_py310.py!} + 上述这些都可以任意的嵌套。 ``` === "Python 3.9+" ```Python hl_lines="9 14 20 23 27" - {!> ../../../docs_src/body_nested_models/tutorial007_py39.py!} + 总结 ``` === "Python 3.6+" ```Python hl_lines="9 14 20 23 27" - {!> ../../../docs_src/body_nested_models/tutorial007.py!} + {!../../../docs_src/body_nested_models/tutorial007.py!} ``` !!! info Notice how `Offer` has a list of `Item`s, which in turn have an optional list of `Image`s -## Bodies of pure lists +## 纯列表请求体 -If the top level value of the JSON body you expect is a JSON `array` (a Python `list`), you can declare the type in the parameter of the function, the same as in Pydantic models: +如果你期望的 JSON 请求体的最外层是一个 JSON `array`(即 Python `list`),则可以在路径操作函数的参数中声明此类型,就像声明 Pydantic 模型一样: ```Python images: List[Image] @@ -317,66 +327,69 @@ as in: === "Python 3.6+" ```Python hl_lines="15" - {!> ../../../docs_src/body_nested_models/tutorial008.py!} + {!../../../docs_src/body_nested_models/tutorial008.py!} ``` -## Editor support everywhere +## 无处不在的编辑器支持 -And you get editor support everywhere. +你可以随处获得编辑器支持。 -Even for items inside of lists: +即使是列表中的元素: -You couldn't get this kind of editor support if you were working directly with `dict` instead of Pydantic models. +如果你直接使用 `dict` 而不是 Pydantic 模型,那你将无法获得这种编辑器支持。 -But you don't have to worry about them either, incoming dicts are converted automatically and your output is converted automatically to JSON too. +但是你根本不必担心这两者,传入的字典会自动被转换,你的输出也会自动被转换为 JSON。 -## Bodies of arbitrary `dict`s +## 任意 `dict` 构成的请求体 -You can also declare a body as a `dict` with keys of some type and values of other type. +你也可以将请求体声明为使用某类型的键和其他类型值的 `dict`。 -Without having to know beforehand what are the valid field/attribute names (as would be the case with Pydantic models). +无需事先知道有效的字段/属性(在使用 Pydantic 模型的场景)名称是什么。 -This would be useful if you want to receive keys that you don't already know. +如果你想接收一些尚且未知的键,这将很有用。 --- -Other useful case is when you want to have keys of other type, e.g. `int`. +其他有用的场景是当你想要接收其他类型的键时,例如 `int`。 -That's what we are going to see here. +这也是我们在接下来将看到的。 -In this case, you would accept any `dict` as long as it has `int` keys with `float` values: +在下面的例子中,你将接受任意键为 `int` 类型并且值为 `float` 类型的 `dict`: === "Python 3.9+" ```Python hl_lines="7" - {!> ../../../docs_src/body_nested_models/tutorial009_py39.py!} + https://fastapi.tiangolo.com/img/tutorial/body-nested-models/image01.png ``` === "Python 3.6+" ```Python hl_lines="9" - {!> ../../../docs_src/body_nested_models/tutorial009.py!} + !!! info + 请注意 Offer 拥有一组 Item 而反过来 Item 又是一个可选的 Image 列表是如何发生的。 ``` + 拥有一组 Item 而反过来 Item 又是一个可选的 Image 列表是如何发生的。 + !!! tip Have in mind that JSON only supports `str` as keys. - But Pydantic has automatic data conversion. + 但是 Pydantic 具有自动转换数据的功能。 - This means that, even though your API clients can only send strings as keys, as long as those strings contain pure integers, Pydantic will convert them and validate them. + 这意味着,即使你的 API 客户端只能将字符串作为键发送,只要这些字符串内容仅包含整数,Pydantic 就会对其进行转换并校验。 - And the `dict` you receive as `weights` will actually have `int` keys and `float` values. + 然后你接收的名为 `weights` 的 `dict` 实际上将具有 `int` 类型的键和 `float` 类型的值。 ## Recap -With **FastAPI** you have the maximum flexibility provided by Pydantic models, while keeping your code simple, short and elegant. +使用 **FastAPI** 你可以拥有 Pydantic 模型提供的极高灵活性,同时保持代码的简单、简短和优雅。 -But with all the benefits: +而且还具有下列好处: -* Editor support (completion everywhere!) +* 编辑器支持(处处皆可自动补全!) * Data conversion (a.k.a. parsing / serialization) -* Data validation -* Schema documentation -* Automatic docs +* 数据校验 +* 模式文档 +* 自动生成的文档 From ed63cd779b8180d0c4d9643ebb6c12deec206461 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:29 +0800 Subject: [PATCH 125/163] New translations body-updates.md (Chinese Simplified) --- docs/zh/docs/tutorial/body-updates.md | 96 +++++++++++++-------------- 1 file changed, 47 insertions(+), 49 deletions(-) diff --git a/docs/zh/docs/tutorial/body-updates.md b/docs/zh/docs/tutorial/body-updates.md index 1c1dfea522e7d..e5a080eb6aafb 100644 --- a/docs/zh/docs/tutorial/body-updates.md +++ b/docs/zh/docs/tutorial/body-updates.md @@ -1,34 +1,34 @@ -# Body - Updates +# 请求体 - 更新数据 -## Update replacing with `PUT` +## 用 `PUT` 更新数据 -To update an item you can use the HTTP `PUT` operation. +更新数据请用 HTTP `PUT` 操作。 -You can use the `jsonable_encoder` to convert the input data to data that can be stored as JSON (e.g. with a NoSQL database). For example, converting `datetime` to `str`. +把输入数据转换为以 JSON 格式存储的数据(比如,使用 NoSQL 数据库时),可以使用 `jsonable_encoder`。 例如,把 `datetime` 转换为 `str`。 === "Python 3.10+" ```Python hl_lines="28-33" - {!> ../../../docs_src/body_updates/tutorial001_py310.py!} + !!! note "笔记" ``` === "Python 3.9+" ```Python hl_lines="30-35" - {!> ../../../docs_src/body_updates/tutorial001_py39.py!} + !!! tip "提示" ``` === "Python 3.6+" ```Python hl_lines="30-35" - {!> ../../../docs_src/body_updates/tutorial001.py!} + {!../../../docs_src/body_updates/tutorial001.py!} ``` -`PUT` is used to receive data that should replace the existing data. +`PUT` 用于接收替换现有数据的数据。 ### Warning about replacing -That means that if you want to update the item `bar` using `PUT` with a body containing: +用 `PUT` 把数据项 `bar` 更新为以下内容时: ```Python { @@ -38,39 +38,39 @@ That means that if you want to update the item `bar` using `PUT` with a body con } ``` -because it doesn't include the already stored attribute `"tax": 20.2`, the input model would take the default value of `"tax": 10.5`. +因为上述数据未包含已存储的属性 `"tax": 20.2`,新的输入模型会把 `"tax": 10.5` 作为默认值。 -And the data would be saved with that "new" `tax` of `10.5`. +因此,本次操作把 `tax` 的值「更新」为 `10.5`。 -## Partial updates with `PATCH` +## 用 `PATCH` 进行部分更新 -You can also use the HTTP `PATCH` operation to *partially* update data. +HTTP `PATCH` 操作用于更新 *部分* 数据。 -This means that you can send only the data that you want to update, leaving the rest intact. +即,只发送要更新的数据,其余数据保持不变。 !!! Note `PATCH` is less commonly used and known than `PUT`. - And many teams use only `PUT`, even for partial updates. + 很多人甚至只用 `PUT` 实现部分更新。 - You are **free** to use them however you want, **FastAPI** doesn't impose any restrictions. + **FastAPI** 对此没有任何限制,可以**随意**互换使用这两种操作。 - But this guide shows you, more or less, how they are intended to be used. + 但本指南也会分别介绍这两种操作各自的用途。 -### Using Pydantic's `exclude_unset` parameter +### 使用 Pydantic 的 `exclude_unset` 参数 -If you want to receive partial updates, it's very useful to use the parameter `exclude_unset` in Pydantic's model's `.dict()`. +更新部分数据时,可以在 Pydantic 模型的 `.dict()` 中使用 `exclude_unset` 参数。 -Like `item.dict(exclude_unset=True)`. +比如,`item.dict(exclude_unset=True)`。 -That would generate a `dict` with only the data that was set when creating the `item` model, excluding default values. +这段代码生成的 `dict` 只包含创建 `item` 模型时显式设置的数据,而不包括默认值。 -Then you can use this to generate a `dict` with only the data that was set (sent in the request), omitting default values: +然后再用它生成一个只含已设置(在请求中所发送)数据,且省略了默认值的 `dict`: === "Python 3.10+" ```Python hl_lines="32" - {!> ../../../docs_src/body_updates/tutorial002_py310.py!} + 关于更新数据的警告 ``` === "Python 3.9+" @@ -82,19 +82,19 @@ Then you can use this to generate a `dict` with only the data that was set (sent === "Python 3.6+" ```Python hl_lines="34" - {!> ../../../docs_src/body_updates/tutorial002.py!} + {!../../../docs_src/body_updates/tutorial002.py!} ``` -### Using Pydantic's `update` parameter +### 使用 Pydantic 的 `update` 参数 -Now, you can create a copy of the existing model using `.copy()`, and pass the `update` parameter with a `dict` containing the data to update. +接下来,用 `.copy()` 为已有模型创建调用 `update` 参数的副本,该参数为包含更新数据的 `dict`。 -Like `stored_item_model.copy(update=update_data)`: +例如,`stored_item_model.copy(update=update_data)`: === "Python 3.10+" ```Python hl_lines="33" - {!> ../../../docs_src/body_updates/tutorial002_py310.py!} + !!! Note "笔记" ``` === "Python 3.9+" @@ -106,23 +106,23 @@ Like `stored_item_model.copy(update=update_data)`: === "Python 3.6+" ```Python hl_lines="35" - {!> ../../../docs_src/body_updates/tutorial002.py!} + {!../../../docs_src/body_updates/tutorial002.py!} ``` -### Partial updates recap +### 更新部分数据小结 -In summary, to apply partial updates you would: +简而言之,更新部分数据应: -* (Optionally) use `PATCH` instead of `PUT`. -* Retrieve the stored data. -* Put that data in a Pydantic model. -* Generate a `dict` without default values from the input model (using `exclude_unset`). - * This way you can update only the values actually set by the user, instead of overriding values already stored with default values in your model. -* Create a copy of the stored model, updating it's attributes with the received partial updates (using the `update` parameter). -* Convert the copied model to something that can be stored in your DB (for example, using the `jsonable_encoder`). - * This is comparable to using the model's `.dict()` method again, but it makes sure (and converts) the values to data types that can be converted to JSON, for example, `datetime` to `str`. -* Save the data to your DB. -* Return the updated model. +* 使用 `PATCH` 而不是 `PUT` (可选,也可以用 `PUT`); +* 提取存储的数据; +* 把数据放入 Pydantic 模型; +* 生成不含输入模型默认值的 `dict` (使用 `exclude_unset` 参数); + * 只更新用户设置过的值,不用模型中的默认值覆盖已存储过的值。 +* 为已存储的模型创建副本,用接收的数据更新其属性 (使用 `update` 参数)。 +* 把模型副本转换为可存入数据库的形式(比如,使用 `jsonable_encoder`)。 + * 这种方式与 Pydantic 模型的 `.dict()` 方法类似,但能确保把值转换为适配 JSON 的数据类型,例如, 把 `datetime` 转换为 `str` 。 +* 把数据保存至数据库; +* 返回更新后的模型。 === "Python 3.10+" @@ -139,17 +139,15 @@ In summary, to apply partial updates you would: === "Python 3.6+" ```Python hl_lines="30-37" - {!> ../../../docs_src/body_updates/tutorial002.py!} + {!../../../docs_src/body_updates/tutorial002.py!} ``` -!!! tip - You can actually use this same technique with an HTTP `PUT` operation. +!!! 实际上,HTTP `PUT` 也可以完成相同的操作。 - But the example here uses `PATCH` because it was created for these use cases. + 但本节以 `PATCH` 为例的原因是,该操作就是为了这种用例创建的。 -!!! note - Notice that the input model is still validated. +!!! 注意,输入模型仍需验证。 - So, if you want to receive partial updates that can omit all the attributes, you need to have a model with all the attributes marked as optional (with default values or `None`). + 因此,如果希望接收的部分更新数据可以省略其他所有属性,则要把模型中所有的属性标记为可选(使用默认值或 `None`)。 - To distinguish from the models with all optional values for **updates** and models with required values for **creation**, you can use the ideas described in [Extra Models](extra-models.md){.internal-link target=_blank}. + 为了区分用于**更新**所有可选值的模型与用于**创建**包含必选值的模型,请参照[更多模型](extra-models.md){.internal-link target=_blank} 一节中的思路。 From 859556d9929f7e64b78fb287d4d7af085734dae9 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:30 +0800 Subject: [PATCH 126/163] New translations body.md (Chinese Simplified) --- docs/zh/docs/tutorial/body.md | 132 +++++++++++++++++----------------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/docs/zh/docs/tutorial/body.md b/docs/zh/docs/tutorial/body.md index 9462df2c65f86..7d840d8ade353 100644 --- a/docs/zh/docs/tutorial/body.md +++ b/docs/zh/docs/tutorial/body.md @@ -1,57 +1,59 @@ -# Request Body +# 请求体 -When you need to send data from a client (let's say, a browser) to your API, you send it as a **request body**. +当你需要将数据从客户端(例如浏览器)发送给 API 时,你将其作为「请求体」发送。 -A **request** body is data sent by the client to your API. A **response** body is the data your API sends to the client. +**请求**体是客户端发送给 API 的数据。 **响应**体是 API 发送给客户端的数据。 -Your API almost always has to send a **response** body. But clients don't necessarily need to send **request** bodies all the time. +你的 API 几乎总是要发送**响应**体。 但是客户端并不总是需要发送**请求**体。 -To declare a **request** body, you use Pydantic models with all their power and benefits. +我们使用 Pydantic 模型来声明**请求**体,并能够获得它们所具有的所有能力和优点。 -!!! info - To send data, you should use one of: `POST` (the more common), `PUT`, `DELETE` or `PATCH`. +!!! 要发送数据,你必须使用下列方法之一:`POST`(较常见)、`PUT`、`DELETE` 或 `PATCH`。 Sending a body with a `GET` request has an undefined behavior in the specifications, nevertheless, it is supported by FastAPI, only for very complex/extreme use cases. As it is discouraged, the interactive docs with Swagger UI won't show the documentation for the body when using `GET`, and proxies in the middle might not support it. -## Import Pydantic's `BaseModel` +## 导入 Pydantic 的 `BaseModel` -First, you need to import `BaseModel` from `pydantic`: +首先,你需要从 `pydantic` 中导入 `BaseModel`: === "Python 3.10+" ```Python hl_lines="2" - {!> ../../../docs_src/body/tutorial001_py310.py!} + https://fastapi.tiangolo.com/img/tutorial/body/image03.png ``` === "Python 3.6+" ```Python hl_lines="4" - {!> ../../../docs_src/body/tutorial001.py!} + {!../../../docs_src/body/tutorial001.py!} ``` -## Create your data model +## 创建数据模型 -Then you declare your data model as a class that inherits from `BaseModel`. +然后,将你的数据模型声明为继承自 `BaseModel` 的类。 -Use standard Python types for all the attributes: +使用标准的 Python 类型来声明所有属性: === "Python 3.10+" ```Python hl_lines="5-9" - {!> ../../../docs_src/body/tutorial001_py310.py!} + !!! info + 你不能使用 GET 操作(HTTP 方法)发送请求体。 ``` + 操作(HTTP 方法)发送请求体。 + === "Python 3.6+" ```Python hl_lines="7-11" - {!> ../../../docs_src/body/tutorial001.py!} + {!../../../docs_src/body/tutorial001.py!} ``` -The same as when declaring query parameters, when a model attribute has a default value, it is not required. Otherwise, it is required. Use `None` to make it just optional. +和声明查询参数时一样,当一个模型属性具有默认值时,它不是必需的。 否则它是一个必需属性。 将默认值设为 `None` 可使其成为可选属性。 -For example, this model above declares a JSON "`object`" (or Python `dict`) like: +例如,上面的模型声明了一个这样的 JSON「`object`」(或 Python `dict`): ```JSON { @@ -62,7 +64,7 @@ For example, this model above declares a JSON "`object`" (or Python `dict`) like } ``` -...as `description` and `tax` are optional (with a default value of `None`), this JSON "`object`" would also be valid: +...由于 `description` 和 `tax` 是可选的(它们的默认值为 `None`),下面的 JSON「`object`」也将是有效的: ```JSON { @@ -71,66 +73,66 @@ For example, this model above declares a JSON "`object`" (or Python `dict`) like } ``` -## Declare it as a parameter +## 声明为参数 -To add it to your *path operation*, declare it the same way you declared path and query parameters: +使用与声明路径和查询参数的相同方式声明请求体,即可将其添加到「路径操作」中: === "Python 3.10+" ```Python hl_lines="16" - {!> ../../../docs_src/body/tutorial001_py310.py!} + https://fastapi.tiangolo.com/img/tutorial/body/image05.png ``` === "Python 3.6+" ```Python hl_lines="18" - {!> ../../../docs_src/body/tutorial001.py!} + {!../../../docs_src/body/tutorial001.py!} ``` -...and declare its type as the model you created, `Item`. +...并且将它的类型声明为你创建的 `Item` 模型。 -## Results +## 结果 -With just that Python type declaration, **FastAPI** will: +仅仅使用了 Python 类型声明,**FastAPI** 将会: -* Read the body of the request as JSON. -* Convert the corresponding types (if needed). -* Validate the data. - * If the data is invalid, it will return a nice and clear error, indicating exactly where and what was the incorrect data. -* Give you the received data in the parameter `item`. - * As you declared it in the function to be of type `Item`, you will also have all the editor support (completion, etc) for all of the attributes and their types. -* Generate JSON Schema definitions for your model, you can also use them anywhere else you like if it makes sense for your project. -* Those schemas will be part of the generated OpenAPI schema, and used by the automatic documentation UIs. +* 将请求体作为 JSON 读取。 +* 转换为相应的类型(在需要时)。 +* 校验数据。 + * 如果数据无效,将返回一条清晰易读的错误信息,指出不正确数据的确切位置和内容。 +* 将接收的数据赋值到参数 `item` 中。 + * 由于你已经在函数中将它声明为 `Item` 类型,你还将获得对于所有属性及其类型的一切编辑器支持(代码补全等)。 +* 为你的模型生成 JSON 模式 定义,你还可以在其他任何对你的项目有意义的地方使用它们。 +* 这些模式将成为生成的 OpenAPI 模式的一部分,并且被自动化文档 UI 所使用。 -## Automatic docs +## 自动化文档 -The JSON Schemas of your models will be part of your OpenAPI generated schema, and will be shown in the interactive API docs: +你所定义模型的 JSON 模式将成为生成的 OpenAPI 模式的一部分,并且在交互式 API 文档中展示: -And will be also used in the API docs inside each *path operation* that needs them: +而且还将在每一个需要它们的*路径操作*的 API 文档中使用: -## Editor support +## 编辑器支持 -In your editor, inside your function you will get type hints and completion everywhere (this wouldn't happen if you received a `dict` instead of a Pydantic model): +在你的编辑器中,你会在函数内部的任意地方得到类型提示和代码补全(如果你接收的是一个 `dict` 而不是 Pydantic 模型,则不会发生这种情况): -You also get error checks for incorrect type operations: +你还会获得对不正确的类型操作的错误检查: -This is not by chance, the whole framework was built around that design. +这并非偶然,整个框架都是围绕该设计而构建。 -And it was thoroughly tested at the design phase, before any implementation, to ensure it would work with all the editors. +并且在进行任何实现之前,已经在设计阶段经过了全面测试,以确保它可以在所有的编辑器中生效。 -There were even some changes to Pydantic itself to support this. +Pydantic 本身甚至也进行了一些更改以支持此功能。 -The previous screenshots were taken with Visual Studio Code. +上面的截图取自 Visual Studio Code。 -But you would get the same editor support with PyCharm and most of the other Python editors: +但是在 PyCharm 和绝大多数其他 Python 编辑器中你也会获得同样的编辑器支持: @@ -145,69 +147,69 @@ But you would get the same editor support with ../../../docs_src/body/tutorial002_py310.py!} + https://fastapi.tiangolo.com/img/tutorial/body/image01.png ``` === "Python 3.6+" ```Python hl_lines="21" - {!> ../../../docs_src/body/tutorial002.py!} + {!../../../docs_src/body/tutorial002.py!} ``` -## Request body + path parameters +## 请求体 + 路径参数 -You can declare path parameters and request body at the same time. +你可以同时声明路径参数和请求体。 -**FastAPI** will recognize that the function parameters that match path parameters should be **taken from the path**, and that function parameters that are declared to be Pydantic models should be **taken from the request body**. +**FastAPI** 将识别出与路径参数匹配的函数参数应**从路径中获取**,而声明为 Pydantic 模型的函数参数应**从请求体中获取**。 === "Python 3.10+" ```Python hl_lines="15-16" - {!> ../../../docs_src/body/tutorial003_py310.py!} + https://fastapi.tiangolo.com/img/tutorial/body/image04.png ``` === "Python 3.6+" ```Python hl_lines="17-18" - {!> ../../../docs_src/body/tutorial003.py!} + {!../../../docs_src/body/tutorial003.py!} ``` -## Request body + path + query parameters +## 请求体 + 路径参数 + 查询参数 -You can also declare **body**, **path** and **query** parameters, all at the same time. +你还可以同时声明**请求体**、**路径参数**和**查询参数**。 -**FastAPI** will recognize each of them and take the data from the correct place. +**FastAPI** 会识别它们中的每一个,并从正确的位置获取数据。 === "Python 3.10+" ```Python hl_lines="16" - {!> ../../../docs_src/body/tutorial004_py310.py!} + https://fastapi.tiangolo.com/img/tutorial/body/image02.png ``` === "Python 3.6+" ```Python hl_lines="18" - {!> ../../../docs_src/body/tutorial004.py!} + {!../../../docs_src/body/tutorial004.py!} ``` -The function parameters will be recognized as follows: +函数参数将依次按如下规则进行识别: -* If the parameter is also declared in the **path**, it will be used as a path parameter. -* If the parameter is of a **singular type** (like `int`, `float`, `str`, `bool`, etc) it will be interpreted as a **query** parameter. -* If the parameter is declared to be of the type of a **Pydantic model**, it will be interpreted as a request **body**. +* 如果在**路径**中也声明了该参数,它将被用作路径参数。 +* 如果参数属于**单一类型**(比如 `int`、`float`、`str`、`bool` 等)它将被解释为**查询**参数。 +* 如果参数的类型被声明为一个 **Pydantic 模型**,它将被解释为**请求体**。 !!! note FastAPI will know that the value of `q` is not required because of the default value `= None`. The `Union` in `Union[str, None]` is not used by FastAPI, but will allow your editor to give you better support and detect errors. -## Without Pydantic +## 不使用 Pydantic -If you don't want to use Pydantic models, you can also use **Body** parameters. See the docs for [Body - Multiple Parameters: Singular values in body](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}. +如果你不想使用 Pydantic 模型,你还可以使用 **Body** 参数。 请参阅文档 [请求体 - 多个参数:请求体中的单一值](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}。 From ecaef02de3c1db14f1215376d7e559a5d495bd94 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:31 +0800 Subject: [PATCH 127/163] New translations cookie-params.md (Chinese Simplified) --- docs/zh/docs/tutorial/cookie-params.md | 34 +++++++++++++++----------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/docs/zh/docs/tutorial/cookie-params.md b/docs/zh/docs/tutorial/cookie-params.md index 111e93458e686..5e9a59af732f5 100644 --- a/docs/zh/docs/tutorial/cookie-params.md +++ b/docs/zh/docs/tutorial/cookie-params.md @@ -1,10 +1,10 @@ -# Cookie Parameters +# Cookie 参数 -You can define Cookie parameters the same way you define `Query` and `Path` parameters. +你可以像定义 `Query` 参数和 `Path` 参数一样来定义 `Cookie` 参数。 -## Import `Cookie` +## 导入 `Cookie` -First import `Cookie`: +首先,导入 `Cookie`: === "Python 3.10+" @@ -30,7 +30,7 @@ First import `Cookie`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="1" - {!> ../../../docs_src/cookie_params/tutorial001_py310.py!} + {!../../../docs_src/cookie_params/tutorial001.py!} ``` === "Python 3.6+ non-Annotated" @@ -39,19 +39,22 @@ First import `Cookie`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="3" - {!> ../../../docs_src/cookie_params/tutorial001.py!} + !!! info + 你需要使用 Cookie 来声明 cookie 参数,否则参数将会被解释为查询参数。 ``` + 来声明 cookie 参数,否则参数将会被解释为查询参数。 + -## Declare `Cookie` parameters +## 声明 `Cookie` 参数 -Then declare the cookie parameters using the same structure as with `Path` and `Query`. +声明 `Cookie` 参数的结构与声明 `Query` 参数和 `Path` 参数时相同。 -The first value is the default value, you can pass all the extra validation or annotation parameters: +第一个值是参数的默认值,同时也可以传递所有验证参数或注释参数,来校验参数: === "Python 3.10+" ```Python hl_lines="9" - {!> ../../../docs_src/cookie_params/tutorial001_an_py310.py!} + 总结 ``` === "Python 3.9+" @@ -63,7 +66,7 @@ The first value is the default value, you can pass all the extra validation or a === "Python 3.6+" ```Python hl_lines="10" - {!> ../../../docs_src/cookie_params/tutorial001_an.py!} + {!../../../docs_src/cookie_params/tutorial001.py!} ``` === "Python 3.10+ non-Annotated" @@ -81,17 +84,20 @@ The first value is the default value, you can pass all the extra validation or a Prefer to use the `Annotated` version if possible. ```Python hl_lines="9" - {!> ../../../docs_src/cookie_params/tutorial001.py!} + !!! note "技术细节" + CookiePathQuery是兄弟类,它们都继承自公共的 Param 类 ``` + 、Path 、Query是兄弟类,它们都继承自公共的 Param 类 + !!! note "Technical Details" `Cookie` is a "sister" class of `Path` and `Query`. It also inherits from the same common `Param` class. - But remember that when you import `Query`, `Path`, `Cookie` and others from `fastapi`, those are actually functions that return special classes. + 但请记住,当你从 `fastapi` 导入的 `Query`、`Path`、`Cookie` 或其他参数声明函数,这些实际上是返回特殊类的函数。 !!! info To declare cookies, you need to use `Cookie`, because otherwise the parameters would be interpreted as query parameters. ## Recap -Declare cookies with `Cookie`, using the same common pattern as `Query` and `Path`. +使用 `Cookie` 声明 cookie 参数,使用方式与 `Query` 和 `Path` 类似。 From a396bd319da0c3167bcf4c77eaf4a5443ca3b205 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:32 +0800 Subject: [PATCH 128/163] New translations cors.md (Chinese Simplified) --- docs/zh/docs/tutorial/cors.md | 86 +++++++++++++++++------------------ 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/docs/zh/docs/tutorial/cors.md b/docs/zh/docs/tutorial/cors.md index 33b11983b32b5..171989cf1da18 100644 --- a/docs/zh/docs/tutorial/cors.md +++ b/docs/zh/docs/tutorial/cors.md @@ -1,84 +1,84 @@ -# CORS (Cross-Origin Resource Sharing) +# CORS(跨域资源共享) -CORS or "Cross-Origin Resource Sharing" refers to the situations when a frontend running in a browser has JavaScript code that communicates with a backend, and the backend is in a different "origin" than the frontend. +CORS 或者「跨域资源共享」 指浏览器中运行的前端拥有与后端通信的 JavaScript 代码,而后端处于与前端不同的「源」的情况。 -## Origin +## 源 -An origin is the combination of protocol (`http`, `https`), domain (`myapp.com`, `localhost`, `localhost.tiangolo.com`), and port (`80`, `443`, `8080`). +源是协议(`http`,`https`)、域(`myapp.com`,`localhost`,`localhost.tiangolo.com`)以及端口(`80`、`443`、`8080`)的组合。 -So, all these are different origins: +因此,这些都是不同的源: * `http://localhost` * `https://localhost` * `http://localhost:8080` -Even if they are all in `localhost`, they use different protocols or ports, so, they are different "origins". +即使它们都在 `localhost` 中,但是它们使用不同的协议或者端口,所以它们都是不同的「源」。 -## Steps +## 步骤 -So, let's say you have a frontend running in your browser at `http://localhost:8080`, and its JavaScript is trying to communicate with a backend running at `http://localhost` (because we don't specify a port, the browser will assume the default port `80`). +假设你的浏览器中有一个前端运行在 `http://localhost:8080`,并且它的 JavaScript 正在尝试与运行在 `http://localhost` 的后端通信(因为我们没有指定端口,浏览器会采用默认的端口 `80`)。 -Then, the browser will send an HTTP `OPTIONS` request to the backend, and if the backend sends the appropriate headers authorizing the communication from this different origin (`http://localhost:8080`) then the browser will let the JavaScript in the frontend send its request to the backend. +然后,浏览器会向后端发送一个 HTTP `OPTIONS` 请求,如果后端发送适当的 headers 来授权来自这个不同源(`http://localhost:8080`)的通信,浏览器将允许前端的 JavaScript 向后端发送请求。 -To achieve this, the backend must have a list of "allowed origins". +为此,后端必须有一个「允许的源」列表。 -In this case, it would have to include `http://localhost:8080` for the frontend to work correctly. +在这种情况下,它必须包含 `http://localhost:8080`,前端才能正常工作。 ## Wildcards -It's also possible to declare the list as `"*"` (a "wildcard") to say that all are allowed. +也可以使用 `"*"`(一个「通配符」)声明这个列表,表示全部都是允许的。 -But that will only allow certain types of communication, excluding everything that involves credentials: Cookies, Authorization headers like those used with Bearer Tokens, etc. +但这仅允许某些类型的通信,不包括所有涉及凭据的内容:像 Cookies 以及那些使用 Bearer 令牌的授权 headers 等。 -So, for everything to work correctly, it's better to specify explicitly the allowed origins. +因此,为了一切都能正常工作,最好显式地指定允许的源。 -## Use `CORSMiddleware` +## 使用 `CORSMiddleware` -You can configure it in your **FastAPI** application using the `CORSMiddleware`. +你可以在 **FastAPI** 应用中使用 `CORSMiddleware` 来配置它。 -* Import `CORSMiddleware`. -* Create a list of allowed origins (as strings). -* Add it as a "middleware" to your **FastAPI** application. +* 导入 `CORSMiddleware`。 +* 创建一个允许的源列表(由字符串组成)。 +* 将其作为「中间件」添加到你的 **FastAPI** 应用中。 -You can also specify if your backend allows: +你也可以指定后端是否允许: -* Credentials (Authorization headers, Cookies, etc). -* Specific HTTP methods (`POST`, `PUT`) or all of them with the wildcard `"*"`. -* Specific HTTP headers or all of them with the wildcard `"*"`. +* 凭证(授权 headers,Cookies 等)。 +* 特定的 HTTP 方法(`POST`,`PUT`)或者使用通配符 `"*"` 允许所有方法。 +* 特定的 HTTP headers 或者使用通配符 `"*"` 允许所有 headers。 ```Python hl_lines="2 6-11 13-19" {!../../../docs_src/cors/tutorial001.py!} ``` -The default parameters used by the `CORSMiddleware` implementation are restrictive by default, so you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be permitted to use them in a Cross-Domain context. +默认情况下,这个 `CORSMiddleware` 实现所使用的默认参数较为保守,所以你需要显式地启用特定的源、方法或者 headers,以便浏览器能够在跨域上下文中使用它们。 -The following arguments are supported: +支持以下参数: -* `allow_origins` - A list of origins that should be permitted to make cross-origin requests. E.g. `['https://example.org', 'https://www.example.org']`. You can use `['*']` to allow any origin. -* `allow_origin_regex` - A regex string to match against origins that should be permitted to make cross-origin requests. e.g. `'https://.*\.example\.org'`. -* `allow_methods` - A list of HTTP methods that should be allowed for cross-origin requests. Defaults to `['GET']`. You can use `['*']` to allow all standard methods. -* `allow_headers` - A list of HTTP request headers that should be supported for cross-origin requests. Defaults to `[]`. You can use `['*']` to allow all headers. The `Accept`, `Accept-Language`, `Content-Language` and `Content-Type` headers are always allowed for simple CORS requests. -* `allow_credentials` - Indicate that cookies should be supported for cross-origin requests. Defaults to `False`. Also, `allow_origins` cannot be set to `['*']` for credentials to be allowed, origins must be specified. -* `expose_headers` - Indicate any response headers that should be made accessible to the browser. Defaults to `[]`. -* `max_age` - Sets a maximum time in seconds for browsers to cache CORS responses. Defaults to `600`. +* `allow_origins` - 一个允许跨域请求的源列表。 E.g. 例如 `['https://example.org', 'https://www.example.org']`。 你可以使用 `['*']` 允许任何源。 +* `allow_origin_regex` - 一个正则表达式字符串,匹配的源允许跨域请求。 例如 `'https://.*\.example\.org'`。 +* `allow_methods` - 一个允许跨域请求的 HTTP 方法列表。 默认为 `['GET']`。 你可以使用 `['*']` 来允许所有标准方法。 +* `allow_headers` - 一个允许跨域请求的 HTTP 请求头列表。 默认为 `[]`。 你可以使用 `['*']` 允许所有的请求头。 `Accept`、`Accept-Language`、`Content-Language` 以及 `Content-Type` 请求头总是允许 CORS 请求。 +* `allow_credentials` - 指示跨域请求支持 cookies。 默认是 `False`。 另外,允许凭证时 `allow_origins` 不能设定为 `['*']`,必须指定源。 +* `expose_headers` - 指示可以被浏览器访问的响应头。 默认为 `[]`。 +* `max_age` - 设定浏览器缓存 CORS 响应的最长时间,单位是秒。 默认为 `600`。 -The middleware responds to two particular types of HTTP request... +中间件响应两种特定类型的 HTTP 请求…… -### CORS preflight requests +### CORS 预检请求 -These are any `OPTIONS` request with `Origin` and `Access-Control-Request-Method` headers. +这是些带有 `Origin` 和 `Access-Control-Request-Method` 请求头的 `OPTIONS` 请求。 -In this case the middleware will intercept the incoming request and respond with appropriate CORS headers, and either a `200` or `400` response for informational purposes. +在这种情况下,中间件将拦截传入的请求并进行响应,出于提供信息的目的返回一个使用了适当的 CORS headers 的 `200` 或 `400` 响应。 -### Simple requests +### 简单请求 -Any request with an `Origin` header. In this case the middleware will pass the request through as normal, but will include appropriate CORS headers on the response. +任何带有 `Origin` 请求头的请求。 在这种情况下,中间件将像平常一样传递请求,但是在响应中包含适当的 CORS headers。 -## More info +## 更多信息 -For more info about CORS, check the Mozilla CORS documentation. +更多关于 CORS 的信息,请查看 Mozilla CORS 文档。 -!!! note "Technical Details" - You could also use `from starlette.middleware.cors import CORSMiddleware`. +!!! !!! note "技术细节" + 你也可以使用 `from starlette.middleware.cors import CORSMiddleware`。 - **FastAPI** provides several middlewares in `fastapi.middleware` just as a convenience for you, the developer. But most of the available middlewares come directly from Starlette. + 出于方便,**FastAPI** 在 `fastapi.middleware` 中为开发者提供了几个中间件。 但是大多数可用的中间件都是直接来自 Starlette。 From 6193f9e28efcba9b45dbfe97953d12b20f564158 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:33 +0800 Subject: [PATCH 129/163] New translations debugging.md (Chinese Simplified) --- docs/zh/docs/tutorial/debugging.md | 58 +++++++++++++++--------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/docs/zh/docs/tutorial/debugging.md b/docs/zh/docs/tutorial/debugging.md index 8d76347cffcba..10a7a31a05296 100644 --- a/docs/zh/docs/tutorial/debugging.md +++ b/docs/zh/docs/tutorial/debugging.md @@ -1,18 +1,18 @@ # Debugging -You can connect the debugger in your editor, for example with Visual Studio Code or PyCharm. +你可以在编辑器中连接调试器,例如使用 Visual Studio Code 或 PyCharm。 -## Call `uvicorn` +## 调用 `uvicorn` -In your FastAPI application, import and run `uvicorn` directly: +在你的 FastAPI 应用中直接导入 `uvicorn` 并运行: ```Python hl_lines="1 15" {!../../../docs_src/debugging/tutorial001.py!} ``` -### About `__name__ == "__main__"` +### 关于 `__name__ == "__main__"` -The main purpose of the `__name__ == "__main__"` is to have some code that is executed when your file is called with: +`__name__ == "__main__"` 的主要目的是使用以下代码调用文件时执行一些代码:
@@ -22,17 +22,17 @@ $ python myapp.py
-but is not called when another file imports it, like in: +而当其它文件导入它时并不会被调用,像这样: ```Python from myapp import app ``` -#### More details +#### 更多细节 -Let's say your file is named `myapp.py`. +假设你的文件命名为 `myapp.py`。 -If you run it with: +如果你这样运行:
@@ -42,7 +42,7 @@ $ python myapp.py
-then the internal variable `__name__` in your file, created automatically by Python, will have as value the string `"__main__"`. +那么文件中由 Python 自动创建的内部变量 `__name__`,会将字符串 `"__main__"` 作为值。 So, the section: @@ -54,9 +54,9 @@ will run. --- -This won't happen if you import that module (file). +如果你是导入这个模块(文件)就不会这样。 -So, if you have another file `importer.py` with: +因此,如果你的另一个文件 `importer.py` 像这样: ```Python from myapp import app @@ -64,7 +64,7 @@ from myapp import app # Some more code ``` -in that case, the automatically created variable inside of `myapp.py` will not have the variable `__name__` with a value of `"__main__"`. +在这种情况下,`myapp.py` 内部的自动变量不会有值为 `"__main__"` 的变量 `__name__`。 So, the line: @@ -74,39 +74,39 @@ So, the line: will not be executed. -!!! info - For more information, check the official Python docs. +!!! !!! info + 更多信息请检查 Python 官方文档. -## Run your code with your debugger +## 使用你的调试器运行代码 -Because you are running the Uvicorn server directly from your code, you can call your Python program (your FastAPI application) directly from the debugger. +由于是从代码直接运行的 Uvicorn 服务器,所以你可以从调试器直接调用 Python 程序(你的 FastAPI 应用)。 --- -For example, in Visual Studio Code, you can: +例如,你可以在 Visual Studio Code 中: * Go to the "Debug" panel. -* "Add configuration...". -* Select "Python" -* Run the debugger with the option "`Python: Current File (Integrated Terminal)`". +* 「添加配置...」。 +* 选中「Python」 +* 运行「Python:当前文件(集成终端)」选项的调试器。 -It will then start the server with your **FastAPI** code, stop at your breakpoints, etc. +然后它会使用你的 **FastAPI** 代码开启服务器,停在断点处,等等。 -Here's how it might look: +看起来可能是这样: --- -If you use Pycharm, you can: +如果使用 Pycharm,你可以: -* Open the "Run" menu. +* 打开「运行」菜单。 * Select the option "Debug...". -* Then a context menu shows up. -* Select the file to debug (in this case, `main.py`). +* 然后出现一个上下文菜单。 +* 选择要调试的文件(本例中的 `main.py`)。 -It will then start the server with your **FastAPI** code, stop at your breakpoints, etc. +然后它会使用你的 **FastAPI** 代码开启服务器,停在断点处,等等。 -Here's how it might look: +看起来可能是这样: From 06f45ba92af687ca6a772ef3748f936d5d5076be Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:34 +0800 Subject: [PATCH 130/163] New translations classes-as-dependencies.md (Chinese Simplified) --- .../dependencies/classes-as-dependencies.md | 119 +++++++++--------- 1 file changed, 60 insertions(+), 59 deletions(-) diff --git a/docs/zh/docs/tutorial/dependencies/classes-as-dependencies.md b/docs/zh/docs/tutorial/dependencies/classes-as-dependencies.md index a1409c8f88701..7da62b650bca0 100644 --- a/docs/zh/docs/tutorial/dependencies/classes-as-dependencies.md +++ b/docs/zh/docs/tutorial/dependencies/classes-as-dependencies.md @@ -1,10 +1,10 @@ -# Classes as Dependencies +# 类作为依赖项 -Before diving deeper into the **Dependency Injection** system, let's upgrade the previous example. +在深入探究 **依赖注入** 系统之前,让我们升级之前的例子。 -## A `dict` from the previous example +## 来自前一个例子的`dict` -In the previous example, we were returning a `dict` from our dependency ("dependable"): +在前面的例子中, 我们从依赖项 ("可依赖对象") 中返回了一个 `dict`: === "Python 3.10+" @@ -42,29 +42,29 @@ In the previous example, we were returning a `dict` from our dependency ("depend {!> ../../../docs_src/dependencies/tutorial001.py!} ``` -But then we get a `dict` in the parameter `commons` of the *path operation function*. +但是后面我们在路径操作函数的参数 `commons` 中得到了一个 `dict`。 -And we know that editors can't provide a lot of support (like completion) for `dict`s, because they can't know their keys and value types. +我们知道编辑器不能为 `dict` 提供很多支持(比如补全),因为编辑器不知道 `dict` 的键和值类型。 -We can do better... +对此,我们可以做的更好... -## What makes a dependency +## 什么构成了依赖项? -Up to now you have seen dependencies declared as functions. +到目前为止,您看到的依赖项都被声明为函数。 -But that's not the only way to declare dependencies (although it would probably be the more common). +但这并不是声明依赖项的唯一方法(尽管它可能是更常见的方法)。 The key factor is that a dependency should be a "callable". -A "**callable**" in Python is anything that Python can "call" like a function. +Python 中的 "**可调用对象**" 是指任何 Python 可以像函数一样 "调用" 的对象。 -So, if you have an object `something` (that might _not_ be a function) and you can "call" it (execute it) like: +所以,如果你有一个对象 `something` (可能*不是*一个函数),你可以 "调用" 它(执行它),就像: ```Python something() ``` -or +或者 ```Python something(some_argument, some_keyword_argument="foo") @@ -72,11 +72,11 @@ something(some_argument, some_keyword_argument="foo") then it is a "callable". -## Classes as dependencies +## 类作为依赖项 -You might notice that to create an instance of a Python class, you use that same syntax. +您可能会注意到,要创建一个 Python 类的实例,您可以使用相同的语法。 -For example: +举个例子: ```Python class Cat: @@ -87,21 +87,21 @@ class Cat: fluffy = Cat(name="Mr Fluffy") ``` -In this case, `fluffy` is an instance of the class `Cat`. +在这个例子中, `fluffy` 是一个 `Cat` 类的实例。 -And to create `fluffy`, you are "calling" `Cat`. +为了创建 `fluffy`,你调用了 `Cat` 。 -So, a Python class is also a **callable**. +所以,Python 类也是 **可调用对象**。 -Then, in **FastAPI**, you could use a Python class as a dependency. +因此,在 **FastAPI** 中,你可以使用一个 Python 类作为一个依赖项。 -What FastAPI actually checks is that it is a "callable" (function, class or anything else) and the parameters defined. +实际上 FastAPI 检查的是它是一个 "可调用对象"(函数,类或其他任何类型)以及定义的参数。 -If you pass a "callable" as a dependency in **FastAPI**, it will analyze the parameters for that "callable", and process them in the same way as the parameters for a *path operation function*. Including sub-dependencies. +如果您在 **FastAPI** 中传递一个 "可调用对象" 作为依赖项,它将分析该 "可调用对象" 的参数,并以处理路径操作函数的参数的方式来处理它们。 包括子依赖项。 -That also applies to callables with no parameters at all. The same as it would be for *path operation functions* with no parameters. +这也适用于完全没有参数的可调用对象。 这与不带参数的路径操作函数一样。 -Then, we can change the dependency "dependable" `common_parameters` from above to the class `CommonQueryParams`: +所以,我们可以将上面的依赖项 "可依赖对象" `common_parameters` 更改为类 `CommonQueryParams`: === "Python 3.10+" @@ -139,7 +139,7 @@ Then, we can change the dependency "dependable" `common_parameters` from above t {!> ../../../docs_src/dependencies/tutorial002.py!} ``` -Pay attention to the `__init__` method used to create the instance of the class: +注意用于创建类实例的 `__init__` 方法: === "Python 3.10+" @@ -177,7 +177,7 @@ Pay attention to the `__init__` method used to create the instance of the class: {!> ../../../docs_src/dependencies/tutorial002.py!} ``` -...it has the same parameters as our previous `common_parameters`: +...它与我们以前的 `common_parameters` 具有相同的参数: === "Python 3.10+" @@ -215,19 +215,19 @@ Pay attention to the `__init__` method used to create the instance of the class: {!> ../../../docs_src/dependencies/tutorial001.py!} ``` -Those parameters are what **FastAPI** will use to "solve" the dependency. +这些参数就是 **FastAPI** 用来 "处理" 依赖项的。 -In both cases, it will have: +在两个例子下,都有: -* An optional `q` query parameter that is a `str`. -* A `skip` query parameter that is an `int`, with a default of `0`. -* A `limit` query parameter that is an `int`, with a default of `100`. +* 一个可选的 `q` 查询参数,是 `str` 类型。 +* 一个 `skip` 查询参数,是 `int` 类型,默认值为 `0`。 +* 一个 `limit` 查询参数,是 `int` 类型,默认值为 `100`。 -In both cases the data will be converted, validated, documented on the OpenAPI schema, etc. +在两个例子下,数据都将被转换、验证、在 OpenAPI schema 上文档化,等等。 -## Use it +## 使用它 -Now you can declare your dependency using this class. +现在,您可以使用这个类来声明你的依赖项了。 === "Python 3.10+" @@ -265,11 +265,11 @@ Now you can declare your dependency using this class. {!> ../../../docs_src/dependencies/tutorial002.py!} ``` -**FastAPI** calls the `CommonQueryParams` class. This creates an "instance" of that class and the instance will be passed as the parameter `commons` to your function. +**FastAPI** 调用 `CommonQueryParams` 类。 这将创建该类的一个 "实例",该实例将作为参数 `commons` 被传递给你的函数。 -## Type annotation vs `Depends` +## 类型注解 vs `Depends` -Notice how we write `CommonQueryParams` twice in the above code: +注意,我们在上面的代码中编写了两次`CommonQueryParams`: === "Python 3.6+ non-Annotated" @@ -283,27 +283,27 @@ Notice how we write `CommonQueryParams` twice in the above code: === "Python 3.6+" ```Python - commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] + ... = Depends(CommonQueryParams) ``` -The last `CommonQueryParams`, in: +最后的 `CommonQueryParams`: ```Python ... Depends(CommonQueryParams) ``` -...is what **FastAPI** will actually use to know what is the dependency. +...实际上是 **Fastapi** 用来知道依赖项是什么的。 -From it is that FastAPI will extract the declared parameters and that is what FastAPI will actually call. +FastAPI 将从依赖项中提取声明的参数,这才是 FastAPI 实际调用的。 --- -In this case, the first `CommonQueryParams`, in: +在本例中,第一个 `CommonQueryParams` : === "Python 3.6+" ```Python - commons: Annotated[CommonQueryParams, ... + 这就是 "可调用对象"。 ``` === "Python 3.6+ non-Annotated" @@ -315,14 +315,15 @@ In this case, the first `CommonQueryParams`, in: commons: CommonQueryParams ... ``` -...doesn't have any special meaning for **FastAPI**. FastAPI won't use it for data conversion, validation, etc. (as it is using the `Depends(CommonQueryParams)` for that). +...对于 **FastAPI** 没有任何特殊的意义。 FastAPI 不会使用它进行数据转换、验证等 (因为对于这,它使用 `= Depends(CommonQueryParams)`)。 -You could actually write just: +你实际上可以只这样编写: === "Python 3.6+" ```Python - commons: Annotated[Any, Depends(CommonQueryParams)] + !!! tip + 如果这看起来更加混乱而不是更加有帮助,那么请忽略它,你不需要它。 ``` === "Python 3.6+ non-Annotated" @@ -334,7 +335,7 @@ You could actually write just: commons = Depends(CommonQueryParams) ``` -..as in: +..就像: === "Python 3.10+" @@ -372,13 +373,13 @@ You could actually write just: {!> ../../../docs_src/dependencies/tutorial003.py!} ``` -But declaring the type is encouraged as that way your editor will know what will be passed as the parameter `commons`, and then it can help you with code completion, type checks, etc: +但是声明类型是被鼓励的,因为那样你的编辑器就会知道将传递什么作为参数 `commons` ,然后它可以帮助你完成代码,类型检查,等等: -## Shortcut +## 快捷方式 -But you see that we are having some code repetition here, writing `CommonQueryParams` twice: +但是您可以看到,我们在这里有一些代码重复了,编写了`CommonQueryParams`两次: === "Python 3.6+ non-Annotated" @@ -395,11 +396,11 @@ But you see that we are having some code repetition here, writing `CommonQueryPa commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] ``` -**FastAPI** provides a shortcut for these cases, in where the dependency is *specifically* a class that **FastAPI** will "call" to create an instance of the class itself. +**FastAPI** 为这些情况提供了一个快捷方式,在这些情况下,依赖项 *明确地* 是一个类,**FastAPI** 将 "调用" 它来创建类本身的一个实例。 -For those specific cases, you can do the following: +对于这些特定的情况,您可以跟随以下操作: -Instead of writing: +不是写成这样: === "Python 3.6+" @@ -416,12 +417,12 @@ Instead of writing: commons: CommonQueryParams = Depends(CommonQueryParams) ``` -...you write: +...而是这样写: === "Python 3.6+" ```Python - commons: Annotated[CommonQueryParams, Depends()] + 关键因素是依赖项应该是 "可调用对象"。 ``` === "Python 3.6 non-Annotated" @@ -433,9 +434,9 @@ Instead of writing: commons: CommonQueryParams = Depends() ``` -You declare the dependency as the type of the parameter, and you use `Depends()` without any parameter, instead of having to write the full class *again* inside of `Depends(CommonQueryParams)`. +您声明依赖项作为参数的类型,并使用 `Depends()` 作为该函数的参数的 "默认" 值(在 `=` 之后),而在 `Depends()` 中没有任何参数,而不是在 `Depends(CommonQueryParams)` 编写完整的类。 -The same example would then look like: +同样的例子看起来像这样: === "Python 3.10+" @@ -473,9 +474,9 @@ The same example would then look like: {!> ../../../docs_src/dependencies/tutorial004.py!} ``` -...and **FastAPI** will know what to do. +... **FastAPI** 会知道怎么处理。 !!! tip If that seems more confusing than helpful, disregard it, you don't *need* it. - It is just a shortcut. Because **FastAPI** cares about helping you minimize code repetition. + 这只是一个快捷方式。 因为 **FastAPI** 关心的是帮助您减少代码重复。 From f5f32f9d9c63aa204d4c74eb23a639b455f2199a Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:35 +0800 Subject: [PATCH 131/163] New translations dependencies-in-path-operation-decorators.md (Chinese Simplified) --- ...pendencies-in-path-operation-decorators.md | 66 +++++++++---------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/docs/zh/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md b/docs/zh/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md index 00a90424934af..f7961ad050a95 100644 --- a/docs/zh/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md +++ b/docs/zh/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md @@ -1,18 +1,18 @@ -# Dependencies in path operation decorators +# 路径操作装饰器依赖项 -In some cases you don't really need the return value of a dependency inside your *path operation function*. +有时,我们并不需要在*路径操作函数*中使用依赖项的返回值。 -Or the dependency doesn't return a value. +或者说,有些依赖项不返回值。 But you still need it to be executed/solved. -For those cases, instead of declaring a *path operation function* parameter with `Depends`, you can add a `list` of `dependencies` to the *path operation decorator*. +对于这种情况,不必在声明*路径操作函数*的参数时使用 `Depends`,而是可以在*路径操作装饰器*中添加一个由 `dependencies` 组成的 `list`。 -## Add `dependencies` to the *path operation decorator* +## 在*路径操作装饰器*中添加 `dependencies` 参数 -The *path operation decorator* receives an optional argument `dependencies`. +*路径操作装饰器*支持可选参数 ~ `dependencies`。 -It should be a `list` of `Depends()`: +该参数的值是由 `Depends()` 组成的 `list`: === "Python 3.9+" @@ -23,7 +23,7 @@ It should be a `list` of `Depends()`: === "Python 3.6+" ```Python hl_lines="18" - {!> ../../../docs_src/dependencies/tutorial006_an.py!} + !!! tip "提示" ``` === "Python 3.6 non-Annotated" @@ -32,30 +32,28 @@ It should be a `list` of `Depends()`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="17" - {!> ../../../docs_src/dependencies/tutorial006.py!} + {!../../../docs_src/dependencies/tutorial006.py!} ``` These dependencies will be executed/solved the same way normal dependencies. But their value (if they return any) won't be passed to your *path operation function*. -!!! tip - Some editors check for unused function parameters, and show them as errors. +!!! 有些编辑器会检查代码中没使用过的函数参数,并显示错误提示。 - Using these `dependencies` in the *path operation decorator* you can make sure they are executed while avoiding editor/tooling errors. + 在*路径操作装饰器*中使用 `dependencies` 参数,可以确保在执行依赖项的同时,避免编辑器显示错误提示。 It might also help avoid confusion for new developers that see an unused parameter in your code and could think it's unnecessary. -!!! info - In this example we use invented custom headers `X-Key` and `X-Token`. +!!! 本例中,使用的是自定义响应头 `X-Key` 和 `X-Token`。 - But in real cases, when implementing security, you would get more benefits from using the integrated [Security utilities (the next chapter)](../security/index.md){.internal-link target=_blank}. + 但实际开发中,尤其是在实现安全措施时,最好使用 FastAPI 内置的[安全工具](../security/index.md){.internal-link target=_blank}(详见下一章)。 -## Dependencies errors and return values +## 依赖项错误和返回值 -You can use the same dependency *functions* you use normally. +路径装饰器依赖项也可以使用普通的依赖项*函数*。 -### Dependency requirements +### 依赖项的需求项 -They can declare request requirements (like headers) or other sub-dependencies: +路径装饰器依赖项可以声明请求的需求项(比如响应头)或其他子依赖项: === "Python 3.9+" @@ -66,7 +64,7 @@ They can declare request requirements (like headers) or other sub-dependencies: === "Python 3.6+" ```Python hl_lines="7 12" - {!> ../../../docs_src/dependencies/tutorial006_an.py!} + !!! info "说明" ``` === "Python 3.6 non-Annotated" @@ -75,23 +73,23 @@ They can declare request requirements (like headers) or other sub-dependencies: Prefer to use the `Annotated` version if possible. ```Python hl_lines="6 11" - {!> ../../../docs_src/dependencies/tutorial006.py!} + {!../../../docs_src/dependencies/tutorial006.py!} ``` ### Raise exceptions -These dependencies can `raise` exceptions, the same as normal dependencies: +路径装饰器依赖项与正常的依赖项一样,可以 `raise` 异常: === "Python 3.9+" ```Python hl_lines="10 15" - {!> ../../../docs_src/dependencies/tutorial006_an_py39.py!} + 但仍要执行或解析该依赖项。 ``` === "Python 3.6+" ```Python hl_lines="9 14" - {!> ../../../docs_src/dependencies/tutorial006_an.py!} + 无论路径装饰器依赖项是否返回值,路径操作都不会使用这些值。 ``` === "Python 3.6 non-Annotated" @@ -100,25 +98,25 @@ These dependencies can `raise` exceptions, the same as normal dependencies: Prefer to use the `Annotated` version if possible. ```Python hl_lines="8 13" - {!> ../../../docs_src/dependencies/tutorial006.py!} + {!../../../docs_src/dependencies/tutorial006.py!} ``` -### Return values +### 返回值 And they can return values or not, the values won't be used. -So, you can re-use a normal dependency (that returns a value) you already use somewhere else, and even though the value won't be used, the dependency will be executed: +因此,可以复用在其他位置使用过的、(能返回值的)普通依赖项,即使没有使用这个值,也会执行该依赖项: === "Python 3.9+" ```Python hl_lines="11 16" - {!> ../../../docs_src/dependencies/tutorial006_an_py39.py!} + 路径操作装饰器依赖项(以下简称为“路径装饰器依赖项”)的执行或解析方式和普通依赖项一样,但就算这些依赖项会返回值,它们的值也不会传递给路径操作函数。 ``` === "Python 3.6+" ```Python hl_lines="10 15" - {!> ../../../docs_src/dependencies/tutorial006_an.py!} + 触发异常 ``` === "Python 3.6 non-Annotated" @@ -127,13 +125,13 @@ So, you can re-use a normal dependency (that returns a value) you already use so Prefer to use the `Annotated` version if possible. ```Python hl_lines="9 14" - {!> ../../../docs_src/dependencies/tutorial006.py!} + {!../../../docs_src/dependencies/tutorial006.py!} ``` -## Dependencies for a group of *path operations* +## 为一组路径操作定义依赖项 -Later, when reading about how to structure bigger applications ([Bigger Applications - Multiple Files](../../tutorial/bigger-applications.md){.internal-link target=_blank}), possibly with multiple files, you will learn how to declare a single `dependencies` parameter for a group of *path operations*. +稍后,[大型应用 - 多文件](../../tutorial/bigger-applications.md){.internal-link target=\_blank}一章中会介绍如何使用多个文件创建大型应用程序,在这一章中,您将了解到如何为一组*路径操作*声明单个 `dependencies` 参数。 -## Global Dependencies +## 全局依赖项 -Next we will see how to add dependencies to the whole `FastAPI` application, so that they apply to each *path operation*. +接下来,我们将学习如何为 `FastAPI` 应用程序添加全局依赖项,创建应用于每个*路径操作*的依赖项。 From 993ae5bd944cf277fdc9f6290d1aee100486bc71 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:36 +0800 Subject: [PATCH 132/163] New translations global-dependencies.md (Chinese Simplified) --- .../tutorial/dependencies/global-dependencies.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/zh/docs/tutorial/dependencies/global-dependencies.md b/docs/zh/docs/tutorial/dependencies/global-dependencies.md index 0989b31d46205..33e768bbe2f9d 100644 --- a/docs/zh/docs/tutorial/dependencies/global-dependencies.md +++ b/docs/zh/docs/tutorial/dependencies/global-dependencies.md @@ -1,21 +1,21 @@ -# Global Dependencies +# 全局依赖项 For some types of applications you might want to add dependencies to the whole application. -Similar to the way you can [add `dependencies` to the *path operation decorators*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, you can add them to the `FastAPI` application. +通过与定义[*路径装饰器依赖项*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} 类似的方式,可以把依赖项添加至整个 `FastAPI` 应用。 -In that case, they will be applied to all the *path operations* in the application: +这样一来,就可以为所有*路径操作*应用该依赖项: === "Python 3.9+" ```Python hl_lines="16" - {!> ../../../docs_src/dependencies/tutorial012_an_py39.py!} + 有时,我们要为整个应用添加依赖项。 ``` === "Python 3.6+" ```Python hl_lines="16" - {!> ../../../docs_src/dependencies/tutorial012_an.py!} + 为一组路径操作定义依赖项 ``` === "Python 3.6 non-Annotated" @@ -24,11 +24,11 @@ In that case, they will be applied to all the *path operations* in the applicati Prefer to use the `Annotated` version if possible. ```Python hl_lines="15" - {!> ../../../docs_src/dependencies/tutorial012.py!} + {!../../../docs_src/dependencies/tutorial012.py!} ``` -And all the ideas in the section about [adding `dependencies` to the *path operation decorators*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} still apply, but in this case, to all of the *path operations* in the app. +[*路径装饰器依赖项*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} 一章的思路均适用于全局依赖项, 在本例中,这些依赖项可以用于应用中的所有*路径操作*。 ## Dependencies for groups of *path operations* -Later, when reading about how to structure bigger applications ([Bigger Applications - Multiple Files](../../tutorial/bigger-applications.md){.internal-link target=_blank}), possibly with multiple files, you will learn how to declare a single `dependencies` parameter for a group of *path operations*. +稍后,[大型应用 - 多文件](../../tutorial/bigger-applications.md){.internal-link target=_blank}一章中会介绍如何使用多个文件创建大型应用程序,在这一章中,您将了解到如何为一组*路径操作*声明单个 `dependencies` 参数。 From a080f090542842631bdfa212f11e110588eb2f5e Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:38 +0800 Subject: [PATCH 133/163] New translations index.md (Chinese Simplified) --- docs/zh/docs/tutorial/dependencies/index.md | 185 ++++++++++---------- 1 file changed, 92 insertions(+), 93 deletions(-) diff --git a/docs/zh/docs/tutorial/dependencies/index.md b/docs/zh/docs/tutorial/dependencies/index.md index a8679d1d8175a..18a301d8e46b0 100644 --- a/docs/zh/docs/tutorial/dependencies/index.md +++ b/docs/zh/docs/tutorial/dependencies/index.md @@ -1,52 +1,52 @@ -# Dependencies +# 依赖项 -**FastAPI** has a very powerful but intuitive **Dependency Injection** system. +FastAPI 提供了简单易用,但功能强大的**依赖注入**系统。 -It is designed to be very simple to use, and to make it very easy for any developer to integrate other components with **FastAPI**. +这个依赖系统设计的简单易用,可以让开发人员轻松地把组件集成至 **FastAPI**。 -## What is "Dependency Injection" +## 什么是「依赖注入」 -**"Dependency Injection"** means, in programming, that there is a way for your code (in this case, your *path operation functions*) to declare things that it requires to work and use: "dependencies". +编程中的**「依赖注入」**是声明代码(本文中为*路径操作函数* )运行所需的,或要使用的「依赖」的一种方式。 -And then, that system (in this case **FastAPI**) will take care of doing whatever is needed to provide your code with those needed dependencies ("inject" the dependencies). +然后,由系统(本文中为 **FastAPI**)负责执行任意需要的逻辑,为代码提供这些依赖(「注入」依赖项)。 This is very useful when you need to: -* Have shared logic (the same code logic again and again). -* Share database connections. -* Enforce security, authentication, role requirements, etc. +* 共享业务逻辑(复用相同的代码逻辑) +* 共享数据库连接 +* 实现安全、验证、角色权限 * And many other things... All these, while minimizing code repetition. -## First Steps +## 第一步 Let's see a very simple example. It will be so simple that it is not very useful, for now. -But this way we can focus on how the **Dependency Injection** system works. +但通过这个例子,您可以初步了解「依赖注入」的工作机制。 -### Create a dependency, or "dependable" +### 创建依赖项 -Let's first focus on the dependency. +首先,要关注的是依赖项。 -It is just a function that can take all the same parameters that a *path operation function* can take: +依赖项就是一个函数,且可以使用与*路径操作函数*相同的参数: === "Python 3.10+" ```Python hl_lines="8-9" - {!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} + 这里只能传给 Depends 一个参数。 ``` === "Python 3.9+" ```Python hl_lines="8-11" - {!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} + !!! check "检查" ``` === "Python 3.6+" ```Python hl_lines="9-12" - {!> ../../../docs_src/dependencies/tutorial001_an.py!} + 下一章介绍,除了函数还有哪些「对象」可以用作依赖项。 ``` === "Python 3.10+ non-Annotated" @@ -55,7 +55,7 @@ It is just a function that can take all the same parameters that a *path operati Prefer to use the `Annotated` version if possible. ```Python hl_lines="6-7" - {!> ../../../docs_src/dependencies/tutorial001_py310.py!} + {!../../../docs_src/dependencies/tutorial001.py!} ``` === "Python 3.6+ non-Annotated" @@ -64,26 +64,26 @@ It is just a function that can take all the same parameters that a *path operati Prefer to use the `Annotated` version if possible. ```Python hl_lines="8-11" - {!> ../../../docs_src/dependencies/tutorial001.py!} + {!../../../docs_src/dependencies/tutorial001.py!} ``` That's it. -**2 lines**. +只用了**2 行**代码。 -And it has the same shape and structure that all your *path operation functions* have. +依赖项函数的形式和结构与*路径操作函数*一样。 -You can think of it as a *path operation function* without the "decorator" (without the `@app.get("/some-path")`). +因此,可以把依赖项当作没有「装饰器」(即,没有 `@app.get("/some-path")` )的路径操作函数。 And it can return anything you want. -In this case, this dependency expects: +本例中的依赖项预期接收如下参数: -* An optional query parameter `q` that is a `str`. -* An optional query parameter `skip` that is an `int`, and by default is `0`. -* An optional query parameter `limit` that is an `int`, and by default is `100`. +* 类型为 `str` 的可选查询参数 `q` +* 类型为 `int` 的可选查询参数 `skip`,默认值是 `0` +* 类型为 `int` 的可选查询参数 `limit`,默认值是 `100` -And then it just returns a `dict` containing those values. +然后,依赖项函数返回包含这些值的 `dict`。 !!! info FastAPI added support for `Annotated` (and started recommending it) in version 0.95.0. @@ -92,24 +92,24 @@ And then it just returns a `dict` containing those values. Make sure you [Upgrade the FastAPI version](../../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} to at least 0.95.1 before using `Annotated`. -### Import `Depends` +### 导入 `Depends` === "Python 3.10+" ```Python hl_lines="3" - {!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} + 如里不了解异步,请参阅[异步:*“着急了?”*](../../async.md){.internal-link target=_blank} 一章中 `async` 和 `await` 的内容。 ``` === "Python 3.9+" ```Python hl_lines="3" - {!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} + 大功告成。 ``` === "Python 3.6+" ```Python hl_lines="3" - {!> ../../../docs_src/dependencies/tutorial001_an.py!} + 依赖注入常用于以下场景: ``` === "Python 3.10+ non-Annotated" @@ -127,29 +127,29 @@ And then it just returns a `dict` containing those values. Prefer to use the `Annotated` version if possible. ```Python hl_lines="3" - {!> ../../../docs_src/dependencies/tutorial001.py!} + {!../../../docs_src/dependencies/tutorial001.py!} ``` ### Declare the dependency, in the "dependant" -The same way you use `Body`, `Query`, etc. with your *path operation function* parameters, use `Depends` with a new parameter: +与在*路径操作函数*参数中使用 `Body`、`Query` 的方式相同,声明依赖项需要使用 `Depends` 和一个新的参数: === "Python 3.10+" ```Python hl_lines="13 18" - {!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} + 外部支持库 ``` === "Python 3.9+" ```Python hl_lines="15 20" - {!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} + 声明依赖项 ``` === "Python 3.6+" ```Python hl_lines="16 21" - {!> ../../../docs_src/dependencies/tutorial001_an.py!} + !!! tip "提示" ``` === "Python 3.10+ non-Annotated" @@ -167,27 +167,27 @@ The same way you use `Body`, `Query`, etc. with your *path operation function* p Prefer to use the `Annotated` version if possible. ```Python hl_lines="15 20" - {!> ../../../docs_src/dependencies/tutorial001.py!} + !!! note "笔记" ``` -Although you use `Depends` in the parameters of your function the same way you use `Body`, `Query`, etc, `Depends` works a bit differently. +虽然,在路径操作函数的参数中使用 `Depends` 的方式与 `Body`、`Query` 相同,但 `Depends` 的工作方式略有不同。 You only give `Depends` a single parameter. -This parameter must be something like a function. +且该参数必须是可调用对象,比如函数。 -You **don't call it** directly (don't add the parenthesis at the end), you just pass it as a parameter to `Depends()`. +上述场景均可以使用**依赖注入**,将代码重复最小化。 -And that function takes parameters in the same way that *path operation functions* do. +该函数接收的参数和*路径操作函数*的参数一样。 !!! tip You'll see what other "things", apart from functions, can be used as dependencies in the next chapter. -Whenever a new request arrives, **FastAPI** will take care of: +接收到新的请求时,**FastAPI** 执行如下操作: -* Calling your dependency ("dependable") function with the correct parameters. -* Get the result from your function. -* Assign that result to the parameter in your *path operation function*. +* 用正确的参数调用依赖项函数(「可依赖项」) +* 获取函数返回的结果 +* 把函数返回的结果赋值给*路径操作函数*的参数 ```mermaid graph TB @@ -200,16 +200,15 @@ common_parameters --> read_items common_parameters --> read_users ``` -This way you write shared code once and **FastAPI** takes care of calling it for your *path operations*. +这样,只编写一次代码,**FastAPI** 就可以为多个*路径操作*共享这段代码 。 -!!! check - Notice that you don't have to create a special class and pass it somewhere to **FastAPI** to "register" it or anything similar. +!!! 注意,无需创建专门的类,并将之传递给 **FastAPI** 以进行「注册」或执行类似的操作。 - You just pass it to `Depends` and **FastAPI** knows how to do the rest. + 只要把它传递给 `Depends`,**FastAPI** 就知道该如何执行后续操作。 ## Share `Annotated` dependencies -In the examples above, you see that there's a tiny bit of **code duplication**. +接下来,我们学习一个非常简单的例子,尽管它过于简单,不是很实用。 When you need to use the `common_parameters()` dependency, you have to write the whole parameter with the type annotation and `Depends()`: @@ -222,19 +221,19 @@ But because we are using `Annotated`, we can store that `Annotated` value in a v === "Python 3.10+" ```Python hl_lines="12 16 21" - {!> ../../../docs_src/dependencies/tutorial001_02_an_py310.py!} + 开发人员可以使用依赖项及其子依赖项为这些路径操作添加不同的权限: ``` === "Python 3.9+" ```Python hl_lines="14 18 23" - {!> ../../../docs_src/dependencies/tutorial001_02_an_py39.py!} + 等…… ``` === "Python 3.6+" ```Python hl_lines="15 19 24" - {!> ../../../docs_src/dependencies/tutorial001_02_an.py!} + 依赖项可以返回各种内容。 ``` !!! tip @@ -244,77 +243,77 @@ But because we are using `Annotated`, we can store that `Annotated` value in a v The dependencies will keep working as expected, and the **best part** is that the **type information will be preserved**, which means that your editor will be able to keep providing you with **autocompletion**, **inline errors**, etc. The same for other tools like `mypy`. -This will be especially useful when you use it in a **large code base** where you use **the same dependencies** over and over again in ***many ***path operations******. +上述这些操作都是可行的,**FastAPI** 知道该怎么处理。 -## To `async` or not to `async` +## 要不要使用 `async`? -As dependencies will also be called by **FastAPI** (the same as your *path operation functions*), the same rules apply while defining your functions. +**FastAPI** 调用依赖项的方式与*路径操作函数*一样,因此,定义依赖项函数,也要应用与路径操作函数相同的规则。 -You can use `async def` or normal `def`. +即,既可以使用异步的 `async def`,也可以使用普通的 `def` 定义依赖项。 -And you can declare dependencies with `async def` inside of normal `def` *path operation functions*, or `def` dependencies inside of `async def` *path operation functions*, etc. +在普通的 `def` *路径操作函数*中,可以声明异步的 `async def` 依赖项;也可以在异步的 `async def` *路径操作函数*中声明普通的 `def` 依赖项。 -It doesn't matter. **FastAPI** will know what to do. +It doesn't matter. 然后,**FastAPI** 会用正确的参数调用函数,并提取请求中的数据。 !!! note If you don't know, check the [Async: *"In a hurry?"*](../../async.md){.internal-link target=_blank} section about `async` and `await` in the docs. -## Integrated with OpenAPI +## 与 OpenAPI 集成 -All the request declarations, validations and requirements of your dependencies (and sub-dependencies) will be integrated in the same OpenAPI schema. +依赖项及子依赖项的所有请求声明、验证和需求都可以集成至同一个 OpenAPI 概图。 -So, the interactive docs will have all the information from these dependencies too: +所以,交互文档里也会显示依赖项的所有信息: -## Simple usage +## 简单用法 -If you look at it, *path operation functions* are declared to be used whenever a *path* and *operation* matches, and then **FastAPI** takes care of calling the function with the correct parameters, extracting the data from the request. +开发人员永远都不需要直接调用这些函数,这些函数是由框架(在此为 **FastAPI** )调用的。 -Actually, all (or most) of the web frameworks work in this same way. +实际上,所有(或大多数)网络框架的工作方式都是这样的。 You never call those functions directly. They are called by your framework (in this case, **FastAPI**). -With the Dependency Injection system, you can also tell **FastAPI** that your *path operation function* also "depends" on something else that should be executed before your *path operation function*, and **FastAPI** will take care of executing it and "injecting" the results. +通过依赖注入系统,只要告诉 **FastAPI** *路径操作函数* 还要「依赖」其他在*路径操作函数*之前执行的内容,**FastAPI** 就会执行函数代码,并「注入」函数返回的结果。 -Other common terms for this same idea of "dependency injection" are: +其他与「依赖注入」概念相同的术语为: -* resources -* providers -* services -* injectables -* components +* 资源(Resource) +* 提供方(Provider) +* 服务(Service) +* 可注入(Injectable) +* 组件(Component) -## **FastAPI** plug-ins +## **FastAPI** 兼容性 -Integrations and "plug-in"s can be built using the **Dependency Injection** system. But in fact, there is actually **no need to create "plug-ins"**, as by using dependencies it's possible to declare an infinite number of integrations and interactions that become available to your *path operation functions*. +**依赖注入**系统支持构建集成和「插件」。 但实际上,FastAPI 根本**不需要创建「插件」**,因为使用依赖项可以声明不限数量的、可用于*路径操作函数*的集成与交互。 -And dependencies can be created in a very simple and intuitive way that allow you to just import the Python packages you need, and integrate them with your API functions in a couple of lines of code, *literally*. +创建依赖项非常简单、直观,并且还支持导入 Python 包。 毫不夸张地说,只要几行代码就可以把需要的 Python 包与 API 函数集成在一起。 -You will see examples of this in the next chapters, about relational and NoSQL databases, security, etc. +下一章将详细介绍在关系型数据库、NoSQL 数据库、安全等方面使用依赖项的例子。 -## **FastAPI** compatibility +## **FastAPI** 插件 -The simplicity of the dependency injection system makes **FastAPI** compatible with: +依赖注入系统如此简洁的特性,让 **FastAPI** 可以与下列系统兼容: -* all the relational databases -* NoSQL databases +* 关系型数据库 +* NoSQL 数据库 * external packages -* external APIs -* authentication and authorization systems -* API usage monitoring systems -* response data injection systems -* etc. +* 外部 API +* 认证和鉴权系统 +* API 使用监控系统 +* 响应数据注入系统 +* 等等…… -## Simple and Powerful +## 简单而强大 -Although the hierarchical dependency injection system is very simple to define and use, it's still very powerful. +虽然,**层级式依赖注入系统**的定义与使用十分简单,但它却非常强大。 -You can define dependencies that in turn can define dependencies themselves. +比如,可以定义依赖其他依赖项的依赖项。 -In the end, a hierarchical tree of dependencies is built, and the **Dependency Injection** system takes care of solving all these dependencies for you (and their sub-dependencies) and providing (injecting) the results at each step. +最后,依赖项层级树构建后,**依赖注入系统**会处理所有依赖项及其子依赖项,并为每一步操作提供(注入)结果。 -For example, let's say you have 4 API endpoints (*path operations*): +比如,下面有 4 个 API 路径操作(*端点*): * `/items/public/` * `/items/private/` @@ -346,8 +345,8 @@ admin_user --> activate_user paying_user --> pro_items ``` -## Integrated with **OpenAPI** +## 与 **OpenAPI** 集成 -All these dependencies, while declaring their requirements, also add parameters, validations, etc. to your *path operations*. +在声明需求时,所有这些依赖项还会把参数、验证等功能添加至路径操作。 -**FastAPI** will take care of adding it all to the OpenAPI schema, so that it is shown in the interactive documentation systems. +**FastAPI** 负责把上述内容全部添加到 OpenAPI 概图,并显示在交互文档中。 From 03fa91065e1151a3e68a9d44ca3e1ed9f52b28fa Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:38 +0800 Subject: [PATCH 134/163] New translations sub-dependencies.md (Chinese Simplified) --- .../tutorial/dependencies/sub-dependencies.md | 74 ++++++++++--------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/docs/zh/docs/tutorial/dependencies/sub-dependencies.md b/docs/zh/docs/tutorial/dependencies/sub-dependencies.md index b89844a1ffd66..fa61d94fa0bb3 100644 --- a/docs/zh/docs/tutorial/dependencies/sub-dependencies.md +++ b/docs/zh/docs/tutorial/dependencies/sub-dependencies.md @@ -1,31 +1,31 @@ -# Sub-dependencies +# 子依赖项 -You can create dependencies that have **sub-dependencies**. +FastAPI 支持创建含**子依赖项**的依赖项。 -They can be as **deep** as you need them to be. +并且,可以按需声明任意**深度**的子依赖项嵌套层级。 -**FastAPI** will take care of solving them. +**FastAPI** 负责处理解析不同深度的子依赖项。 -## First dependency "dependable" +## 第一层依赖项 You could create a first dependency ("dependable") like: === "Python 3.10+" ```Python hl_lines="8-9" - {!> ../../../docs_src/dependencies/tutorial005_an_py310.py!} + 这个函数很简单(不过也没什么用),但却有助于让我们专注于了解子依赖项的工作方式。 ``` === "Python 3.9+" ```Python hl_lines="8-9" - {!> ../../../docs_src/dependencies/tutorial005_an_py39.py!} + 小结 ``` === "Python 3.6+" ```Python hl_lines="9-10" - {!> ../../../docs_src/dependencies/tutorial005_an.py!} + !!! tip "提示" ``` === "Python 3.10 non-Annotated" @@ -34,7 +34,7 @@ You could create a first dependency ("dependable") like: Prefer to use the `Annotated` version if possible. ```Python hl_lines="6-7" - {!> ../../../docs_src/dependencies/tutorial005_py310.py!} + {!../../../docs_src/dependencies/tutorial005.py!} ``` === "Python 3.6 non-Annotated" @@ -46,13 +46,13 @@ You could create a first dependency ("dependable") like: {!> ../../../docs_src/dependencies/tutorial005.py!} ``` -It declares an optional query parameter `q` as a `str`, and then it just returns it. +这段代码声明了类型为 `str` 的可选查询参数 `q`,然后返回这个查询参数。 This is quite simple (not very useful), but will help us focus on how the sub-dependencies work. ## Second dependency, "dependable" and "dependant" -Then you can create another dependency function (a "dependable") that at the same time declares a dependency of its own (so it is a "dependant" too): +接下来,创建另一个依赖项函数,并同时用该依赖项自身再声明一个依赖项(所以这也是一个「依赖项」): === "Python 3.10+" @@ -63,7 +63,7 @@ Then you can create another dependency function (a "dependable") that at the sam === "Python 3.9+" ```Python hl_lines="13" - {!> ../../../docs_src/dependencies/tutorial005_an_py39.py!} + 下列代码创建了第一层依赖项: ``` === "Python 3.6+" @@ -87,19 +87,19 @@ Then you can create another dependency function (a "dependable") that at the sam Prefer to use the `Annotated` version if possible. ```Python hl_lines="13" - {!> ../../../docs_src/dependencies/tutorial005.py!} + {!../../../docs_src/dependencies/tutorial005.py!} ``` -Let's focus on the parameters declared: +这里重点说明一下声明的参数: -* Even though this function is a dependency ("dependable") itself, it also declares another dependency (it "depends" on something else). - * It depends on the `query_extractor`, and assigns the value returned by it to the parameter `q`. -* It also declares an optional `last_query` cookie, as a `str`. - * If the user didn't provide any query `q`, we use the last query used, which we saved to a cookie before. +* 尽管该函数自身是依赖项,但还声明了另一个依赖项(它「依赖」于其他对象) + * 该函数依赖 `query_extractor`, 并把 `query_extractor` 的返回值赋给参数 `q` +* 同时,该函数还声明了类型是 `str` 的可选 cookie(`last_query`) + * 用户未提供查询参数 `q` 时,则使用上次使用后保存在 cookie 中的查询 -## Use the dependency +## 使用依赖项 -Then we can use the dependency with: +接下来,就可以使用依赖项: === "Python 3.10+" @@ -110,13 +110,13 @@ Then we can use the dependency with: === "Python 3.9+" ```Python hl_lines="23" - {!> ../../../docs_src/dependencies/tutorial005_an_py39.py!} + 第二层依赖项 ``` === "Python 3.6+" ```Python hl_lines="24" - {!> ../../../docs_src/dependencies/tutorial005_an.py!} + !!! info "信息" ``` === "Python 3.10 non-Annotated" @@ -134,13 +134,12 @@ Then we can use the dependency with: Prefer to use the `Annotated` version if possible. ```Python hl_lines="22" - {!> ../../../docs_src/dependencies/tutorial005.py!} + {!../../../docs_src/dependencies/tutorial005.py!} ``` -!!! info - Notice that we are only declaring one dependency in the *path operation function*, the `query_or_cookie_extractor`. +!!! 注意,这里在*路径操作函数*中只声明了一个依赖项,即 `query_or_cookie_extractor` 。 - But **FastAPI** will know that it has to solve `query_extractor` first, to pass the results of that to `query_or_cookie_extractor` while calling it. + 但 **FastAPI** 必须先处理 `query_extractor`,以便在调用 `query_or_cookie_extractor` 时使用 `query_extractor` 返回的结果。 ```mermaid graph TB @@ -153,19 +152,22 @@ read_query["/items/"] query_extractor --> query_or_cookie_extractor --> read_query ``` -## Using the same dependency multiple times +## 多次使用同一个依赖项 -If one of your dependencies is declared multiple times for the same *path operation*, for example, multiple dependencies have a common sub-dependency, **FastAPI** will know to call that sub-dependency only once per request. +如果在同一个*路径操作* 多次声明了同一个依赖项,例如,多个依赖项共用一个子依赖项,**FastAPI** 在处理同一请求时,只调用一次该子依赖项。 -And it will save the returned value in a "cache" and pass it to all the "dependants" that need it in that specific request, instead of calling the dependency multiple times for the same request. +FastAPI 不会为同一个请求多次调用同一个依赖项,而是把依赖项的返回值进行「缓存」,并把它传递给同一请求中所有需要使用该返回值的「依赖项」。 -In an advanced scenario where you know you need the dependency to be called at every step (possibly multiple times) in the same request instead of using the "cached" value, you can set the parameter `use_cache=False` when using `Depends`: +在高级使用场景中,如果不想使用「缓存」值,而是为需要在同一请求的每一步操作(多次)中都实际调用依赖项,可以把 `Depends` 的参数 `use_cache` 的值设置为 `False` : === "Python 3.6+" ```Python hl_lines="1" - async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]): - return {"fresh_value": fresh_value} + 这些简单的例子现在看上去虽然没有什么实用价值, + +但在**安全**一章中,您会了解到这些例子的用途, + +以及这些例子所能节省的代码量。 ``` === "Python 3.6+ non-Annotated" @@ -175,16 +177,16 @@ In an advanced scenario where you know you need the dependency to be called at e ```Python hl_lines="1" async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)): - return {"fresh_value": fresh_value} + return {"fresh_value": fresh_value} ``` ## Recap -Apart from all the fancy words used here, the **Dependency Injection** system is quite simple. +千万别被本章里这些花里胡哨的词藻吓倒了,其实**依赖注入**系统非常简单。 -Just functions that look the same as the *path operation functions*. +依赖注入无非是与*路径操作函数*一样的函数罢了。 -But still, it is very powerful, and allows you to declare arbitrarily deeply nested dependency "graphs" (trees). +但它依然非常强大,能够声明任意嵌套深度的「图」或树状的依赖结构。 !!! tip All this might not seem as useful with these simple examples. From 341db049a8242692222de3264de94ca1c054d668 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:40 +0800 Subject: [PATCH 135/163] New translations encoder.md (Chinese Simplified) --- docs/zh/docs/tutorial/encoder.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/zh/docs/tutorial/encoder.md b/docs/zh/docs/tutorial/encoder.md index 735aa2209ffed..3c0b9152fc1dd 100644 --- a/docs/zh/docs/tutorial/encoder.md +++ b/docs/zh/docs/tutorial/encoder.md @@ -1,24 +1,24 @@ -# JSON Compatible Encoder +# JSON 兼容编码器 -There are some cases where you might need to convert a data type (like a Pydantic model) to something compatible with JSON (like a `dict`, `list`, etc). +在某些情况下,您可能需要将数据类型(如Pydantic模型)转换为与JSON兼容的数据类型(如`dict`、`list`等)。 -For example, if you need to store it in a database. +比如,如果您需要将其存储在数据库中。 -For that, **FastAPI** provides a `jsonable_encoder()` function. +对于这种要求, **FastAPI**提供了`jsonable_encoder()`函数。 -## Using the `jsonable_encoder` +## 使用`jsonable_encoder` -Let's imagine that you have a database `fake_db` that only receives JSON compatible data. +让我们假设你有一个数据库名为`fake_db`,它只能接收与JSON兼容的数据。 -For example, it doesn't receive `datetime` objects, as those are not compatible with JSON. +例如,它不接收`datetime`这类的对象,因为这些对象与JSON不兼容。 -So, a `datetime` object would have to be converted to a `str` containing the data in ISO format. +因此,`datetime`对象必须将转换为包含ISO格式化的`str`类型对象。 -The same way, this database wouldn't receive a Pydantic model (an object with attributes), only a `dict`. +同样,这个数据库也不会接收Pydantic模型(带有属性的对象),而只接收`dict`。 -You can use `jsonable_encoder` for that. +对此你可以使用`jsonable_encoder`。 -It receives an object, like a Pydantic model, and returns a JSON compatible version: +它接收一个对象,比如Pydantic模型,并会返回一个JSON兼容的版本: === "Python 3.10+" @@ -32,11 +32,11 @@ It receives an object, like a Pydantic model, and returns a JSON compatible vers {!> ../../../docs_src/encoder/tutorial001.py!} ``` -In this example, it would convert the Pydantic model to a `dict`, and the `datetime` to a `str`. +在这个例子中,它将Pydantic模型转换为`dict`,并将`datetime`转换为`str`。 -The result of calling it is something that can be encoded with the Python standard `json.dumps()`. +调用它的结果后就可以使用Python标准编码中的`json.dumps()`。 -It doesn't return a large `str` containing the data in JSON format (as a string). It returns a Python standard data structure (e.g. a `dict`) with values and sub-values that are all compatible with JSON. +这个操作不会返回一个包含JSON格式(作为字符串)数据的庞大的`str`。 它将返回一个Python标准数据结构(例如`dict`),其值和子值都与JSON兼容。 -!!! note - `jsonable_encoder` is actually used by **FastAPI** internally to convert data. But it is useful in many other scenarios. +!!! !!! note + `jsonable_encoder`实际上是FastAPI内部用来转换数据的。 但是它在许多其他场景中也很有用。 From a945c014f40d84283f417ddeeba5514c7993bacb Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:41 +0800 Subject: [PATCH 136/163] New translations extra-data-types.md (Chinese Simplified) --- docs/zh/docs/tutorial/extra-data-types.md | 72 +++++++++++------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/docs/zh/docs/tutorial/extra-data-types.md b/docs/zh/docs/tutorial/extra-data-types.md index 7d6ffbc780cb0..60fc4c3bc68df 100644 --- a/docs/zh/docs/tutorial/extra-data-types.md +++ b/docs/zh/docs/tutorial/extra-data-types.md @@ -1,59 +1,59 @@ -# Extra Data Types +# 额外数据类型 -Up to now, you have been using common data types, like: +到目前为止,您一直在使用常见的数据类型,如: * `int` * `float` * `str` * `bool` -But you can also use more complex data types. +但是您也可以使用更复杂的数据类型。 -And you will still have the same features as seen up to now: +您仍然会拥有现在已经看到的相同的特性: -* Great editor support. -* Data conversion from incoming requests. -* Data conversion for response data. -* Data validation. -* Automatic annotation and documentation. +* 很棒的编辑器支持。 +* 传入请求的数据转换。 +* 响应数据转换。 +* 数据验证。 +* 自动补全和文档。 -## Other data types +## 其他数据类型 -Here are some of the additional data types you can use: +下面是一些你可以使用的其他数据类型: * `UUID`: - * A standard "Universally Unique Identifier", common as an ID in many databases and systems. - * In requests and responses will be represented as a `str`. + * 一种标准的 "通用唯一标识符" ,在许多数据库和系统中用作ID。 + * 在请求和响应中将以 `str` 表示。 * `datetime.datetime`: - * A Python `datetime.datetime`. - * In requests and responses will be represented as a `str` in ISO 8601 format, like: `2008-09-15T15:53:00+05:00`. + * 一个 Python `datetime.datetime`. + * 在请求和响应中将表示为 ISO 8601 格式的 `str` ,比如: `2008-09-15T15:53:00+05:00`. * `datetime.date`: * Python `datetime.date`. - * In requests and responses will be represented as a `str` in ISO 8601 format, like: `2008-09-15`. + * 在请求和响应中将表示为 ISO 8601 格式的 `str` ,比如: `2008-09-15`. * `datetime.time`: - * A Python `datetime.time`. - * In requests and responses will be represented as a `str` in ISO 8601 format, like: `14:23:55.003`. + * 一个 Python `datetime.time`. + * 在请求和响应中将表示为 ISO 8601 格式的 `str` ,比如: `14:23:55.003`. * `datetime.timedelta`: - * A Python `datetime.timedelta`. - * In requests and responses will be represented as a `float` of total seconds. - * Pydantic also allows representing it as a "ISO 8601 time diff encoding", see the docs for more info. + * 一个 Python `datetime.timedelta`. + * 在请求和响应中将表示为 `float` 代表总秒数。 + * Pydantic 也允许将其表示为 "ISO 8601 时间差异编码", 查看文档了解更多信息。 * `frozenset`: - * In requests and responses, treated the same as a `set`: - * In requests, a list will be read, eliminating duplicates and converting it to a `set`. - * In responses, the `set` will be converted to a `list`. - * The generated schema will specify that the `set` values are unique (using JSON Schema's `uniqueItems`). + * 在请求和响应中,作为 `set` 对待: + * 在请求中,列表将被读取,消除重复,并将其转换为一个 `set`。 + * 在响应中 `set` 将被转换为 `list` 。 + * 产生的模式将指定那些 `set` 的值是唯一的 (使用 JSON 模式的 `uniqueItems`)。 * `bytes`: - * Standard Python `bytes`. - * In requests and responses will be treated as `str`. - * The generated schema will specify that it's a `str` with `binary` "format". + * 标准的 Python `bytes`。 + * 在请求和相应中被当作 `str` 处理。 + * 生成的模式将指定这个 `str` 是 `binary` "格式"。 * `Decimal`: - * Standard Python `Decimal`. - * In requests and responses, handled the same as a `float`. -* You can check all the valid pydantic data types here: Pydantic data types. + * 标准的 Python `Decimal`。 + * 在请求和相应中被当做 `float` 一样处理。 +* 您可以在这里检查所有有效的pydantic数据类型: Pydantic data types. -## Example +## 例子 -Here's an example *path operation* with parameters using some of the above types. +下面是一个*路径操作*的示例,其中的参数使用了上面的一些类型。 === "Python 3.10+" @@ -79,7 +79,7 @@ Here's an example *path operation* with parameters using some of the above types Prefer to use the `Annotated` version if possible. ```Python hl_lines="1 2 11-15" - {!> ../../../docs_src/extra_data_types/tutorial001_py310.py!} + {!../../../docs_src/extra_data_types/tutorial001.py!} ``` === "Python 3.6+ non-Annotated" @@ -88,10 +88,10 @@ Here's an example *path operation* with parameters using some of the above types Prefer to use the `Annotated` version if possible. ```Python hl_lines="1 2 12-16" - {!> ../../../docs_src/extra_data_types/tutorial001.py!} + {!../../../docs_src/extra_data_types/tutorial001.py!} ``` -Note that the parameters inside the function have their natural data type, and you can, for example, perform normal date manipulations, like: +注意,函数内的参数有原生的数据类型,你可以,例如,执行正常的日期操作,如: === "Python 3.10+" From ec22cf1d35a2ddb6489a41ece35942d9c17f96a8 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:42 +0800 Subject: [PATCH 137/163] New translations extra-models.md (Chinese Simplified) --- docs/zh/docs/tutorial/extra-models.md | 127 +++++++++++++------------- 1 file changed, 64 insertions(+), 63 deletions(-) diff --git a/docs/zh/docs/tutorial/extra-models.md b/docs/zh/docs/tutorial/extra-models.md index e91e879e41672..1a1f3c3354ca8 100644 --- a/docs/zh/docs/tutorial/extra-models.md +++ b/docs/zh/docs/tutorial/extra-models.md @@ -1,21 +1,21 @@ -# Extra Models +# 额外的模型 -Continuing with the previous example, it will be common to have more than one related model. +我们从前面的示例继续,拥有多个相关的模型是很常见的。 -This is especially the case for user models, because: +对用户模型来说尤其如此,因为: -* The **input model** needs to be able to have a password. -* The **output model** should not have a password. -* The **database model** would probably need to have a hashed password. +* **输入模型**需要拥有密码属性。 +* **输出模型**不应该包含密码。 +* **数据库模型**很可能需要保存密码的哈希值。 -!!! danger - Never store user's plaintext passwords. Always store a "secure hash" that you can then verify. +!!! !!! danger + 永远不要存储用户的明文密码。 始终存储一个可以用于验证的「安全哈希值」。 - If you don't know, you will learn what a "password hash" is in the [security chapters](security/simple-oauth2.md#password-hashing){.internal-link target=_blank}. + 如果你尚未了解该知识,你可以在[安全章节](security/simple-oauth2.md#password-hashing){.internal-link target=_blank}中学习何为「密码哈希值」。 -## Multiple models +## 多个模型 -Here's a general idea of how the models could look like with their password fields and the places where they are used: +下面是应该如何根据它们的密码字段以及使用位置去定义模型的大概思路: === "Python 3.10+" @@ -26,38 +26,38 @@ Here's a general idea of how the models could look like with their password fiel === "Python 3.6+" ```Python hl_lines="9 11 16 22 24 29-30 33-35 40-41" - {!> ../../../docs_src/extra_models/tutorial001.py!} + {!../../../docs_src/extra_models/tutorial001.py!} ``` -### About `**user_in.dict()` +### 关于 `**user_in.dict()` -#### Pydantic's `.dict()` +#### Pydantic 的 `.dict()` -`user_in` is a Pydantic model of class `UserIn`. +`user_in` 是一个 `UserIn` 类的 Pydantic 模型. -Pydantic models have a `.dict()` method that returns a `dict` with the model's data. +Pydantic 模型具有 `.dict()` 方法,该方法返回一个拥有模型数据的 `dict`。 -So, if we create a Pydantic object `user_in` like: +因此,如果我们像下面这样创建一个 Pydantic 对象 `user_in`: ```Python user_in = UserIn(username="john", password="secret", email="john.doe@example.com") ``` -and then we call: +然后我们调用: ```Python user_dict = user_in.dict() ``` -we now have a `dict` with the data in the variable `user_dict` (it's a `dict` instead of a Pydantic model object). +现在我们有了一个数据位于变量 `user_dict` 中的 `dict`(它是一个 `dict` 而不是 Pydantic 模型对象)。 -And if we call: +如果我们调用: ```Python print(user_dict) ``` -we would get a Python `dict` with: +我们将获得一个这样的 Python `dict`: ```Python { @@ -68,17 +68,17 @@ we would get a Python `dict` with: } ``` -#### Unwrapping a `dict` +#### 解包 `dict` -If we take a `dict` like `user_dict` and pass it to a function (or class) with `**user_dict`, Python will "unwrap" it. It will pass the keys and values of the `user_dict` directly as key-value arguments. +如果我们将 `user_dict` 这样的 `dict` 以 `**user_dict` 形式传递给一个函数(或类),Python将对其进行「解包」。 它会将 `user_dict` 的键和值作为关键字参数直接传递。 -So, continuing with the `user_dict` from above, writing: +因此,从上面的 `user_dict` 继续,编写: ```Python UserInDB(**user_dict) ``` -Would result in something equivalent to: +会产生类似于以下的结果: ```Python UserInDB( @@ -89,7 +89,7 @@ UserInDB( ) ``` -Or more exactly, using `user_dict` directly, with whatever contents it might have in the future: +或者更确切地,直接使用 `user_dict` 来表示将来可能包含的任何内容: ```Python UserInDB( @@ -100,34 +100,34 @@ UserInDB( ) ``` -#### A Pydantic model from the contents of another +#### 来自于其他模型内容的 Pydantic 模型 -As in the example above we got `user_dict` from `user_in.dict()`, this code: +...因为 `user_in.dict()` 是一个 `dict`,然后我们通过以`**`开头传递给 `UserInDB` 来使 Python「解包」它。 ```Python user_dict = user_in.dict() UserInDB(**user_dict) ``` -would be equivalent to: +等同于: ```Python UserInDB(**user_in.dict()) ``` -...because `user_in.dict()` is a `dict`, and then we make Python "unwrap" it by passing it to `UserInDB` prepended with `**`. +如上例所示,我们从 `user_in.dict()` 中获得了 `user_dict`,此代码: -So, we get a Pydantic model from the data in another Pydantic model. +这样,我们获得了一个来自于其他 Pydantic 模型中的数据的 Pydantic 模型。 -#### Unwrapping a `dict` and extra keywords +#### 解包 `dict` 和额外关键字 -And then adding the extra keyword argument `hashed_password=hashed_password`, like in: +然后添加额外的关键字参数 `hashed_password=hashed_password`,例如: ```Python UserInDB(**user_in.dict(), hashed_password=hashed_password) ``` -...ends up being like: +...最终的结果如下: ```Python UserInDB( @@ -142,21 +142,21 @@ UserInDB( !!! warning The supporting additional functions are just to demo a possible flow of the data, but they of course are not providing any real security. -## Reduce duplication +## 减少重复 -Reducing code duplication is one of the core ideas in **FastAPI**. +减少代码重复是 **FastAPI** 的核心思想之一。 -As code duplication increments the chances of bugs, security issues, code desynchronization issues (when you update in one place but not in the others), etc. +因为代码重复会增加出现 bug、安全性问题、代码失步问题(当你在一个位置更新了代码但没有在其他位置更新)等的可能性。 -And these models are all sharing a lot of the data and duplicating attribute names and types. +上面的这些模型都共享了大量数据,并拥有重复的属性名称和类型。 -We could do better. +我们可以做得更好。 -We can declare a `UserBase` model that serves as a base for our other models. And then we can make subclasses of that model that inherit its attributes (type declarations, validation, etc). +我们可以声明一个 `UserBase` 模型作为其他模型的基类。 然后我们可以创建继承该模型属性(类型声明,校验等)的子类。 -All the data conversion, validation, documentation, etc. will still work as normally. +所有的数据转换、校验、文档生成等仍将正常运行。 -That way, we can declare just the differences between the models (with plaintext `password`, with `hashed_password` and without password): +这样,我们可以仅声明模型之间的差异部分(具有明文的 `password`、具有 `hashed_password` 以及不包括密码)。 === "Python 3.10+" @@ -167,19 +167,19 @@ That way, we can declare just the differences between the models (with plaintext === "Python 3.6+" ```Python hl_lines="9 15-16 19-20 23-24" - {!> ../../../docs_src/extra_models/tutorial002.py!} + {!../../../docs_src/extra_models/tutorial002.py!} ``` -## `Union` or `anyOf` +## `Union` 或者 `anyOf` -You can declare a response to be the `Union` of two types, that means, that the response would be any of the two. +你可以将一个响应声明为两种类型的 `Union`,这意味着该响应将是两种类型中的任何一种。 -It will be defined in OpenAPI with `anyOf`. +这将在 OpenAPI 中使用 `anyOf` 进行定义。 -To do that, use the standard Python type hint `typing.Union`: +为此,请使用标准的 Python 类型提示 `typing.Union`: -!!! note - When defining a `Union`, include the most specific type first, followed by the less specific type. In the example below, the more specific `PlaneItem` comes before `CarItem` in `Union[PlaneItem, CarItem]`. +!!! !!! note + 定义一个 `Union` 类型时,首先包括最详细的类型,然后是不太详细的类型。 在下面的示例中,更详细的 `PlaneItem` 位于 `Union[PlaneItem,CarItem]` 中的 `CarItem` 之前。 === "Python 3.10+" @@ -190,7 +190,7 @@ To do that, use the standard Python type hint ../../../docs_src/extra_models/tutorial003.py!} + {!../../../docs_src/extra_models/tutorial003.py!} ``` ### `Union` in Python 3.10 @@ -207,46 +207,47 @@ some_variable: PlaneItem | CarItem But if we put that in `response_model=PlaneItem | CarItem` we would get an error, because Python would try to perform an **invalid operation** between `PlaneItem` and `CarItem` instead of interpreting that as a type annotation. -## List of models +## 模型列表 -The same way, you can declare responses of lists of objects. +你可以用同样的方式声明由对象列表构成的响应。 -For that, use the standard Python `typing.List` (or just `list` in Python 3.9 and above): +为此,请使用标准的 Python `typing.List`: === "Python 3.9+" ```Python hl_lines="18" - {!> ../../../docs_src/extra_models/tutorial004_py39.py!} + !!! warning + 辅助性的额外函数只是为了演示可能的数据流,但它们显然不能提供任何真正的安全性。 ``` === "Python 3.6+" ```Python hl_lines="1 20" - {!> ../../../docs_src/extra_models/tutorial004.py!} + {!../../../docs_src/extra_models/tutorial004.py!} ``` -## Response with arbitrary `dict` +## 任意 `dict` 构成的响应 -You can also declare a response using a plain arbitrary `dict`, declaring just the type of the keys and values, without using a Pydantic model. +你还可以使用一个任意的普通 `dict` 声明响应,仅声明键和值的类型,而不使用 Pydantic 模型。 -This is useful if you don't know the valid field/attribute names (that would be needed for a Pydantic model) beforehand. +如果你事先不知道有效的字段/属性名称(对于 Pydantic 模型是必需的),这将很有用。 -In this case, you can use `typing.Dict` (or just `dict` in Python 3.9 and above): +在这种情况下,你可以使用 `typing.Dict`: === "Python 3.9+" ```Python hl_lines="6" - {!> ../../../docs_src/extra_models/tutorial005_py39.py!} + 总结 ``` === "Python 3.6+" ```Python hl_lines="1 8" - {!> ../../../docs_src/extra_models/tutorial005.py!} + {!../../../docs_src/extra_models/tutorial005.py!} ``` ## Recap -Use multiple Pydantic models and inherit freely for each case. +使用多个 Pydantic 模型,并针对不同场景自由地继承。 -You don't need to have a single data model per entity if that entity must be able to have different "states". As the case with the user "entity" with a state including `password`, `password_hash` and no password. +如果一个实体必须能够具有不同的「状态」,你无需为每个状态的实体定义单独的数据模型。 以用户「实体」为例,其状态有包含 `password`、包含 `password_hash` 以及不含密码。 From 48337df42af95b521b5eb996d73642ce6a896e5a Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:43 +0800 Subject: [PATCH 138/163] New translations first-steps.md (Chinese Simplified) --- docs/zh/docs/tutorial/first-steps.md | 210 ++++++++++++++------------- 1 file changed, 106 insertions(+), 104 deletions(-) diff --git a/docs/zh/docs/tutorial/first-steps.md b/docs/zh/docs/tutorial/first-steps.md index 1368b89c76081..597e8e6f89acf 100644 --- a/docs/zh/docs/tutorial/first-steps.md +++ b/docs/zh/docs/tutorial/first-steps.md @@ -1,14 +1,14 @@ -# First Steps +# 第一步 -The simplest FastAPI file could look like this: +最简单的 FastAPI 文件可能像下面这样: ```Python {!../../../docs_src/first_steps/tutorial001.py!} ``` -Copy that to a file `main.py`. +将其复制到 `main.py` 文件中。 -Run the live server: +运行实时服务器:
@@ -21,85 +21,87 @@ $ uvicorn main:app --reload INFO: Waiting for application startup. INFO: Application startup complete. ``` +INFO: Application startup complete. +```
-!!! note - The command `uvicorn main:app` refers to: +!!! !!! note + `uvicorn main:app` 命令含义如下: - * `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 use for development. + * `main`:`main.py` 文件(一个 Python「模块」)。 + * `app`:在 `main.py` 文件中通过 `app = FastAPI()` 创建的对象。 + * `--reload`:让服务器在更新代码后重新启动。 仅在开发时使用该选项。 -In the output, there's a line with something like: +在输出中,会有一行信息像下面这样: ```hl_lines="4" INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ``` -That line shows the URL where your app is being served, in your local machine. +该行显示了你的应用在本机所提供服务的 URL 地址。 -### Check it +### 查看 -Open your browser at
http://127.0.0.1:8000. +打开浏览器访问 http://127.0.0.1:8000。 -You will see the JSON response as: +你将看到如下的 JSON 响应: ```JSON {"message": "Hello World"} ``` -### Interactive API docs +### 交互式 API 文档 -Now go to http://127.0.0.1:8000/docs. +跳转到 http://127.0.0.1:8000/docs。 -You will see the automatic interactive API documentation (provided by Swagger UI): +你将会看到自动生成的交互式 API 文档(由 Swagger UI 提供): ![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) -### Alternative API docs +### 可选的 API 文档 -And now, go to http://127.0.0.1:8000/redoc. +前往 http://127.0.0.1:8000/redoc。 -You will see the alternative automatic documentation (provided by ReDoc): +你将会看到可选的自动生成文档 (由 ReDoc 提供): ![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) ### OpenAPI -**FastAPI** generates a "schema" with all your API using the **OpenAPI** standard for defining APIs. +**FastAPI** 使用定义 API 的 **OpenAPI** 标准将你的所有 API 转换成「模式」。 -#### "Schema" +#### 「模式」 -A "schema" is a definition or description of something. Not the code that implements it, but just an abstract description. +「模式」是对事物的一种定义或描述。 它并非具体的实现代码,而只是抽象的描述。 -#### API "schema" +#### API「模式」 In this case, OpenAPI is a specification that dictates how to define a schema of your API. -This schema definition includes your API paths, the possible parameters they take, etc. +定义的 OpenAPI 模式将包括你的 API 路径,以及它们可能使用的参数等等。 -#### Data "schema" +#### 数据「模式」 The term "schema" might also refer to the shape of some data, like a JSON content. -In that case, it would mean the JSON attributes, and data types they have, etc. +在这种情况下,它可以表示 JSON 的属性及其具有的数据类型,等等。 -#### OpenAPI and JSON Schema +#### OpenAPI 和 JSON Schema -OpenAPI defines an API schema for your API. And that schema includes definitions (or "schemas") of the data sent and received by your API using **JSON Schema**, the standard for JSON data schemas. +OpenAPI 为你的 API 定义 API 模式。 该模式中包含了你的 API 发送和接收的数据的定义(或称为「模式」),这些定义通过 JSON 数据模式标准 **JSON Schema** 所生成。 -#### Check the `openapi.json` +#### 查看 `openapi.json` -If you are curious about how the raw OpenAPI schema looks like, FastAPI automatically generates a JSON (schema) with the descriptions of all your API. +如果你对原始的 OpenAPI 模式长什么样子感到好奇,其实它只是一个自动生成的包含了所有 API 描述的 JSON。 -You can see it directly at: http://127.0.0.1:8000/openapi.json. +你可以直接在:http://127.0.0.1:8000/openapi.json 看到它。 -It will show a JSON starting with something like: +它将显示以如下内容开头的 JSON: ```JSON { - "openapi": "3.1.0", + "openapi": "3.0.2", "info": { "title": "FastAPI", "version": "0.1.0" @@ -118,40 +120,40 @@ It will show a JSON starting with something like: ... ``` -#### What is OpenAPI for +#### OpenAPI 的用途 The OpenAPI schema is what powers the two interactive documentation systems included. -And there are dozens of alternatives, all based on OpenAPI. You could easily add any of those alternatives to your application built with **FastAPI**. +并且还有数十种替代方案,它们全部都基于 OpenAPI。 你可以轻松地将这些替代方案中的任何一种添加到使用 **FastAPI** 构建的应用程序中。 -You could also use it to generate code automatically, for clients that communicate with your API. For example, frontend, mobile or IoT applications. +你还可以使用它自动生成与你的 API 进行通信的客户端代码。 例如 web 前端,移动端或物联网嵌入程序。 ## Recap, step by step -### Step 1: import `FastAPI` +### 步骤 1:导入 `FastAPI` ```Python hl_lines="1" {!../../../docs_src/first_steps/tutorial001.py!} ``` -`FastAPI` is a Python class that provides all the functionality for your API. +`FastAPI` 是一个为你的 API 提供了所有功能的 Python 类。 -!!! note "Technical Details" - `FastAPI` is a class that inherits directly from `Starlette`. +!!! !!! note "技术细节" + `FastAPI` 是直接从 `Starlette` 继承的类。 - You can use all the Starlette functionality with `FastAPI` too. + 你可以通过 `FastAPI` 使用所有的 Starlette 的功能。 -### Step 2: create a `FastAPI` "instance" +### 步骤 2:创建一个 `FastAPI`「实例」 ```Python hl_lines="3" {!../../../docs_src/first_steps/tutorial001.py!} ``` -Here the `app` variable will be an "instance" of the class `FastAPI`. +这里的变量 `app` 会是 `FastAPI` 类的一个「实例」。 -This will be the main point of interaction to create all your API. +这个实例将是创建你所有 API 的主要交互对象。 -This `app` is the same one referred by `uvicorn` in the command: +这个 `app` 同样在如下命令中被 `uvicorn` 所引用:
@@ -163,13 +165,13 @@ $ uvicorn main:app --reload
-If you create your app like: +如果你像下面这样创建应用: ```Python hl_lines="3" {!../../../docs_src/first_steps/tutorial002.py!} ``` -And put it in a file `main.py`, then you would call `uvicorn` like: +将代码放入 `main.py` 文件中,然后你可以像下面这样运行 `uvicorn`:
@@ -181,152 +183,152 @@ $ uvicorn main:my_awesome_api --reload
-### Step 3: create a *path operation* +### 步骤 3:创建一个*路径操作* -#### Path +#### 路径 -"Path" here refers to the last part of the URL starting from the first `/`. +这里的「路径」指的是 URL 中从第一个 `/` 起的后半部分。 -So, in a URL like: +所以,在一个这样的 URL 中: ``` https://example.com/items/foo ``` -...the path would be: +...路径会是: ``` /items/foo ``` -!!! info - A "path" is also commonly called an "endpoint" or a "route". +!!! !!! info + 「路径」也通常被称为「端点」或「路由」。 -While building an API, the "path" is the main way to separate "concerns" and "resources". +开发 API 时,「路径」是用来分离「关注点」和「资源」的主要手段。 -#### Operation +#### 操作 -"Operation" here refers to one of the HTTP "methods". +这里的「操作」指的是一种 HTTP「方法」。 -One of: +下列之一: * `POST` * `GET` * `PUT` * `DELETE` -...and the more exotic ones: +...以及更少见的几种: * `OPTIONS` * `HEAD` * `PATCH` * `TRACE` -In the HTTP protocol, you can communicate to each path using one (or more) of these "methods". +在 HTTP 协议中,你可以使用以上的其中一种(或多种)「方法」与每个路径进行通信。 --- -When building APIs, you normally use these specific HTTP methods to perform a specific action. +在开发 API 时,你通常使用特定的 HTTP 方法去执行特定的行为。 -Normally you use: +通常使用: -* `POST`: to create data. -* `GET`: to read data. -* `PUT`: to update data. -* `DELETE`: to delete data. +* `POST`:创建数据。 +* `GET`:读取数据。 +* `PUT`:更新数据。 +* `DELETE`:删除数据。 -So, in OpenAPI, each of the HTTP methods is called an "operation". +因此,在 OpenAPI 中,每一个 HTTP 方法都被称为「操作」。 We are going to call them "**operations**" too. -#### Define a *path operation decorator* +#### 定义一个*路径操作装饰器* ```Python hl_lines="6" {!../../../docs_src/first_steps/tutorial001.py!} ``` -The `@app.get("/")` tells **FastAPI** that the function right below is in charge of handling requests that go to: +`@app.get("/")` 告诉 **FastAPI** 在它下方的函数负责处理如下访问请求: -* the path `/` -* using a get operation +* 请求路径为 `/` +* 使用 get 操作 -!!! info "`@decorator` Info" That `@something` syntax in Python is called a "decorator". +!!! !!! info "`@decorator` Info" `@something` 语法在 Python 中被称为「装饰器」。 - You put it on top of a function. Like a pretty decorative hat (I guess that's where the term came from). + You put it on top of a function. 像一顶漂亮的装饰帽一样,将它放在一个函数的上方(我猜测这个术语的命名就是这么来的)。 - A "decorator" takes the function below and does something with it. + 装饰器接收位于其下方的函数并且用它完成一些工作。 - In our case, this decorator tells **FastAPI** that the function below corresponds to the **path** `/` with an **operation** `get`. + 在我们的例子中,这个装饰器告诉 **FastAPI** 位于其下方的函数对应着**路径** `/` 加上 `get` **操作**。 - It is the "**path operation decorator**". + 它是一个「**路径操作装饰器**」。 -You can also use the other operations: +你也可以使用其他的操作: * `@app.post()` * `@app.put()` * `@app.delete()` -And the more exotic ones: +以及更少见的: * `@app.options()` * `@app.head()` * `@app.patch()` * `@app.trace()` -!!! tip - You are free to use each operation (HTTP method) as you wish. +!!! !!! tip + 您可以随意使用任何一个操作(HTTP方法)。 - **FastAPI** doesn't enforce any specific meaning. + **FastAPI** 没有强制要求操作有任何特定的含义。 - The information here is presented as a guideline, not a requirement. + 此处提供的信息仅作为指导,而不是要求。 - For example, when using GraphQL you normally perform all the actions using only `POST` operations. + 比如,当使用 GraphQL 时通常你所有的动作都通过 `post` 一种方法执行。 -### Step 4: define the **path operation function** +### 步骤 4:定义**路径操作函数** -This is our "**path operation function**": +这是我们的「**路径操作函数**」: -* **path**: is `/`. -* **operation**: is `get`. -* **function**: is the function below the "decorator" (below `@app.get("/")`). +* **路径**:是 `/`。 +* **操作**:是 `get`。 +* **函数**:是位于「装饰器」下方的函数(位于 `@app.get("/")` 下方)。 ```Python hl_lines="7" {!../../../docs_src/first_steps/tutorial001.py!} ``` -This is a Python function. +这是一个 Python 函数。 -It will be called by **FastAPI** whenever it receives a request to the URL "`/`" using a `GET` operation. +每当 **FastAPI** 接收一个使用 `GET` 方法访问 URL「`/`」的请求时这个函数会被调用。 -In this case, it is an `async` function. +在这个例子中,它是一个 `async` 函数。 --- -You could also define it as a normal function instead of `async def`: +你也可以将其定义为常规函数而不使用 `async def`: ```Python hl_lines="7" {!../../../docs_src/first_steps/tutorial003.py!} ``` -!!! note - If you don't know the difference, check the [Async: *"In a hurry?"*](../async.md#in-a-hurry){.internal-link target=_blank}. +!!! !!! note + 如果你不知道两者的区别,请查阅 [Async: *"In a hurry?"*](https://fastapi.tiangolo.com/async/#in-a-hurry){.internal-link target=_blank}。 -### Step 5: return the content +### 步骤 5:返回内容 ```Python hl_lines="8" {!../../../docs_src/first_steps/tutorial001.py!} ``` -You can return a `dict`, `list`, singular values as `str`, `int`, etc. +你可以返回一个 `dict`、`list`,像 `str`、`int` 一样的单个值,等等。 -You can also return Pydantic models (you'll see more about that later). +你还可以返回 Pydantic 模型(稍后你将了解更多)。 -There are many other objects and models that will be automatically converted to JSON (including ORMs, etc). Try using your favorite ones, it's highly probable that they are already supported. +还有许多其他将会自动转换为 JSON 的对象和模型(包括 ORM 对象等)。 尝试下使用你最喜欢的一种,它很有可能已经被支持。 ## Recap -* Import `FastAPI`. -* Create an `app` instance. -* Write a **path operation decorator** (like `@app.get("/")`). -* Write a **path operation function** (like `def root(): ...` above). -* Run the development server (like `uvicorn main:app --reload`). +* 导入 `FastAPI`。 +* 创建一个 `app` 实例。 +* 编写一个**路径操作装饰器**(如 `@app.get("/")`)。 +* 编写一个**路径操作函数**(如上面的 `def root(): ...`)。 +* 运行开发服务器(如 `uvicorn main:app --reload`)。 From 1c5356f4f1c2535e5d33d39f14824bc06d7442ab Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:43 +0800 Subject: [PATCH 139/163] New translations handling-errors.md (Chinese Simplified) --- docs/zh/docs/tutorial/handling-errors.md | 139 +++++++++++------------ 1 file changed, 68 insertions(+), 71 deletions(-) diff --git a/docs/zh/docs/tutorial/handling-errors.md b/docs/zh/docs/tutorial/handling-errors.md index b150201e797d3..46a31245ea790 100644 --- a/docs/zh/docs/tutorial/handling-errors.md +++ b/docs/zh/docs/tutorial/handling-errors.md @@ -1,53 +1,53 @@ -# Handling Errors +# 处理错误 -There are many situations in where you need to notify an error to a client that is using your API. +某些情况下,需要向客户端返回错误提示。 -This client could be a browser with a frontend, a code from someone else, an IoT device, etc. +这里所谓的客户端包括前端浏览器、其他应用程序、物联网设备等。 You could need to tell the client that: -* The client doesn't have enough privileges for that operation. -* The client doesn't have access to that resource. -* The item the client was trying to access doesn't exist. +* 客户端没有执行操作的权限 +* 客户端没有访问资源的权限 +* 客户端要访问的项目不存在 * etc. -In these cases, you would normally return an **HTTP status code** in the range of **400** (from 400 to 499). +遇到这些情况时,通常要返回 **4XX**(400 至 499)**HTTP 状态码**。 This is similar to the 200 HTTP status codes (from 200 to 299). Those "200" status codes mean that somehow there was a "success" in the request. The status codes in the 400 range mean that there was an error from the client. -Remember all those **"404 Not Found"** errors (and jokes)? +大家都知道**「404 Not Found」**错误,还有调侃这个错误的笑话吧? -## Use `HTTPException` +## 使用 `HTTPException` -To return HTTP responses with errors to the client you use `HTTPException`. +向客户端返回 HTTP 错误响应,可以使用 `HTTPException`。 -### Import `HTTPException` +### 导入 `HTTPException` ```Python hl_lines="1" {!../../../docs_src/handling_errors/tutorial001.py!} ``` -### Raise an `HTTPException` in your code +### 触发 `HTTPException` -`HTTPException` is a normal Python exception with additional data relevant for APIs. +`HTTPException` 是额外包含了和 API 有关数据的常规 Python 异常。 -Because it's a Python exception, you don't `return` it, you `raise` it. +因为是 Python 异常,所以不能 `return`,只能 `raise`。 -This also means that if you are inside a utility function that you are calling inside of your *path operation function*, and you raise the `HTTPException` from inside of that utility function, it won't run the rest of the code in the *path operation function*, it will terminate that request right away and send the HTTP error from the `HTTPException` to the client. +如在调用*路径操作函数*里的工具函数时,触发了 `HTTPException`,FastAPI 就不再继续执行*路径操作函数*中的后续代码,而是立即终止请求,并把 `HTTPException` 的 HTTP 错误发送至客户端。 -The benefit of raising an exception over `return`ing a value will be more evident in the section about Dependencies and Security. +在介绍依赖项与安全的章节中,您可以了解更多用 `raise` 异常代替 `return` 值的优势。 -In this example, when the client requests an item by an ID that doesn't exist, raise an exception with a status code of `404`: +本例中,客户端用 `ID` 请求的 `item` 不存在时,触发状态码为 `404` 的异常: ```Python hl_lines="11" {!../../../docs_src/handling_errors/tutorial001.py!} ``` -### The resulting response +### 响应结果 -If the client requests `http://example.com/items/foo` (an `item_id` `"foo"`), that client will receive an HTTP status code of 200, and a JSON response of: +请求为 `http://example.com/items/foo`(`item_id` 为 `「foo」`)时,客户端会接收到 HTTP 状态码 - 200 及如下 JSON 响应结果: ```JSON { @@ -55,7 +55,7 @@ If the client requests `http://example.com/items/foo` (an `item_id` `"foo"`), th } ``` -But if the client requests `http://example.com/items/bar` (a non-existent `item_id` `"bar"`), that client will receive an HTTP status code of 404 (the "not found" error), and a JSON response of: +但如果客户端请求 `http://example.com/items/bar`(`item_id` `「bar」` 不存在时),则会接收到 HTTP 状态码 - 404(「未找到」错误)及如下 JSON 响应结果: ```JSON { @@ -63,77 +63,74 @@ But if the client requests `http://example.com/items/bar` (a non-existent `item_ } ``` -!!! tip - When raising an `HTTPException`, you can pass any value that can be converted to JSON as the parameter `detail`, not only `str`. +!!! 触发 `HTTPException` 时,可以用参数 `detail` 传递任何能转换为 JSON 的值,不仅限于 `str`。 - You could pass a `dict`, a `list`, etc. - - They are handled automatically by **FastAPI** and converted to JSON. + 还支持传递 `dict`、`list` 等数据结构。 **FastAPI** 能自动处理这些数据,并将之转换为 JSON。 -## Add custom headers +## 添加自定义响应头 -There are some situations in where it's useful to be able to add custom headers to the HTTP error. For example, for some types of security. +有些场景下要为 HTTP 错误添加自定义响应头。 例如,出于某些方面的安全需要。 -You probably won't need to use it directly in your code. +一般情况下可能不会需要在代码中直接使用响应头。 -But in case you needed it for an advanced scenario, you can add custom headers: +但对于某些高级应用场景,还是需要添加自定义响应头: ```Python hl_lines="14" {!../../../docs_src/handling_errors/tutorial002.py!} ``` -## Install custom exception handlers +## 安装自定义异常处理器 -You can add custom exception handlers with the same exception utilities from Starlette. +添加自定义处理器,要使用 [Starlette 的异常工具](https://www.starlette.io/exceptions/)。 -Let's say you have a custom exception `UnicornException` that you (or a library you use) might `raise`. +假设要触发的自定义异常叫作 `UnicornException`。 -And you want to handle this exception globally with FastAPI. +且需要 FastAPI 实现全局处理该异常。 -You could add a custom exception handler with `@app.exception_handler()`: +此时,可以用 `@app.exception_handler()` 添加自定义异常控制器: ```Python hl_lines="5-7 13-18 24" {!../../../docs_src/handling_errors/tutorial003.py!} ``` -Here, if you request `/unicorns/yolo`, the *path operation* will `raise` a `UnicornException`. +请求 `/unicorns/yolo` 时,路径操作会触发 `UnicornException`。 -But it will be handled by the `unicorn_exception_handler`. +但该异常将会被 `unicorn_exception_handler` 处理。 -So, you will receive a clean error, with an HTTP status code of `418` and a JSON content of: +接收到的错误信息清晰明了,HTTP 状态码为 `418`,JSON 内容如下: ```JSON -{"message": "Oops! yolo did something. There goes a rainbow..."} +{"message": "Oops! yolo did something. {"message": "Oops! yolo did something. There goes a rainbow..."} ``` !!! note "Technical Details" You could also use `from starlette.requests import Request` and `from starlette.responses import JSONResponse`. - **FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. The same with `Request`. + `from starlette.requests import Request` 和 `from starlette.responses import JSONResponse` 也可以用于导入 `Request` 和 `JSONResponse`。 **FastAPI** 提供了与 `starlette.responses` 相同的 `fastapi.responses` 作为快捷方式,但大部分响应操作都可以直接从 Starlette 导入。 同理,`Request` 也是如此。 ## Override the default exception handlers -**FastAPI** has some default exception handlers. +**FastAPI** 自带了一些默认异常处理器。 -These handlers are in charge of returning the default JSON responses when you `raise` an `HTTPException` and when the request has invalid data. +触发 `HTTPException` 或请求无效数据时,这些处理器返回默认的 JSON 响应结果。 You can override these exception handlers with your own. -### Override request validation exceptions +### 覆盖请求验证异常 -When a request contains invalid data, **FastAPI** internally raises a `RequestValidationError`. +请求中包含无效数据时,**FastAPI** 内部会触发 `RequestValidationError`。 And it also includes a default exception handler for it. -To override it, import the `RequestValidationError` and use it with `@app.exception_handler(RequestValidationError)` to decorate the exception handler. +覆盖默认异常处理器时需要导入 `RequestValidationError`,并用 `@app.excption_handler(RequestValidationError)` 装饰异常处理器。 -The exception handler will receive a `Request` and the exception. +这样,异常处理器就可以接收 `Request` 与异常。 ```Python hl_lines="2 14-16" {!../../../docs_src/handling_errors/tutorial004.py!} ``` -Now, if you go to `/items/foo`, instead of getting the default JSON error with: +访问 `/items/foo`,可以看到以下内容替换了默认 JSON 错误信息: ```JSON { @@ -163,21 +160,21 @@ path -> item_id !!! warning These are technical details that you might skip if it's not important for you now. -`RequestValidationError` is a sub-class of Pydantic's `ValidationError`. +`RequestValidationError` 是 Pydantic 的 `ValidationError` 的子类。 -**FastAPI** uses it so that, if you use a Pydantic model in `response_model`, and your data has an error, you will see the error in your log. +**FastAPI** 调用的就是 `RequestValidationError` 类,因此,如果在 `response_model` 中使用 Pydantic 模型,且数据有错误时,在日志中就会看到这个错误。 -But the client/user will not see it. Instead, the client will receive an "Internal Server Error" with a HTTP status code `500`. +但客户端或用户看不到这个错误。 反之,客户端接收到的是 HTTP 状态码为 `500` 的「内部服务器错误」。 -It should be this way because if you have a Pydantic `ValidationError` in your *response* or anywhere in your code (not in the client's *request*), it's actually a bug in your code. +这是因为在*响应*或代码(不是在客户端的请求里)中出现的 Pydantic `ValidationError` 是代码的 bug。 -And while you fix it, your clients/users shouldn't have access to internal information about the error, as that could expose a security vulnerability. +修复错误时,客户端或用户不能访问错误的内部信息,否则会造成安全隐患。 -### Override the `HTTPException` error handler +### 覆盖 `HTTPException` 错误处理器 -The same way, you can override the `HTTPException` handler. +同理,也可以覆盖 `HTTPException` 处理器。 -For example, you could want to return a plain text response instead of JSON for these errors: +例如,只为错误返回纯文本响应,而不是返回 JSON 格式的内容: ```Python hl_lines="3-4 9-11 22" {!../../../docs_src/handling_errors/tutorial004.py!} @@ -186,19 +183,19 @@ For example, you could want to return a plain text response instead of JSON for !!! note "Technical Details" You could also use `from starlette.responses import PlainTextResponse`. - **FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. + 还可以使用 `from starlette.responses import PlainTextResponse`。 **FastAPI** 提供了与 `starlette.responses` 相同的 `fastapi.responses` 作为快捷方式,但大部分响应都可以直接从 Starlette 导入。 -### Use the `RequestValidationError` body +### 使用 `RequestValidationError` 的请求体 -The `RequestValidationError` contains the `body` it received with invalid data. +`RequestValidationError` 包含其接收到的无效数据请求的 `body` 。 -You could use it while developing your app to log the body and debug it, return it to the user, etc. +开发时,可以用这个请求体生成日志、调试错误,并返回给用户。 ```Python hl_lines="14" {!../../../docs_src/handling_errors/tutorial005.py!} ``` -Now try sending an invalid item like: +现在试着发送一个无效的 `item`,例如: ```JSON { @@ -207,7 +204,7 @@ Now try sending an invalid item like: } ``` -You will receive a response telling you that the data is invalid containing the received body: +收到的响应包含 `body` 信息,并说明数据是无效的: ```JSON hl_lines="12-15" { @@ -228,31 +225,31 @@ You will receive a response telling you that the data is invalid containing the } ``` -#### FastAPI's `HTTPException` vs Starlette's `HTTPException` +#### FastAPI `HTTPException` vs Starlette `HTTPException` -**FastAPI** has its own `HTTPException`. +**FastAPI** 也提供了自有的 `HTTPException`。 -And **FastAPI**'s `HTTPException` error class inherits from Starlette's `HTTPException` error class. +**FastAPI** 的 `HTTPException` 继承自 Starlette 的 `HTTPException` 错误类。 -The only difference, is that **FastAPI**'s `HTTPException` allows you to add headers to be included in the response. +它们之间的唯一区别是,**FastAPI** 的 `HTTPException` 可以在响应中添加响应头。 -This is needed/used internally for OAuth 2.0 and some security utilities. +OAuth 2.0 等安全工具需要在内部调用这些响应头。 -So, you can keep raising **FastAPI**'s `HTTPException` as normally in your code. +因此你可以继续像平常一样在代码中触发 **FastAPI** 的 `HTTPException` 。 -But when you register an exception handler, you should register it for Starlette's `HTTPException`. +但注册异常处理器时,应该注册到来自 Starlette 的 `HTTPException`。 -This way, if any part of Starlette's internal code, or a Starlette extension or plug-in, raises a Starlette `HTTPException`, your handler will be able to catch and handle it. +这样做是为了,当 Starlette 的内部代码、扩展或插件触发 Starlette `HTTPException` 时,处理程序能够捕获、并处理此异常。 -In this example, to be able to have both `HTTPException`s in the same code, Starlette's exceptions is renamed to `StarletteHTTPException`: +注意,本例代码中同时使用了这两个 `HTTPException`,此时,要把 Starlette 的 `HTTPException` 命名为 `StarletteHTTPException`: ```Python from starlette.exceptions import HTTPException as StarletteHTTPException ``` -### Re-use **FastAPI**'s exception handlers +### 复用 **FastAPI** 异常处理器 -If you want to use the exception along with the same default exception handlers from **FastAPI**, You can import and re-use the default exception handlers from `fastapi.exception_handlers`: +FastAPI 支持先对异常进行某些处理,然后再使用 **FastAPI** 中处理该异常的默认异常处理器。 ```Python hl_lines="2-5 15 21" {!../../../docs_src/handling_errors/tutorial006.py!} From 935cf2646a4327dfd34dcb595186d26fe48d5dfe Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:44 +0800 Subject: [PATCH 140/163] New translations header-params.md (Chinese Simplified) --- docs/zh/docs/tutorial/header-params.md | 74 ++++++++++++++------------ 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/docs/zh/docs/tutorial/header-params.md b/docs/zh/docs/tutorial/header-params.md index 9e928cdc6b482..945345f38ef25 100644 --- a/docs/zh/docs/tutorial/header-params.md +++ b/docs/zh/docs/tutorial/header-params.md @@ -1,10 +1,10 @@ -# Header Parameters +# Header 参数 -You can define Header parameters the same way you define `Query`, `Path` and `Cookie` parameters. +你可以使用定义 `Query`, `Path` 和 `Cookie` 参数一样的方法定义 Header 参数。 -## Import `Header` +## 导入 `Header` -First import `Header`: +首先导入 `Header`: === "Python 3.10+" @@ -30,7 +30,7 @@ First import `Header`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="1" - {!> ../../../docs_src/header_params/tutorial001_py310.py!} + {!../../../docs_src/header_params/tutorial001.py!} ``` === "Python 3.6+ non-Annotated" @@ -39,14 +39,14 @@ First import `Header`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="3" - {!> ../../../docs_src/header_params/tutorial001.py!} + {!../../../docs_src/header_params/tutorial002.py!} ``` -## Declare `Header` parameters +## 声明 `Header` 参数 -Then declare the header parameters using the same structure as with `Path`, `Query` and `Cookie`. +然后使用和`Path`, `Query` and `Cookie` 一样的结构定义 header 参数 -The first value is the default value, you can pass all the extra validation or annotation parameters: +第一个值是默认值,你可以传递所有的额外验证或注释参数: === "Python 3.10+" @@ -84,29 +84,29 @@ The first value is the default value, you can pass all the extra validation or a {!> ../../../docs_src/header_params/tutorial001.py!} ``` -!!! note "Technical Details" - `Header` is a "sister" class of `Path`, `Query` and `Cookie`. It also inherits from the same common `Param` class. +!!! !!! note "技术细节" + `Header` 是 `Path`, `Query` 和 `Cookie` 的兄弟类型。 它也继承自通用的 `Param` 类. - But remember that when you import `Query`, `Path`, `Header`, and others from `fastapi`, those are actually functions that return special classes. + 但是请记得,当你从`fastapi`导入 `Query`, `Path`, `Header`, 或其他时,实际上导入的是返回特定类型的函数。 !!! info To declare headers, you need to use `Header`, because otherwise the parameters would be interpreted as query parameters. -## Automatic conversion +## 自动转换 -`Header` has a little extra functionality on top of what `Path`, `Query` and `Cookie` provide. +`Header` 在 `Path`, `Query` 和 `Cookie` 提供的功能之上有一点额外的功能。 -Most of the standard headers are separated by a "hyphen" character, also known as the "minus symbol" (`-`). +大多数标准的headers用 "连字符" 分隔,也称为 "减号" (`-`)。 -But a variable like `user-agent` is invalid in Python. +但是像 `user-agent` 这样的变量在Python中是无效的。 -So, by default, `Header` will convert the parameter names characters from underscore (`_`) to hyphen (`-`) to extract and document the headers. +因此, 默认情况下, `Header` 将把参数名称的字符从下划线 (`_`) 转换为连字符 (`-`) 来提取并记录 headers. -Also, HTTP headers are case-insensitive, so, you can declare them with standard Python style (also known as "snake_case"). +同时,HTTP headers 是大小写不敏感的,因此,因此可以使用标准Python样式(也称为 "snake_case")声明它们。 -So, you can use `user_agent` as you normally would in Python code, instead of needing to capitalize the first letters as `User_Agent` or something similar. +因此,您可以像通常在Python代码中那样使用 `user_agent` ,而不需要将首字母大写为 `User_Agent` 或类似的东西。 -If for some reason you need to disable automatic conversion of underscores to hyphens, set the parameter `convert_underscores` of `Header` to `False`: +如果出于某些原因,你需要禁用下划线到连字符的自动转换,设置`Header`的参数 `convert_underscores` 为 `False`: === "Python 3.10+" @@ -117,8 +117,11 @@ If for some reason you need to disable automatic conversion of underscores to hy === "Python 3.9+" ```Python hl_lines="11" - {!> ../../../docs_src/header_params/tutorial002_an_py39.py!} + !!! info + 为了声明headers, 你需要使用Header, 因为否则参数将被解释为查询参数。 ``` +, 因为否则参数将被解释为查询参数。 + === "Python 3.6+" @@ -141,21 +144,21 @@ If for some reason you need to disable automatic conversion of underscores to hy Prefer to use the `Annotated` version if possible. ```Python hl_lines="10" - {!> ../../../docs_src/header_params/tutorial002.py!} + {!../../../docs_src/header_params/tutorial001.py!} ``` !!! warning Before setting `convert_underscores` to `False`, bear in mind that some HTTP proxies and servers disallow the usage of headers with underscores. -## Duplicate headers +## 重复的 headers -It is possible to receive duplicate headers. That means, the same header with multiple values. +有可能收到重复的headers。 这意味着,相同的header具有多个值。 -You can define those cases using a list in the type declaration. +您可以在类型声明中使用一个list来定义这些情况。 -You will receive all the values from the duplicate header as a Python `list`. +你可以通过一个Python `list` 的形式获得重复header的所有值。 -For example, to declare a header of `X-Token` that can appear more than once, you can write: +比如, 为了声明一个 `X-Token` header 可以出现多次,你可以这样写: === "Python 3.10+" @@ -190,8 +193,11 @@ For example, to declare a header of `X-Token` that can appear more than once, yo Prefer to use the `Annotated` version if possible. ```Python hl_lines="9" - {!> ../../../docs_src/header_params/tutorial003_py39.py!} + !!! warning + 在设置 convert_underscoresFalse 之前,请记住,一些HTTP代理和服务器不允许使用带有下划线的headers。 ``` + 为 False 之前,请记住,一些HTTP代理和服务器不允许使用带有下划线的headers。 + === "Python 3.6+ non-Annotated" @@ -199,17 +205,17 @@ For example, to declare a header of `X-Token` that can appear more than once, yo Prefer to use the `Annotated` version if possible. ```Python hl_lines="9" - {!> ../../../docs_src/header_params/tutorial003.py!} + {!../../../docs_src/header_params/tutorial003.py!} ``` -If you communicate with that *path operation* sending two HTTP headers like: +如果你与*路径操作*通信时发送两个HTTP headers,就像: ``` X-Token: foo X-Token: bar ``` -The response would be like: +响应会是: ```JSON { @@ -220,8 +226,8 @@ The response would be like: } ``` -## Recap +## 回顾 -Declare headers with `Header`, using the same common pattern as `Query`, `Path` and `Cookie`. +使用 `Header` 来声明 header , 使用和 `Query`, `Path` 与 `Cookie` 相同的模式。 -And don't worry about underscores in your variables, **FastAPI** will take care of converting them. +不用担心变量中的下划线,**FastAPI** 会负责转换它们。 From 81e05aab9faef4bfcc8a4d81843e87a8eef59e22 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:45 +0800 Subject: [PATCH 141/163] New translations index.md (Chinese Simplified) --- docs/zh/docs/tutorial/index.md | 42 ++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/docs/zh/docs/tutorial/index.md b/docs/zh/docs/tutorial/index.md index d592f4744650a..2753a8e3c9442 100644 --- a/docs/zh/docs/tutorial/index.md +++ b/docs/zh/docs/tutorial/index.md @@ -1,6 +1,6 @@ -# Tutorial - User Guide +# 教程 - 用户指南 -This tutorial shows you how to use **FastAPI** with most of its features, step by step. +本教程将一步步向你展示如何使用 **FastAPI** 的绝大部分特性。 Each section gradually builds on the previous ones, but it's structured to separate topics, so that you can go directly to any specific one to solve your specific API needs. @@ -8,11 +8,11 @@ It is also built to work as a future reference. So you can come back and see exactly what you need. -## Run the code +## 运行代码 -All the code blocks can be copied and used directly (they are actually tested Python files). +所有代码片段都可以复制后直接使用(它们实际上是经过测试的 Python 文件)。 -To run any of the examples, copy the code to a file `main.py`, and start `uvicorn` with: +要运行任何示例,请将代码复制到 `main.py` 文件中,然后使用以下命令启动 `uvicorn`:
@@ -25,20 +25,22 @@ $ uvicorn main:app --reload INFO: Waiting for application startup. INFO: Application startup complete. ``` +INFO: Application startup complete. +```
It is **HIGHLY encouraged** that you write or copy the code, edit it and run it locally. -Using it in your editor is what really shows you the benefits of FastAPI, seeing how little code you have to write, all the type checks, autocompletion, etc. +在编辑器中使用 FastAPI 会真正地展现出它的优势:只需要编写很少的代码,所有的类型检查,代码补全等等。 --- -## Install FastAPI +## 安装 FastAPI -The first step is to install FastAPI. +第一个步骤是安装 FastAPI。 -For the tutorial, you might want to install it with all the optional dependencies and features: +为了使用本教程,你可能需要安装所有的可选依赖及对应功能:
@@ -50,33 +52,33 @@ $ pip install "fastapi[all]"
-...that also includes `uvicorn`, that you can use as the server that runs your code. +......以上安装还包括了 `uvicorn`,你可以将其用作运行代码的服务器。 -!!! note - You can also install it part by part. +!!! !!! note + 你也可以分开来安装。 - This is what you would probably do once you want to deploy your application to production: + 假如你想将应用程序部署到生产环境,你可能要执行以下操作: ``` pip install fastapi ``` - Also install `uvicorn` to work as the server: + 并且安装`uvicorn`来作为服务器: ``` pip install "uvicorn[standard]" ``` - And the same for each of the optional dependencies that you want to use. + 然后对你想使用的每个可选依赖项也执行相同的操作。 -## Advanced User Guide +## 进阶用户指南 -There is also an **Advanced User Guide** that you can read later after this **Tutorial - User guide**. +在本**教程-用户指南**之后,你可以阅读**进阶用户指南**。 -The **Advanced User Guide**, builds on this, uses the same concepts, and teaches you some extra features. +**进阶用户指南**以本教程为基础,使用相同的概念,并教授一些额外的特性。 -But you should first read the **Tutorial - User Guide** (what you are reading right now). +但是你应该先阅读**教程-用户指南**(即你现在正在阅读的内容)。 -It's designed so that you can build a complete application with just the **Tutorial - User Guide**, and then extend it in different ways, depending on your needs, using some of the additional ideas from the **Advanced User Guide**. +教程经过精心设计,使你可以仅通过**教程-用户指南**来开发一个完整的应用程序,然后根据你的需要,使用**进阶用户指南**中的一些其他概念,以不同的方式来扩展它。 From 1949c1b6d2d2e2f1ad682e3f82cd2474ce5ba3da Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:46 +0800 Subject: [PATCH 142/163] New translations metadata.md (Chinese Simplified) --- docs/zh/docs/tutorial/metadata.md | 98 +++++++++++++++---------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/docs/zh/docs/tutorial/metadata.md b/docs/zh/docs/tutorial/metadata.md index e0722c2e3a9a2..209920b95de28 100644 --- a/docs/zh/docs/tutorial/metadata.md +++ b/docs/zh/docs/tutorial/metadata.md @@ -1,22 +1,22 @@ -# Metadata and Docs URLs +# 元数据和文档 URL -You can customize several metadata configurations in your **FastAPI** application. +你可以在 **FastAPI** 应用中自定义几个元数据配置。 ## Metadata for API -You can set the following fields that are used in the OpenAPI specification and the automatic API docs UIs: +**Description**:在 OpenAPI 和自动 API 文档用户界面中用作 API 的描述。 +| The license information for the exposed API. It can contain several fields.
`license_info` fields -| Parameter | Type | Description | -| ------------------ | -------- | --------------------------------------------------------------------------------------------------------- | -| `title` | `str` | The title of the API. | -| `summary` | `str` | A short summary of the API. Available since OpenAPI 3.1.0, FastAPI 0.99.0. | -| `description` | `str` | A short description of the API. It can use Markdown. | -| `version` | `string` | The version of the API. This is the version of your own application, not of OpenAPI. For example `2.5.0`. | -| `terms_of_service` | `str` | A URL to the Terms of Service for the API. If provided, this has to be a URL. | -| `contact` | `dict` | The contact information for the exposed API. It can contain several fields.
contact fields
ParameterTypeDescription
namestrThe identifying name of the contact person/organization.
urlstrThe URL pointing to the contact information. MUST be in the format of a URL.
emailstrThe email address of the contact person/organization. MUST be in the format of an email address.
| -| `license_info` | `dict` | The license information for the exposed API. It can contain several fields.
license_info fields
ParameterTypeDescription
namestrREQUIRED (if a license_info is set). The license name used for the API.
identifierstrAn SPDX license expression for the API. The identifier field is mutually exclusive of the url field. Available since OpenAPI 3.1.0, FastAPI 0.99.0.
urlstrA URL to the license used for the API. MUST be in the format of a URL.
| +| Parameter | Type | Description | +| ----------------------------------------------------------------------------------------------------------------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | `str` | **REQUIRED** (if a `license_info` is set). The license name used for the API. | +| `identifier` | `str` | An SPDX license expression for the API. The `identifier` field is mutually exclusive of the `url` field. Available since OpenAPI 3.1.0, FastAPI 0.99.0. | +| `!!! 信息 + 阅读更多关于标签的信息路径操作配置{.internal-link target=_blank}。` | `str` | A URL to the license used for the API. MUST be in the format of a URL. | +
| +|| -You can set them as follows: +你可以设定: ```Python hl_lines="3-16 19-32" {!../../../docs_src/metadata/tutorial001.py!} @@ -25,7 +25,7 @@ You can set them as follows: !!! tip You can write Markdown in the `description` field and it will be rendered in the output. -With this configuration, the automatic API docs would look like: +通过这样设置,自动 API 文档看起来会像: @@ -39,38 +39,38 @@ For example: {!../../../docs_src/metadata/tutorial001_1.py!} ``` -## Metadata for tags +## 标签元数据 -You can also add additional metadata for the different tags used to group your path operations with the parameter `openapi_tags`. +你也可以使用参数 `openapi_tags`,为用于分组路径操作的不同标签添加额外的元数据。 -It takes a list containing one dictionary for each tag. +它接受一个列表,这个列表包含每个标签对应的一个字典。 -Each dictionary can contain: +每个字典可以包含: -* `name` (**required**): a `str` with the same tag name you use in the `tags` parameter in your *path operations* and `APIRouter`s. -* `description`: a `str` with a short description for the tag. It can have Markdown and will be shown in the docs UI. -* `externalDocs`: a `dict` describing external documentation with: - * `description`: a `str` with a short description for the external docs. - * `url` (**required**): a `str` with the URL for the external documentation. +* `name`(**必要**):一个 `str`,它与*路径操作*和 `APIRouter` 中使用的 `tags` 参数有相同的标签名。 +* `description`:一个用于简短描述标签的 `str`。 它支持 Markdown 并且会在文档用户界面中显示。 +* `externalDocs`:一个描述外部文档的 `dict`: + * `description`:用于简短描述外部文档的 `str`。 + * `url`(**必要**):外部文档的 URL `str`。 -### Create metadata for tags +### 创建标签元数据 -Let's try that in an example with tags for `users` and `items`. +让我们在带有标签的示例中为 `users` 和 `items` 试一下。 -Create metadata for your tags and pass it to the `openapi_tags` parameter: +创建标签元数据并把它传递给 `openapi_tags` 参数: ```Python hl_lines="3-16 18" {!../../../docs_src/metadata/tutorial004.py!} ``` -Notice that you can use Markdown inside of the descriptions, for example "login" will be shown in bold (**login**) and "fancy" will be shown in italics (_fancy_). +注意你可以在描述内使用 Markdown,例如「login」会显示为粗体(**login**)以及「fancy」会显示为斜体(_fancy_)。 -!!! tip - You don't have to add metadata for all the tags that you use. +!!! !!! 提示 + 不必为你使用的所有标签都添加元数据。 -### Use your tags +### 使用你的标签 -Use the `tags` parameter with your *path operations* (and `APIRouter`s) to assign them to different tags: +将 `tags` 参数和*路径操作*(以及 `APIRouter`)一起使用,将其分配给不同的标签: ```Python hl_lines="21 26" {!../../../docs_src/metadata/tutorial004.py!} @@ -79,44 +79,44 @@ Use the `tags` parameter with your *path operations* (and `APIRouter`s) to assig !!! info Read more about tags in [Path Operation Configuration](../path-operation-configuration/#tags){.internal-link target=_blank}. -### Check the docs +### 查看文档 -Now, if you check the docs, they will show all the additional metadata: +如果你现在查看文档,它们会显示所有附加的元数据: -### Order of tags +### 标签顺序 -The order of each tag metadata dictionary also defines the order shown in the docs UI. +每个标签元数据字典的顺序也定义了在文档用户界面显示的顺序。 -For example, even though `users` would go after `items` in alphabetical order, it is shown before them, because we added their metadata as the first dictionary in the list. +例如按照字母顺序,即使 `users` 排在 `items` 之后,它也会显示在前面,因为我们将它的元数据添加为列表内的第一个字典。 ## OpenAPI URL -By default, the OpenAPI schema is served at `/openapi.json`. +默认情况下,OpenAPI 模式服务于 `/openapi.json`。 -But you can configure it with the parameter `openapi_url`. +但是你可以通过参数 `openapi_url` 对其进行配置。 -For example, to set it to be served at `/api/v1/openapi.json`: +例如,将其设置为服务于 `/api/v1/openapi.json`: ```Python hl_lines="3" {!../../../docs_src/metadata/tutorial002.py!} ``` -If you want to disable the OpenAPI schema completely you can set `openapi_url=None`, that will also disable the documentation user interfaces that use it. +如果你想完全禁用 OpenAPI 模式,可以将其设置为 `openapi_url=None`,这样也会禁用使用它的文档用户界面。 -## Docs URLs +## 文档 URLs -You can configure the two documentation user interfaces included: +你可以配置两个文档用户界面,包括: -* **Swagger UI**: served at `/docs`. - * You can set its URL with the parameter `docs_url`. - * You can disable it by setting `docs_url=None`. -* **ReDoc**: served at `/redoc`. - * You can set its URL with the parameter `redoc_url`. - * You can disable it by setting `redoc_url=None`. +* **Swagger UI**:服务于 `/docs`。 + * 可以使用参数 `docs_url` 设置它的 URL。 + * 可以通过设置 `docs_url=None` 禁用它。 +* **Version**:API 版本,例如 `v2` 或者 `2.5.0`。 + * 可以使用参数 `redoc_url` 设置它的 URL。 + * 可以通过设置 `redoc_url=None` 禁用它。 -For example, to set Swagger UI to be served at `/documentation` and disable ReDoc: +例如,设置 Swagger UI 服务于 `/documentation` 并禁用 ReDoc: ```Python hl_lines="3" {!../../../docs_src/metadata/tutorial003.py!} From b4af5cf316d568f57e8a1de6f3f86d5b44e0ca7c Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:47 +0800 Subject: [PATCH 143/163] New translations middleware.md (Chinese Simplified) --- docs/zh/docs/tutorial/middleware.md | 64 ++++++++++++++--------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/docs/zh/docs/tutorial/middleware.md b/docs/zh/docs/tutorial/middleware.md index 3c6868fe4de73..8be591d8ee697 100644 --- a/docs/zh/docs/tutorial/middleware.md +++ b/docs/zh/docs/tutorial/middleware.md @@ -1,61 +1,61 @@ -# Middleware +# 中间件 -You can add middleware to **FastAPI** applications. +你可以向 **FastAPI** 应用添加中间件. -A "middleware" is a function that works with every **request** before it is processed by any specific *path operation*. And also with every **response** before returning it. +"中间件"是一个函数,它在每个**请求**被特定的*路径操作*处理之前,以及在每个**响应**返回之前工作. And also with every **response** before returning it. -* It takes each **request** that comes to your application. -* It can then do something to that **request** or run any needed code. -* Then it passes the **request** to be processed by the rest of the application (by some *path operation*). -* It then takes the **response** generated by the application (by some *path operation*). -* It can do something to that **response** or run any needed code. -* Then it returns the **response**. +* 它接收你的应用程序的每一个**请求**. +* 然后它可以对这个**请求**做一些事情或者执行任何需要的代码. +* 然后它将**请求**传递给应用程序的其他部分 (通过某种*路径操作*). +* 然后它获取应用程序生产的**响应** (通过某种*路径操作*). +* 它可以对该**响应**做些什么或者执行任何需要的代码. +* 然后它返回这个 **响应**. -!!! note "Technical Details" - If you have dependencies with `yield`, the exit code will run *after* the middleware. +!!! !!! note "技术细节" + 如果你使用了 `yield` 关键字依赖, 依赖中的退出代码将在执行中间件*后*执行. - If there were any background tasks (documented later), they will run *after* all the middleware. + 如果有任何后台任务(稍后记录), 它们将在执行中间件*后*运行. -## Create a middleware +## 创建中间件 -To create a middleware you use the decorator `@app.middleware("http")` on top of a function. +要创建中间件你可以在函数的顶部使用装饰器 `@app.middleware("http")`. The middleware function receives: -* The `request`. -* A function `call_next` that will receive the `request` as a parameter. - * This function will pass the `request` to the corresponding *path operation*. - * Then it returns the `response` generated by the corresponding *path operation*. -* You can then modify further the `response` before returning it. +* `request`. +* 一个函数 `call_next` 它将接收 `request` 作为参数. + * 这个函数将 `request` 传递给相应的 *路径操作*. + * 然后它将返回由相应的*路径操作*生成的 `response`. +* 然后你可以在返回 `response` 前进一步修改它. ```Python hl_lines="8-9 11 14" {!../../../docs_src/middleware/tutorial001.py!} ``` -!!! tip - Have in mind that custom proprietary headers can be added using the 'X-' prefix. +!!! !!! tip + 请记住可以 用'X-' 前缀添加专有自定义请求头. - But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your CORS configurations ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) using the parameter `expose_headers` documented in Starlette's CORS docs. + 但是如果你想让浏览器中的客户端看到你的自定义请求头, 你需要把它们加到 CORS 配置 ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) 的 `expose_headers` 参数中,在 Starlette's CORS docs文档中. -!!! note "Technical Details" - You could also use `from starlette.requests import Request`. +!!! !!! note "技术细节" + 你也可以使用 `from starlette.requests import Request`. - **FastAPI** provides it as a convenience for you, the developer. But it comes directly from Starlette. + **FastAPI** 为了开发者方便提供了该对象. 但其实它直接来自于 Starlette. But it comes directly from Starlette. -### Before and after the `response` +### 在 `response` 的前和后 -You can add code to be run with the `request`, before any *path operation* receives it. +在任何*路径操作*收到`request`前,可以添加要和请求一起运行的代码. -And also after the `response` is generated, before returning it. +也可以在*响应*生成但是返回之前添加代码. -For example, you could add a custom header `X-Process-Time` containing the time in seconds that it took to process the request and generate a response: +例如你可以添加自定义请求头 `X-Process-Time` 包含以秒为单位的接收请求和生成响应的时间: ```Python hl_lines="10 12-13" {!../../../docs_src/middleware/tutorial001.py!} ``` -## Other middlewares +## 其他中间件 -You can later read more about other middlewares in the [Advanced User Guide: Advanced Middleware](../advanced/middleware.md){.internal-link target=_blank}. +你可以稍后在 [Advanced User Guide: Advanced Middleware](../advanced/middleware.md){.internal-link target=_blank}阅读更多关于中间件的教程. -You will read about how to handle CORS with a middleware in the next section. +你将在下一节中学习如何使用中间件处理 CORS . From 84da9df9da71489d3625385d6e3ebc9065b7cdf7 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:49 +0800 Subject: [PATCH 144/163] New translations path-operation-configuration.md (Chinese Simplified) --- .../tutorial/path-operation-configuration.md | 84 ++++++++++--------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/docs/zh/docs/tutorial/path-operation-configuration.md b/docs/zh/docs/tutorial/path-operation-configuration.md index ee3b5cd0ad9f6..7102406acc732 100644 --- a/docs/zh/docs/tutorial/path-operation-configuration.md +++ b/docs/zh/docs/tutorial/path-operation-configuration.md @@ -1,66 +1,65 @@ -# Path Operation Configuration +# 路径操作配置 -There are several parameters that you can pass to your *path operation decorator* to configure it. +*路径操作装饰器*支持多种配置参数。 !!! warning Notice that these parameters are passed directly to the *path operation decorator*, not to your *path operation function*. -## Response Status Code +## `status_code` 状态码 -You can define the (HTTP) `status_code` to be used in the response of your *path operation*. +`status_code` 用于定义*路径操作*响应中的 HTTP 状态码。 -You can pass directly the `int` code, like `404`. +可以直接传递 `int` 代码, 比如 `404`。 -But if you don't remember what each number code is for, you can use the shortcut constants in `status`: +如果记不住数字码的涵义,也可以用 `status` 的快捷常量: === "Python 3.10+" ```Python hl_lines="1 15" - {!> ../../../docs_src/path_operation_configuration/tutorial001_py310.py!} + 注意:以下参数应直接传递给**路径操作装饰器**,不能传递给*路径操作函数*。 ``` === "Python 3.9+" ```Python hl_lines="3 17" - {!> ../../../docs_src/path_operation_configuration/tutorial001_py39.py!} + !!! check "检查" ``` === "Python 3.6+" ```Python hl_lines="3 17" - {!> ../../../docs_src/path_operation_configuration/tutorial001.py!} + API 文档会把该路径操作标记为弃用: ``` -That status code will be used in the response and will be added to the OpenAPI schema. +状态码在响应中使用,并会被添加到 OpenAPI 概图。 -!!! note "Technical Details" - You could also use `from starlette import status`. +!!! 也可以使用 `from starlette import status` 导入状态码。 - **FastAPI** provides the same `starlette.status` as `fastapi.status` just as a convenience for you, the developer. But it comes directly from Starlette. + **FastAPI** 的`fastapi.status` 和 `starlette.status` 一样,只是快捷方式。 But it comes directly from Starlette. ## Tags -You can add tags to your *path operation*, pass the parameter `tags` with a `list` of `str` (commonly just one `str`): +`tags` 参数的值是由 `str` 组成的 `list` (一般只有一个 `str` ),`tags` 用于为*路径操作*添加标签: === "Python 3.10+" ```Python hl_lines="15 20 25" - {!> ../../../docs_src/path_operation_configuration/tutorial002_py310.py!} + {!../../../docs_src/path_operation_configuration/tutorial005.py!} ``` === "Python 3.9+" ```Python hl_lines="17 22 27" - {!> ../../../docs_src/path_operation_configuration/tutorial002_py39.py!} + 注意,`response_description` 只用于描述响应,`description` 一般则用于描述*路径操作*。 ``` === "Python 3.6+" ```Python hl_lines="17 22 27" - {!> ../../../docs_src/path_operation_configuration/tutorial002.py!} + {!../../../docs_src/path_operation_configuration/tutorial002.py!} ``` -They will be added to the OpenAPI schema and used by the automatic documentation interfaces: +OpenAPI 概图会自动添加标签,供 API 文档接口使用: @@ -73,94 +72,99 @@ In these cases, it could make sense to store the tags in an `Enum`. **FastAPI** supports that the same way as with plain strings: ```Python hl_lines="1 8-10 13 18" -{!../../../docs_src/path_operation_configuration/tutorial002b.py!} +{!../../../docs_src/path_operation_configuration/tutorial001.py!} ``` ## Summary and description -You can add a `summary` and `description`: +`summary` 和 `description` 参数 === "Python 3.10+" ```Python hl_lines="18-19" - {!> ../../../docs_src/path_operation_configuration/tutorial003_py310.py!} + {!../../../docs_src/path_operation_configuration/tutorial003.py!} ``` === "Python 3.9+" ```Python hl_lines="20-21" - {!> ../../../docs_src/path_operation_configuration/tutorial003_py39.py!} + !!! warning "警告" ``` === "Python 3.6+" ```Python hl_lines="20-21" - {!> ../../../docs_src/path_operation_configuration/tutorial003.py!} + 路径装饰器还支持 summarydescription 这两个参数: ``` + 和 description 这两个参数: + ## Description from docstring -As descriptions tend to be long and cover multiple lines, you can declare the *path operation* description in the function docstring and **FastAPI** will read it from there. +描述内容比较长且占用多行时,可以在函数的 docstring 中声明*路径操作*的描述,**FastAPI** 支持从文档字符串中读取描述内容。 -You can write Markdown in the docstring, it will be interpreted and displayed correctly (taking into account docstring indentation). +文档字符串支持 Markdown,能正确解析和显示 Markdown 的内容,但要注意文档字符串的缩进。 === "Python 3.10+" ```Python hl_lines="17-25" - {!> ../../../docs_src/path_operation_configuration/tutorial004_py310.py!} + {!../../../docs_src/path_operation_configuration/tutorial004.py!} ``` === "Python 3.9+" ```Python hl_lines="19-27" - {!> ../../../docs_src/path_operation_configuration/tutorial004_py39.py!} + 下图为 Markdown 文本在 API 文档中的显示效果: ``` === "Python 3.6+" ```Python hl_lines="19-27" - {!> ../../../docs_src/path_operation_configuration/tutorial004.py!} + 文档字符串(docstring) ``` +) + It will be used in the interactive docs: -## Response description +## 响应描述 -You can specify the response description with the parameter `response_description`: +`response_description` 参数用于定义响应的描述说明: === "Python 3.10+" ```Python hl_lines="19" - {!> ../../../docs_src/path_operation_configuration/tutorial005_py310.py!} + !!! info "说明" ``` === "Python 3.9+" ```Python hl_lines="21" - {!> ../../../docs_src/path_operation_configuration/tutorial005_py39.py!} + !!! note "技术细节" ``` === "Python 3.6+" ```Python hl_lines="21" - {!> ../../../docs_src/path_operation_configuration/tutorial005.py!} + tags 参数 ``` + 参数 + !!! info Notice that `response_description` refers specifically to the response, the `description` refers to the *path operation* in general. -!!! check - OpenAPI specifies that each *path operation* requires a response description. +!!! OpenAPI 规定每个*路径操作*都要有响应描述。 - So, if you don't provide one, **FastAPI** will automatically generate one of "Successful response". + 如果没有定义响应描述,**FastAPI** 则自动生成内容为 "Successful response" 的响应描述。 -## Deprecate a *path operation* +## 弃用*路径操作* -If you need to mark a *path operation* as deprecated, but without removing it, pass the parameter `deprecated`: +`deprecated` 参数可以把*路径操作*标记为弃用,无需直接删除: ```Python hl_lines="16" {!../../../docs_src/path_operation_configuration/tutorial006.py!} @@ -170,10 +174,10 @@ It will be clearly marked as deprecated in the interactive docs: -Check how deprecated and non-deprecated *path operations* look like: +下图显示了正常*路径操作*与弃用*路径操作* 的区别: ## Recap -You can configure and add metadata for your *path operations* easily by passing parameters to the *path operation decorators*. +通过传递参数给*路径操作装饰器* ,即可轻松地配置*路径操作*、添加元数据。 From 651faea64d0b2b7cb4f07ee9f92be6dbccfe450c Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:50 +0800 Subject: [PATCH 145/163] New translations path-params-numeric-validations.md (Chinese Simplified) --- .../path-params-numeric-validations.md | 113 +++++++++--------- 1 file changed, 59 insertions(+), 54 deletions(-) diff --git a/docs/zh/docs/tutorial/path-params-numeric-validations.md b/docs/zh/docs/tutorial/path-params-numeric-validations.md index fd211224826c3..fc045d9b4b3a7 100644 --- a/docs/zh/docs/tutorial/path-params-numeric-validations.md +++ b/docs/zh/docs/tutorial/path-params-numeric-validations.md @@ -1,10 +1,10 @@ -# Path Parameters and Numeric Validations +# 路径参数和数值校验 -In the same way that you can declare more validations and metadata for query parameters with `Query`, you can declare the same type of validations and metadata for path parameters with `Path`. +与使用 `Query` 为查询参数声明更多的校验和元数据的方式相同,你也可以使用 `Path` 为路径参数声明相同类型的校验和元数据。 -## Import Path +## 导入 Path -First, import `Path` from `fastapi`, and import `Annotated`: +首先,从 `fastapi` 导入 `Path`: === "Python 3.10+" @@ -30,7 +30,7 @@ First, import `Path` from `fastapi`, and import `Annotated`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="1" - {!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!} + {!../../../docs_src/path_params_numeric_validations/tutorial001.py!} ``` === "Python 3.6+ non-Annotated" @@ -39,7 +39,7 @@ First, import `Path` from `fastapi`, and import `Annotated`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="3" - {!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!} + {!../../../docs_src/path_params_numeric_validations/tutorial002.py!} ``` !!! info @@ -49,11 +49,11 @@ First, import `Path` from `fastapi`, and import `Annotated`: Make sure you [Upgrade the FastAPI version](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} to at least 0.95.1 before using `Annotated`. -## Declare metadata +## 声明元数据 -You can declare all the same parameters as for `Query`. +你可以声明与 `Query` 相同的所有参数。 -For example, to declare a `title` metadata value for the path parameter `item_id` you can type: +例如,要声明路径参数 `item_id`的 `title` 元数据值,你可以输入: === "Python 3.10+" @@ -79,7 +79,7 @@ For example, to declare a `title` metadata value for the path parameter `item_id Prefer to use the `Annotated` version if possible. ```Python hl_lines="8" - {!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!} + {!../../../docs_src/path_params_numeric_validations/tutorial005.py!} ``` === "Python 3.6+ non-Annotated" @@ -94,28 +94,28 @@ For example, to declare a `title` metadata value for the path parameter `item_id !!! note A path parameter is always required as it has to be part of the path. - So, you should declare it with `...` to mark it as required. + 所以,你应该在声明时使用 `...` 将其标记为必需参数。 - Nevertheless, even if you declared it with `None` or set a default value, it would not affect anything, it would still be always required. + 然而,即使你使用 `None` 声明路径参数或设置一个其他默认值也不会有任何影响,它依然会是必需参数。 -## Order the parameters as you need +## 按需对参数排序 !!! tip This is probably not as important or necessary if you use `Annotated`. -Let's say that you want to declare the query parameter `q` as a required `str`. +假设你想要声明一个必需的 `str` 类型查询参数 `q`。 -And you don't need to declare anything else for that parameter, so you don't really need to use `Query`. +而且你不需要为该参数声明任何其他内容,所以实际上你并不需要使用 `Query`。 But you still need to use `Path` for the `item_id` path parameter. And you don't want to use `Annotated` for some reason. -Python will complain if you put a value with a "default" before a value that doesn't have a "default". +如果你将带有「默认值」的参数放在没有「默认值」的参数之前,Python 将会报错。 -But you can re-order them, and have the value without a default (the query parameter `q`) first. +但是你可以对其重新排序,并将不带默认值的值(查询参数 `q`)放到最前面。 -It doesn't matter for **FastAPI**. It will detect the parameters by their names, types and default declarations (`Query`, `Path`, etc), it doesn't care about the order. +对 **FastAPI** 来说这无关紧要。 它将通过参数的名称、类型和默认值声明(`Query`、`Path` 等)来检测参数,而不在乎参数的顺序。 -So, you can declare your function as: +因此,你可以将函数声明为: === "Python 3.6 non-Annotated" @@ -126,7 +126,8 @@ So, you can declare your function as: {!> ../../../docs_src/path_params_numeric_validations/tutorial002.py!} ``` -But have in mind that if you use `Annotated`, you won't have this problem, it won't matter as you're not using the function parameter default values for `Query()` or `Path()`. +!!! note "技术细节" + 当你从 `fastapi` 导入 `Query`、`Path` 和其他同类对象时,它们实际上是函数。 === "Python 3.9+" @@ -137,10 +138,10 @@ But have in mind that if you use `Annotated`, you won't have this problem, it wo === "Python 3.6+" ```Python hl_lines="9" - {!> ../../../docs_src/path_params_numeric_validations/tutorial002_an.py!} + {!../../../docs_src/path_params_numeric_validations/tutorial006.py!} ``` -## Order the parameters as you need, tricks +## 按需对参数排序的技巧 !!! tip This is probably not as important or necessary if you use `Annotated`. @@ -149,16 +150,17 @@ Here's a **small trick** that can be handy, but you won't need it often. If you want to: -* declare the `q` query parameter without a `Query` nor any default value -* declare the path parameter `item_id` using `Path` +* !!! info + `Query`、`Path` 以及你后面会看到的其他类继承自一个共同的 `Param` 类(不需要直接使用它)。 +* 但是你仍然需要使用 `Path` 来声明路径参数 `item_id`。 * have them in a different order * not use `Annotated` ...Python has a little special syntax for that. -Pass `*`, as the first parameter of the function. +传递 `*` 作为函数的第一个参数。 -Python won't do anything with that `*`, but it will know that all the following parameters should be called as keyword arguments (key-value pairs), also known as kwargs. Even if they don't have a default value. +Python 不会对该 `*` 做任何事情,但是它将知道之后的所有参数都应作为关键字参数(键值对),也被称为 kwargs,来调用。 即使它们没有默认值。 ```Python hl_lines="7" {!../../../docs_src/path_params_numeric_validations/tutorial003.py!} @@ -177,14 +179,14 @@ Have in mind that if you use `Annotated`, as you are not using function paramete === "Python 3.6+" ```Python hl_lines="9" - {!> ../../../docs_src/path_params_numeric_validations/tutorial003_an.py!} + {!../../../docs_src/path_params_numeric_validations/tutorial001.py!} ``` -## Number validations: greater than or equal +## 数值校验:大于等于 -With `Query` and `Path` (and others you'll see later) you can declare number constraints. +使用 `Query` 和 `Path`(以及你将在后面看到的其他类)可以声明字符串约束,但也可以声明数值约束。 -Here, with `ge=1`, `item_id` will need to be an integer number "`g`reater than or `e`qual" to `1`. +像下面这样,添加 `ge=1` 后,`item_id` 将必须是一个大于(`g`reater than)或等于(`e`qual)`1` 的整数。 === "Python 3.9+" @@ -195,7 +197,7 @@ Here, with `ge=1`, `item_id` will need to be an integer number "`g`reater than o === "Python 3.6+" ```Python hl_lines="9" - {!> ../../../docs_src/path_params_numeric_validations/tutorial004_an.py!} + {!../../../docs_src/path_params_numeric_validations/tutorial004.py!} ``` === "Python 3.6+ non-Annotated" @@ -207,12 +209,12 @@ Here, with `ge=1`, `item_id` will need to be an integer number "`g`reater than o {!> ../../../docs_src/path_params_numeric_validations/tutorial004.py!} ``` -## Number validations: greater than and less than or equal +## 数值校验:大于和小于等于 -The same applies for: +同样的规则适用于: -* `gt`: `g`reater `t`han -* `le`: `l`ess than or `e`qual +* `gt`:大于(`g`reater `t`han) +* `ge`:大于等于(`g`reater than or `e`qual) === "Python 3.9+" @@ -223,7 +225,7 @@ The same applies for: === "Python 3.6+" ```Python hl_lines="9" - {!> ../../../docs_src/path_params_numeric_validations/tutorial005_an.py!} + 总结 ``` === "Python 3.6+ non-Annotated" @@ -235,27 +237,30 @@ The same applies for: {!> ../../../docs_src/path_params_numeric_validations/tutorial005.py!} ``` -## Number validations: floats, greater than and less than +## 数值校验:浮点数、大于和小于 -Number validations also work for `float` values. +数值校验同样适用于 `float` 值。 -Here's where it becomes important to be able to declare gt and not just ge. As with it you can require, for example, that a value must be greater than `0`, even if it is less than `1`. +能够声明 gt 而不仅仅是 ge 在这个前提下变得重要起来。 例如,你可以要求一个值必须大于 `0`,即使它小于 `1`。 -So, `0.5` would be a valid value. But `0.0` or `0` would not. +因此,`0.5` 将是有效值。 但是 `0.0`或 `0` 不是。 -And the same for lt. +对于 lt 也是一样的。 === "Python 3.9+" ```Python hl_lines="13" - {!> ../../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py!} + !!! note + 路径参数总是必需的,因为它必须是路径的一部分。 ``` === "Python 3.6+" ```Python hl_lines="12" - {!> ../../../docs_src/path_params_numeric_validations/tutorial006_an.py!} + 如果你想不使用 Query 声明没有默认值的查询参数 q,同时使用 Path 声明路径参数 item_id,并使它们的顺序与上面不同,Python 对此有一些特殊的语法。 ``` + 声明没有默认值的查询参数 q,同时使用 Path 声明路径参数 item_id,并使它们的顺序与上面不同,Python 对此有一些特殊的语法。 + === "Python 3.6+ non-Annotated" @@ -268,27 +273,27 @@ And the same for lt. ## Recap -With `Query`, `Path` (and others you haven't seen yet) you can declare metadata and string validations in the same ways as with [Query Parameters and String Validations](query-params-str-validations.md){.internal-link target=_blank}. +你能够以与 [查询参数和字符串校验](query-params-str-validations.md){.internal-link target=_blank} 相同的方式使用 `Query`、`Path`(以及其他你还没见过的类)声明元数据和字符串校验。 -And you can also declare numeric validations: +而且你还可以声明数值校验: -* `gt`: `g`reater `t`han -* `ge`: `g`reater than or `e`qual -* `lt`: `l`ess `t`han -* `le`: `l`ess than or `e`qual +* `gt`:大于(`g`reater `t`han) +* `le`:小于等于(`l`ess than or `e`qual) +* `lt`:小于(`l`ess `t`han) +* `le`:小于等于(`l`ess than or `e`qual) !!! info `Query`, `Path`, and other classes you will see later are subclasses of a common `Param` class. - All of them share the same parameters for additional validation and metadata you have seen. + 而且它们都共享相同的所有你已看到并用于添加额外校验和元数据的参数。 !!! note "Technical Details" When you import `Query`, `Path` and others from `fastapi`, they are actually functions. - That when called, return instances of classes of the same name. + 当被调用时,它们返回同名类的实例。 - So, you import `Query`, which is a function. And when you call it, it returns an instance of a class also named `Query`. + 如此,你导入 `Query` 这个函数。 当你调用它时,它将返回一个同样命名为 `Query` 的类的实例。 - These functions are there (instead of just using the classes directly) so that your editor doesn't mark errors about their types. + 因为使用了这些函数(而不是直接使用类),所以你的编辑器不会标记有关其类型的错误。 - That way you can use your normal editor and coding tools without having to add custom configurations to disregard those errors. + 这样,你可以使用常规的编辑器和编码工具,而不必添加自定义配置来忽略这些错误。 From 98419712837934baa99d04de160d9ac49bb067f7 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:51 +0800 Subject: [PATCH 146/163] New translations path-params.md (Chinese Simplified) --- docs/zh/docs/tutorial/path-params.md | 172 +++++++++++++-------------- 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/docs/zh/docs/tutorial/path-params.md b/docs/zh/docs/tutorial/path-params.md index 6b14a69a6f553..a307010299d68 100644 --- a/docs/zh/docs/tutorial/path-params.md +++ b/docs/zh/docs/tutorial/path-params.md @@ -1,48 +1,48 @@ -# Path Parameters +# 路径参数 -You can declare path "parameters" or "variables" with the same syntax used by Python format strings: +你可以使用与 Python 格式化字符串相同的语法来声明路径"参数"或"变量": ```Python hl_lines="6-7" {!../../../docs_src/path_params/tutorial001.py!} ``` -The value of the path parameter `item_id` will be passed to your function as the argument `item_id`. +路径参数 `item_id` 的值将作为参数 `item_id` 传递给你的函数。 -So, if you run this example and go to http://127.0.0.1:8000/items/foo, you will see a response of: +所以,如果你运行示例并访问 http://127.0.0.1:8000/items/foo,将会看到如下响应: ```JSON {"item_id":"foo"} ``` -## Path parameters with types +## 有类型的路径参数 -You can declare the type of a path parameter in the function, using standard Python type annotations: +你可以使用标准的 Python 类型标注为函数中的路径参数声明类型。 ```Python hl_lines="7" {!../../../docs_src/path_params/tutorial002.py!} ``` -In this case, `item_id` is declared to be an `int`. +在这个例子中,`item_id` 被声明为 `int` 类型。 -!!! check - This will give you editor support inside of your function, with error checks, completion, etc. +!!! !!! check + 这将为你的函数提供编辑器支持,包括错误检查、代码补全等等。 -## Data conversion +## 数据转换 -If you run this example and open your browser at http://127.0.0.1:8000/items/3, you will see a response of: +如果你运行示例并打开浏览器访问 http://127.0.0.1:8000/items/3,将得到如下响应: ```JSON {"item_id":3} ``` -!!! check - Notice that the value your function received (and returned) is `3`, as a Python `int`, not a string `"3"`. +!!! !!! check + 注意函数接收(并返回)的值为 3,是一个 Python `int` 值,而不是字符串 `"3"`。 - So, with that type declaration, **FastAPI** gives you automatic request "parsing". + 所以,**FastAPI** 通过上面的类型声明提供了对请求的自动"解析"。 -## Data validation +## 数据校验 -But if you go to the browser at http://127.0.0.1:8000/items/foo, you will see a nice HTTP error of: +但如果你通过浏览器访问 http://127.0.0.1:8000/items/foo,你会看到一个清晰可读的 HTTP 错误: ```JSON { @@ -59,61 +59,61 @@ But if you go to the browser at http://127.0.0.1:8000/items/4.2 +如果你提供的是 `float` 而非整数也会出现同样的错误,比如: http://127.0.0.1:8000/items/4.2 -!!! check - So, with the same Python type declaration, **FastAPI** gives you data validation. +!!! !!! check + 所以,通过同样的 Python 类型声明,**FastAPI** 提供了数据校验功能。 - Notice that the error also clearly states exactly the point where the validation didn't pass. + 注意上面的错误同样清楚地指出了校验未通过的具体原因。 - This is incredibly helpful while developing and debugging code that interacts with your API. + 在开发和调试与你的 API 进行交互的代码时,这非常有用。 -## Documentation +## 文档 -And when you open your browser at http://127.0.0.1:8000/docs, you will see an automatic, interactive, API documentation like: +当你打开浏览器访问 http://127.0.0.1:8000/docs,你将看到自动生成的交互式 API 文档: -!!! check - Again, just with that same Python type declaration, **FastAPI** gives you automatic, interactive documentation (integrating Swagger UI). +!!! !!! check + 再一次,还是通过相同的 Python 类型声明,**FastAPI** 为你提供了自动生成的交互式文档(集成 Swagger UI)。 - Notice that the path parameter is declared to be an integer. + 注意这里的路径参数被声明为一个整数。 -## Standards-based benefits, alternative documentation +## 基于标准的好处:可选文档 -And because the generated schema is from the OpenAPI standard, there are many compatible tools. +由于生成的 API 模式来自于 OpenAPI 标准,所以有很多工具与其兼容。 -Because of this, **FastAPI** itself provides an alternative API documentation (using ReDoc), which you can access at http://127.0.0.1:8000/redoc: +正因如此,**FastAPI** 内置了一个可选的 API 文档(使用 Redoc): -The same way, there are many compatible tools. Including code generation tools for many languages. +The same way, there are many compatible tools. 同样的,还有很多其他兼容的工具,包括适用于多种语言的代码生成工具。 ## Pydantic -All the data validation is performed under the hood by Pydantic, so you get all the benefits from it. And you know you are in good hands. +所有的数据校验都由 Pydantic 在幕后完成,所以你可以从它所有的优点中受益。 并且你知道它在这方面非常胜任。 -You can use the same type declarations with `str`, `float`, `bool` and many other complex data types. +你可以使用同样的类型声明来声明 `str`、`float`、`bool` 以及许多其他的复合数据类型。 -Several of these are explored in the next chapters of the tutorial. +本教程的下一章节将探讨其中的一些内容。 ## Order matters -When creating *path operations*, you can find situations where you have a fixed path. +在创建*路径操作*时,你会发现有些情况下路径是固定的。 -Like `/users/me`, let's say that it's to get data about the current user. +比如 `/users/me`,我们假设它用来获取关于当前用户的数据. -And then you can also have a path `/users/{user_id}` to get data about a specific user by some user ID. +然后,你还可以使用路径 `/users/{user_id}` 来通过用户 ID 获取关于特定用户的数据。 -Because *path operations* are evaluated in order, you need to make sure that the path for `/users/me` is declared before the one for `/users/{user_id}`: +由于*路径操作*是按顺序依次运行的,你需要确保路径 `/users/me` 声明在路径 `/users/{user_id}`之前: ```Python hl_lines="6 11" {!../../../docs_src/path_params/tutorial003.py!} ``` -Otherwise, the path for `/users/{user_id}` would match also for `/users/me`, "thinking" that it's receiving a parameter `user_id` with a value of `"me"`. +否则,`/users/{user_id}` 的路径还将与 `/users/me` 相匹配,"认为"自己正在接收一个值为 `"me"` 的 `user_id` 参数。 Similarly, you cannot redefine a path operation: @@ -123,70 +123,70 @@ Similarly, you cannot redefine a path operation: The first one will always be used since the path matches first. -## Predefined values +## 预设值 -If you have a *path operation* that receives a *path parameter*, but you want the possible valid *path parameter* values to be predefined, you can use a standard Python `Enum`. +如果你有一个接收路径参数的路径操作,但你希望预先设定可能的有效参数值,则可以使用标准的 Python `Enum` 类型。 -### Create an `Enum` class +### 创建一个 `Enum` 类 -Import `Enum` and create a sub-class that inherits from `str` and from `Enum`. +导入 `Enum` 并创建一个继承自 `str` 和 `Enum` 的子类。 -By inheriting from `str` the API docs will be able to know that the values must be of type `string` and will be able to render correctly. +通过从 `str` 继承,API 文档将能够知道这些值必须为 `string` 类型并且能够正确地展示出来。 -Then create class attributes with fixed values, which will be the available valid values: +然后创建具有固定值的类属性,这些固定值将是可用的有效值: ```Python hl_lines="1 6-9" {!../../../docs_src/path_params/tutorial005.py!} ``` -!!! info - Enumerations (or enums) are available in Python since version 3.4. +!!! !!! info + 枚举(或 enums)从 3.4 版本起在 Python 中可用。 -!!! tip - If you are wondering, "AlexNet", "ResNet", and "LeNet" are just names of Machine Learning models. +!!! !!! tip + 如果你想知道,"AlexNet"、"ResNet" 和 "LeNet" 只是机器学习中的模型名称。 -### Declare a *path parameter* +### 声明*路径参数* -Then create a *path parameter* with a type annotation using the enum class you created (`ModelName`): +然后使用你定义的枚举类(`ModelName`)创建一个带有类型标注的*路径参数*: ```Python hl_lines="16" {!../../../docs_src/path_params/tutorial005.py!} ``` -### Check the docs +### 查看文档 -Because the available values for the *path parameter* are predefined, the interactive docs can show them nicely: +*路径参数*的值将是一个*枚举成员*。 -### Working with Python *enumerations* +### 使用 Python *枚举类型* -The value of the *path parameter* will be an *enumeration member*. +因为已经指定了*路径参数*的可用值,所以交互式文档可以恰当地展示它们: -#### Compare *enumeration members* +#### 比较*枚举成员* -You can compare it with the *enumeration member* in your created enum `ModelName`: +你可以将它与你创建的枚举类 `ModelName` 中的*枚举成员*进行比较: ```Python hl_lines="17" {!../../../docs_src/path_params/tutorial005.py!} ``` -#### Get the *enumeration value* +#### 获取*枚举值* -You can get the actual value (a `str` in this case) using `model_name.value`, or in general, `your_enum_member.value`: +你可以使用 `model_name.value` 或通常来说 `your_enum_member.value` 来获取实际的值(在这个例子中为 `str`): ```Python hl_lines="20" {!../../../docs_src/path_params/tutorial005.py!} ``` -!!! tip - You could also access the value `"lenet"` with `ModelName.lenet.value`. +!!! !!! tip + 你也可以通过 `ModelName.lenet.value` 来获取值 `"lenet"`。 -#### Return *enumeration members* +#### 返回*枚举成员* -You can return *enum members* from your *path operation*, even nested in a JSON body (e.g. a `dict`). +你可以从*路径操作*中返回*枚举成员*,即使嵌套在 JSON 结构中(例如一个 `dict` 中)。 -They will be converted to their corresponding values (strings in this case) before returning them to the client: +在返回给客户端之前,它们将被转换为对应的值: ```Python hl_lines="18 21 23" {!../../../docs_src/path_params/tutorial005.py!} @@ -201,52 +201,52 @@ In your client you will get a JSON response like: } ``` -## Path parameters containing paths +## 包含路径的路径参数 -Let's say you have a *path operation* with a path `/files/{file_path}`. +假设你有一个*路径操作*,它的路径为 `/files/{file_path}`。 -But you need `file_path` itself to contain a *path*, like `home/johndoe/myfile.txt`. +但是你需要 `file_path` 自身也包含*路径*,比如 `home/johndoe/myfile.txt`。 -So, the URL for that file would be something like: `/files/home/johndoe/myfile.txt`. +因此,该文件的URL将类似于这样:`/files/home/johndoe/myfile.txt`。 -### OpenAPI support +### OpenAPI 支持 -OpenAPI doesn't support a way to declare a *path parameter* to contain a *path* inside, as that could lead to scenarios that are difficult to test and define. +你可以使用直接来自 Starlette 的选项来声明一个包含*路径*的*路径参数*: -Nevertheless, you can still do it in **FastAPI**, using one of the internal tools from Starlette. +不过,你仍然可以通过 Starlette 的一个内部工具在 **FastAPI** 中实现它。 -And the docs would still work, although not adding any documentation telling that the parameter should contain a path. +而且文档依旧可以使用,但是不会添加任何该参数应包含路径的说明。 -### Path convertor +### 路径转换器 -Using an option directly from Starlette you can declare a *path parameter* containing a *path* using a URL like: +OpenAPI 不支持任何方式去声明*路径参数*以在其内部包含*路径*,因为这可能会导致难以测试和定义的情况出现。 ``` /files/{file_path:path} ``` -In this case, the name of the parameter is `file_path`, and the last part, `:path`, tells it that the parameter should match any *path*. +在这种情况下,参数的名称为 `file_path`,结尾部分的 `:path` 说明该参数应匹配任意的*路径*。 -So, you can use it with: +因此,你可以这样使用它: ```Python hl_lines="6" {!../../../docs_src/path_params/tutorial004.py!} ``` -!!! tip - You could need the parameter to contain `/home/johndoe/myfile.txt`, with a leading slash (`/`). +!!! !!! tip + 你可能会需要参数包含 `/home/johndoe/myfile.txt`,以斜杠(`/`)开头。 - In that case, the URL would be: `/files//home/johndoe/myfile.txt`, with a double slash (`//`) between `files` and `home`. + 在这种情况下,URL 将会是 `/files//home/johndoe/myfile.txt`,在`files` 和 `home` 之间有一个双斜杠(`//`)。 ## Recap -With **FastAPI**, by using short, intuitive and standard Python type declarations, you get: +使用 **FastAPI**,通过简短、直观和标准的 Python 类型声明,你将获得: -* Editor support: error checks, autocompletion, etc. -* Data "parsing" -* Data validation -* API annotation and automatic documentation +* 编辑器支持:错误检查,代码补全等 +* 数据 "解析" +* 数据校验 +* API 标注和自动生成的文档 -And you only have to declare them once. +而且你只需要声明一次即可。 -That's probably the main visible advantage of **FastAPI** compared to alternative frameworks (apart from the raw performance). +这可能是 **FastAPI** 与其他框架相比主要的明显优势(除了原始性能以外)。 From d0ea2653a6a96b1331ca875e3fda5dcf01efc438 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:52 +0800 Subject: [PATCH 147/163] New translations query-params-str-validations.md (Chinese Simplified) --- .../tutorial/query-params-str-validations.md | 222 +++++++++--------- 1 file changed, 115 insertions(+), 107 deletions(-) diff --git a/docs/zh/docs/tutorial/query-params-str-validations.md b/docs/zh/docs/tutorial/query-params-str-validations.md index 6905626cc983f..15ef418f38a1c 100644 --- a/docs/zh/docs/tutorial/query-params-str-validations.md +++ b/docs/zh/docs/tutorial/query-params-str-validations.md @@ -1,8 +1,8 @@ -# Query Parameters and String Validations +# 查询参数和字符串校验 -**FastAPI** allows you to declare additional information and validation for your parameters. +**FastAPI** 允许你为参数声明额外的信息和校验。 -Let's take this application as example: +让我们以下面的应用程序为例: === "Python 3.10+" @@ -16,22 +16,23 @@ Let's take this application as example: {!> ../../../docs_src/query_params_str_validations/tutorial001.py!} ``` -The query parameter `q` is of type `Union[str, None]` (or `str | None` in Python 3.10), that means that it's of type `str` but could also be `None`, and indeed, the default value is `None`, so FastAPI will know it's not required. +!!! note + 请记住,在这种情况下 FastAPI 将不会检查列表的内容。 !!! note FastAPI will know that the value of `q` is not required because of the default value `= None`. The `Union` in `Union[str, None]` will allow your editor to give you better support and detect errors. -## Additional validation +## 额外的校验 -We are going to enforce that even though `q` is optional, whenever it is provided, **its length doesn't exceed 50 characters**. +我们打算添加约束条件:即使 `q` 是可选的,但只要提供了该参数,则该参数值**不能超过50个字符的长度**。 -### Import `Query` and `Annotated` +### 导入 `Query` To achieve that, first import: -* `Query` from `fastapi` +* 为此,首先从 `fastapi` 导入 `Query`: * `Annotated` from `typing` (or from `typing_extensions` in Python below 3.9) === "Python 3.10+" @@ -39,7 +40,7 @@ To achieve that, first import: In Python 3.9 or above, `Annotated` is part of the standard library, so you can import it from `typing`. ```Python hl_lines="1 3" - {!> ../../../docs_src/query_params_str_validations/tutorial002_an_py310.py!} + {!../../../docs_src/query_params_str_validations/tutorial002.py!} ``` === "Python 3.6+" @@ -59,7 +60,8 @@ To achieve that, first import: Make sure you [Upgrade the FastAPI version](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} to at least 0.95.1 before using `Annotated`. -## Use `Annotated` in the type for the `q` parameter +## !!! tip + 要声明类型为 `list` 的查询参数,如上例所示,你需要显式地使用 `Query`,否则该参数将被解释为请求体。 Remember I told you before that `Annotated` can be used to add metadata to your parameters in the [Python Types Intro](../python-types.md#type-hints-with-metadata-annotations){.internal-link target=_blank}? @@ -84,7 +86,7 @@ What we will do is wrap that with `Annotated`, so it becomes: === "Python 3.10+" ```Python - q: Annotated[str | None] = None + 总结 ``` === "Python 3.6+" @@ -93,11 +95,11 @@ What we will do is wrap that with `Annotated`, so it becomes: q: Annotated[Union[str, None]] = None ``` -Both of those versions mean the same thing, `q` is a parameter that can be a `str` or `None`, and by default, it is `None`. +查询参数 `q` 的类型为 `str`,默认值为 `None`,因此它是可选的。 Now let's jump to the fun stuff. 🎉 -## Add `Query` to `Annotated` in the `q` parameter +## 但是 `Query` 显式地将其声明为查询参数。 Now that we have this `Annotated` where we can put more metadata, add `Query` to it, and set the parameter `max_length` to 50: @@ -110,10 +112,11 @@ Now that we have this `Annotated` where we can put more metadata, add `Query` to === "Python 3.6+" ```Python hl_lines="10" - {!> ../../../docs_src/query_params_str_validations/tutorial002_an.py!} + {!../../../docs_src/query_params_str_validations/tutorial006d.py!} ``` -Notice that the default value is still `None`, so the parameter is still optional. +!!! note + 具有默认值还会使该参数成为可选参数。 But now, having `Query(max_length=50)` inside of `Annotated`, we are telling FastAPI that we want it to extract this value from the query parameters (this would have been the default anyway 🤷) and that we want to have **additional validation** for this value (that's why we do this, to get the additional validation). 😎 @@ -123,14 +126,14 @@ FastAPI will now: * Show a **clear error** for the client when the data is not valid * **Document** the parameter in the OpenAPI schema *path operation* (so it will show up in the **automatic docs UI**) -## Alternative (old) `Query` as the default value +## 你可以向 `Query` 的第一个参数传入 `None` 用作查询参数的默认值,以同样的方式你也可以传递其他默认值。 Previous versions of FastAPI (before 0.95.0) required you to use `Query` as the default value of your parameter, instead of putting it in `Annotated`, there's a high chance that you will see code using it around, so I'll explain it to you. !!! tip For new code and whenever possible, use `Annotated` as explained above. There are multiple advantages (explained below) and no disadvantages. 🍰 -This is how you would use `Query()` as the default value of your function parameter, setting the parameter `max_length` to 50: +现在,将 `Query` 用作查询参数的默认值,并将它的 `max_length` 参数设置为 50: === "Python 3.10+" @@ -141,18 +144,18 @@ This is how you would use `Query()` as the default value of your function parame === "Python 3.6+" ```Python hl_lines="9" - {!> ../../../docs_src/query_params_str_validations/tutorial002.py!} + {!../../../docs_src/query_params_str_validations/tutorial002.py!} ``` -As in this case (without using `Annotated`) we have to replace the default value `None` in the function with `Query()`, we now need to set the default value with the parameter `Query(default=None)`, it serves the same purpose of defining that default value (at least for FastAPI). +由于我们必须用 `Query(default=None)` 替换默认值 `None`,`Query` 的第一个参数同样也是用于定义默认值。 -So: +所以: ```Python q: Union[str, None] = Query(default=None) ``` -...makes the parameter optional, with a default value of `None`, the same as: +...使得参数可选,等同于: ```Python q: Union[str, None] = None @@ -164,13 +167,13 @@ And in Python 3.10 and above: q: str | None = Query(default=None) ``` -...makes the parameter optional, with a default value of `None`, the same as: +有另一种方法可以显式的声明一个值是必需的,即将默认参数的默认值设为 `...` : ```Python -q: str | None = None +q: str = None ``` -But it declares it explicitly as being a query parameter. +声明为必需参数 !!! info Have in mind that the most important part to make a parameter optional is the part: @@ -191,17 +194,18 @@ But it declares it explicitly as being a query parameter. The `Union[str, None]` part allows your editor to provide better support, but it is not what tells FastAPI that this parameter is not required. -Then, we can pass more parameters to `Query`. In this case, the `max_length` parameter that applies to strings: +然后,我们可以将更多的参数传递给 `Query`。 在本例中,适用于字符串的 `max_length` 参数: ```Python q: Union[str, None] = Query(default=None, max_length=50) ``` -This will validate the data, show a clear error when the data is not valid, and document the parameter in the OpenAPI schema *path operation*. +将会校验数据,在数据无效时展示清晰的错误信息,并在 OpenAPI 模式的*路径操作*中记录该参​​数。 -### `Query` as the default value or in `Annotated` +### 使用 `Query` 作为默认值 -Have in mind that when using `Query` inside of `Annotated` you cannot use the `default` parameter for `Query`. +!!! tip + 请记住,在大多数情况下,当你需要某些东西时,可以简单地省略 `default` 参数,因此你通常不必使用 `...` 或 `Required` Instead use the actual default value of the function parameter. Otherwise, it would be inconsistent. @@ -237,9 +241,9 @@ When you don't use `Annotated` and instead use the **(old) default value style** Because `Annotated` can have more than one metadata annotation, you could now even use the same function with other tools, like Typer. 🚀 -## Add more validations +## 添加更多校验 -You can also add a parameter `min_length`: +你还可以添加 `min_length` 参数: === "Python 3.10+" @@ -274,12 +278,12 @@ You can also add a parameter `min_length`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="10" - {!> ../../../docs_src/query_params_str_validations/tutorial003.py!} + {!../../../docs_src/query_params_str_validations/tutorial003.py!} ``` -## Add regular expressions +## 添加正则表达式 -You can define a regular expression `pattern` that the parameter should match: +你可以定义一个参数值必须匹配的正则表达式: === "Python 3.10+" @@ -305,7 +309,7 @@ You can define a ../../../docs_src/query_params_str_validations/tutorial004_py310.py!} + {!../../../docs_src/query_params_str_validations/tutorial013.py!} ``` === "Python 3.6+ non-Annotated" @@ -314,18 +318,18 @@ You can define a ../../../docs_src/query_params_str_validations/tutorial004.py!} + {!../../../docs_src/query_params_str_validations/tutorial004.py!} ``` -This specific regular expression pattern checks that the received parameter value: +这个指定的正则表达式通过以下规则检查接收到的参数值: -* `^`: starts with the following characters, doesn't have characters before. -* `fixedquery`: has the exact value `fixedquery`. -* `$`: ends there, doesn't have any more characters after `fixedquery`. +* `^`:以该符号之后的字符开头,符号之前没有字符。 +* `fixedquery`: 值精确地等于 `fixedquery`。 +* `$`: 到此结束,在 `fixedquery` 之后没有更多字符。 -If you feel lost with all these **"regular expression"** ideas, don't worry. They are a hard topic for many people. You can still do a lot of stuff without needing regular expressions yet. +如果你对所有的这些**「正则表达式」**概念感到迷茫,请不要担心。 对于许多人来说这都是一个困难的主题。 你仍然可以在无需正则表达式的情况下做很多事情。 -But whenever you need them and go and learn them, know that you can already use them directly in **FastAPI**. +但是,一旦你需要用到并去学习它们时,请了解你已经可以在 **FastAPI** 中直接使用它们。 ### Pydantic v1 `regex` instead of `pattern` @@ -341,11 +345,12 @@ You could still see some code using it: But know that this is deprecated and it should be updated to use the new parameter `pattern`. 🤓 -## Default values +## 默认值 -You can, of course, use default values other than `None`. +!!! note + 请记住,不同的工具对 OpenAPI 的支持程度可能不同。 -Let's say that you want to declare the `q` query parameter to have a `min_length` of `3`, and to have a default value of `"fixedquery"`: +假设你想要声明查询参数 `q`,使其 `min_length` 为 `3`,并且默认值为 `fixedquery`: === "Python 3.9+" @@ -356,7 +361,7 @@ Let's say that you want to declare the `q` query parameter to have a `min_length === "Python 3.6+" ```Python hl_lines="8" - {!> ../../../docs_src/query_params_str_validations/tutorial005_an.py!} + {!../../../docs_src/query_params_str_validations/tutorial005.py!} ``` === "Python 3.6+ non-Annotated" @@ -365,7 +370,7 @@ Let's say that you want to declare the `q` query parameter to have a `min_length Prefer to use the `Annotated` version if possible. ```Python hl_lines="7" - {!> ../../../docs_src/query_params_str_validations/tutorial005.py!} + {!../../../docs_src/query_params_str_validations/tutorial008.py!} ``` !!! note @@ -373,19 +378,19 @@ Let's say that you want to declare the `q` query parameter to have a `min_length ## Make it required -When we don't need to declare more validations or metadata, we can make the `q` query parameter required just by not declaring a default value, like: +当我们不需要声明额外的校验或元数据时,只需不声明默认值就可以使 `q` 参数成为必需参数,例如: ```Python q: str ``` -instead of: +代替: ```Python q: Union[str, None] = None ``` -But we are now declaring it with `Query`, for example like: +但是现在我们正在用 `Query` 声明它,例如: === "Annotated" @@ -399,7 +404,7 @@ But we are now declaring it with `Query`, for example like: q: Union[str, None] = Query(default=None, min_length=3) ``` -So, when you need to declare a value as required while using `Query`, you can simply not declare a default value: +因此,当你在使用 `Query` 且需要声明一个值是必需的时,只需不声明默认参数: === "Python 3.9+" @@ -410,7 +415,7 @@ So, when you need to declare a value as required while using `Query`, you can si === "Python 3.6+" ```Python hl_lines="8" - {!> ../../../docs_src/query_params_str_validations/tutorial006_an.py!} + {!../../../docs_src/query_params_str_validations/tutorial006c.py!} ``` === "Python 3.6+ non-Annotated" @@ -419,7 +424,7 @@ So, when you need to declare a value as required while using `Query`, you can si Prefer to use the `Annotated` version if possible. ```Python hl_lines="7" - {!> ../../../docs_src/query_params_str_validations/tutorial006.py!} + {!../../../docs_src/query_params_str_validations/tutorial006.py!} ``` @@ -428,7 +433,7 @@ So, when you need to declare a value as required while using `Query`, you can si Still, probably better to use the `Annotated` version. 😉 -### Required with Ellipsis (`...`) +### 使用省略号(`...`)声明必需参数 There's an alternative way to explicitly declare that a value is required. You can set the default to the literal value `...`: @@ -450,21 +455,21 @@ There's an alternative way to explicitly declare that a value is required. You c Prefer to use the `Annotated` version if possible. ```Python hl_lines="7" - {!> ../../../docs_src/query_params_str_validations/tutorial006b.py!} + {!../../../docs_src/query_params_str_validations/tutorial006b.py!} ``` -!!! info - If you hadn't seen that `...` before: it is a special single value, it is part of Python and is called "Ellipsis". +!!! !!! info + 如果你之前没见过 `...` 这种用法:它是一个特殊的单独值,它是 Python 的一部分并且被称为「省略号」。 - It is used by Pydantic and FastAPI to explicitly declare that a value is required. + Pydantic 和 FastAPI 使用它来显式的声明需要一个值。 -This will let **FastAPI** know that this parameter is required. +这将使 **FastAPI** 知道此查询参数是必需的。 -### Required with `None` +### 使用`None`声明必需参数 -You can declare that a parameter can accept `None`, but that it's still required. This would force clients to send a value, even if the value is `None`. +你可以声明一个参数可以接收`None`值,但它仍然是必需的。 这将强制客户端发送一个值,即使该值是`None`。 -To do that, you can declare that `None` is a valid type but still use `...` as the default: +为此,你可以声明`None`是一个有效的类型,并仍然使用`default=...`: === "Python 3.10+" @@ -505,9 +510,9 @@ To do that, you can declare that `None` is a valid type but still use `...` as t !!! tip Pydantic, which is what powers all the data validation and serialization in FastAPI, has a special behavior when you use `Optional` or `Union[Something, None]` without a default value, you can read more about it in the Pydantic docs about Required Optional fields. -### Use Pydantic's `Required` instead of Ellipsis (`...`) +### 使用Pydantic中的`Required`代替省略号(`...`) -If you feel uncomfortable using `...`, you can also import and use `Required` from Pydantic: +如果你觉得使用 `...` 不舒服,你也可以从 Pydantic 导入并使用 `Required`: === "Python 3.9+" @@ -533,11 +538,11 @@ If you feel uncomfortable using `...`, you can also import and use `Required` fr !!! tip Remember that in most of the cases, when something is required, you can simply omit the default, so you normally don't have to use `...` nor `Required`. -## Query parameter list / multiple values +## 查询参数列表 / 多个值 -When you define a query parameter explicitly with `Query` you can also declare it to receive a list of values, or said in other way, to receive multiple values. +当你使用 `Query` 显式地定义查询参数时,你还可以声明它去接收一组值,或换句话来说,接收多个值。 -For example, to declare a query parameter `q` that can appear multiple times in the URL, you can write: +例如,要声明一个可在 URL 中出现多次的查询参数 `q`,你可以这样写: === "Python 3.10+" @@ -554,8 +559,11 @@ For example, to declare a query parameter `q` that can appear multiple times in === "Python 3.6+" ```Python hl_lines="10" - {!> ../../../docs_src/query_params_str_validations/tutorial011_an.py!} + !!! tip + Pydantic 是 FastAPI 中所有数据验证和序列化的核心,当你在没有设默认值的情况下使用 OptionalUnion[Something, None] 时,它具有特殊行为,你可以在 Pydantic 文档中阅读有关必需可选字段的更多信息。 ``` + 或 Union[Something, None] 时,它具有特殊行为,你可以在 Pydantic 文档中阅读有关必需可选字段的更多信息。 + === "Python 3.10+ non-Annotated" @@ -581,18 +589,18 @@ For example, to declare a query parameter `q` that can appear multiple times in Prefer to use the `Annotated` version if possible. ```Python hl_lines="9" - {!> ../../../docs_src/query_params_str_validations/tutorial011.py!} + {!../../../docs_src/query_params_str_validations/tutorial011.py!} ``` -Then, with a URL like: +然后,输入如下网址: ``` http://localhost:8000/items/?q=foo&q=bar ``` -you would receive the multiple `q` *query parameters'* values (`foo` and `bar`) in a Python `list` inside your *path operation function*, in the *function parameter* `q`. +你会在*路径操作函数*的*函数参数* `q` 中以一个 Python `list` 的形式接收到*查询参数* `q` 的多个值(`foo` 和 `bar`)。 -So, the response to that URL would be: +因此,该 URL 的响应将会是: ```JSON { @@ -606,13 +614,13 @@ So, the response to that URL would be: !!! tip To declare a query parameter with a type of `list`, like in the example above, you need to explicitly use `Query`, otherwise it would be interpreted as a request body. -The interactive API docs will update accordingly, to allow multiple values: +交互式 API 文档将会相应地进行更新,以允许使用多个值: -### Query parameter list / multiple values with defaults +### 具有默认值的查询参数列表 / 多个值 -And you can also define a default `list` of values if none are provided: +你还可以定义在没有任何给定值时的默认 `list` 值: === "Python 3.9+" @@ -641,16 +649,16 @@ And you can also define a default `list` of values if none are provided: Prefer to use the `Annotated` version if possible. ```Python hl_lines="9" - {!> ../../../docs_src/query_params_str_validations/tutorial012.py!} + {!../../../docs_src/query_params_str_validations/tutorial012.py!} ``` -If you go to: +如果你访问: ``` http://localhost:8000/items/ ``` -the default of `q` will be: `["foo", "bar"]` and your response will be: +`q` 的默认值将为:`["foo", "bar"]`,你的响应会是: ```JSON { @@ -661,9 +669,9 @@ the default of `q` will be: `["foo", "bar"]` and your response will be: } ``` -#### Using `list` +#### 使用 `list` -You can also use `list` directly instead of `List[str]` (or `list[str]` in Python 3.9+): +你也可以直接使用 `list` 代替 `List [str]`: === "Python 3.9+" @@ -689,20 +697,20 @@ You can also use `list` directly instead of `List[str]` (or `list[str]` in Pytho !!! note Have in mind that in this case, FastAPI won't check the contents of the list. - For example, `List[int]` would check (and document) that the contents of the list are integers. But `list` alone wouldn't. + 例如,`List[int]` 将检查(并记录到文档)列表的内容必须是整数。 但是单独的 `list` 不会。 -## Declare more metadata +## 声明更多元数据 -You can add more information about the parameter. +你可以添加更多有关该参数的信息。 -That information will be included in the generated OpenAPI and used by the documentation user interfaces and external tools. +这些信息将包含在生成的 OpenAPI 模式中,并由文档用户界面和外部工具所使用。 !!! note Have in mind that different tools might have different levels of OpenAPI support. - Some of them might not show all the extra information declared yet, although in most of the cases, the missing feature is already planned for development. + 其中一些可能不会展示所有已声明的额外信息,尽管在大多数情况下,缺少的这部分功能已经计划进行开发。 -You can add a `title`: +你可以添加 `title`: === "Python 3.10+" @@ -719,7 +727,7 @@ You can add a `title`: === "Python 3.6+" ```Python hl_lines="11" - {!> ../../../docs_src/query_params_str_validations/tutorial007_an.py!} + {!../../../docs_src/query_params_str_validations/tutorial007.py!} ``` === "Python 3.10+ non-Annotated" @@ -740,7 +748,7 @@ You can add a `title`: {!> ../../../docs_src/query_params_str_validations/tutorial007.py!} ``` -And a `description`: +以及 `description`: === "Python 3.10+" @@ -775,26 +783,26 @@ And a `description`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="13" - {!> ../../../docs_src/query_params_str_validations/tutorial008.py!} + {!../../../docs_src/query_params_str_validations/tutorial001.py!} ``` -## Alias parameters +## 别名参数 -Imagine that you want the parameter to be `item-query`. +假设你想要查询参数为 `item-query`。 -Like in: +像下面这样: ``` http://127.0.0.1:8000/items/?item-query=foobaritems ``` -But `item-query` is not a valid Python variable name. +但是 `item-query` 不是一个有效的 Python 变量名称。 -The closest would be `item_query`. +最接近的有效名称是 `item_query`。 -But you still need it to be exactly `item-query`... +但是你仍然要求它在 URL 中必须是 `item-query`... -Then you can declare an `alias`, and that alias is what will be used to find the parameter value: +这时你可以用 `alias` 参数声明一个别名,该别名将用于在 URL 中查找查询参数值: === "Python 3.10+" @@ -829,16 +837,16 @@ Then you can declare an `alias`, and that alias is what will be used to find the Prefer to use the `Annotated` version if possible. ```Python hl_lines="9" - {!> ../../../docs_src/query_params_str_validations/tutorial009.py!} + {!../../../docs_src/query_params_str_validations/tutorial009.py!} ``` -## Deprecating parameters +## 弃用参数 -Now let's say you don't like this parameter anymore. +现在假设你不再喜欢此参数。 -You have to leave it there a while because there are clients using it, but you want the docs to clearly show it as deprecated. +你不得不将其保留一段时间,因为有些客户端正在使用它,但你希望文档清楚地将其展示为已弃用。 -Then pass the parameter `deprecated=True` to `Query`: +那么将参数 `deprecated=True` 传入 `Query`: === "Python 3.10+" @@ -873,10 +881,10 @@ Then pass the parameter `deprecated=True` to `Query`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="18" - {!> ../../../docs_src/query_params_str_validations/tutorial010.py!} + {!../../../docs_src/query_params_str_validations/tutorial010.py!} ``` -The docs will show it like this: +文档将会像下面这样展示它: @@ -922,21 +930,21 @@ To exclude a query parameter from the generated OpenAPI schema (and thus, from t ## Recap -You can declare additional validations and metadata for your parameters. +你可以为查询参数声明额外的校验和元数据。 -Generic validations and metadata: +通用的校验和元数据: * `alias` * `title` * `description` * `deprecated` -Validations specific for strings: +特定于字符串的校验: * `min_length` * `max_length` * `regex` -In these examples you saw how to declare validations for `str` values. +在这些示例中,你了解了如何声明对 `str` 值的校验。 -See the next chapters to see how to declare validations for other types, like numbers. +请参阅下一章节,以了解如何声明对其他类型例如数值的校验。 From 2e9036321e5104852a552967df87fc61df8e52f5 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:53 +0800 Subject: [PATCH 148/163] New translations query-params.md (Chinese Simplified) --- docs/zh/docs/tutorial/query-params.md | 122 ++++++++++++++------------ 1 file changed, 64 insertions(+), 58 deletions(-) diff --git a/docs/zh/docs/tutorial/query-params.md b/docs/zh/docs/tutorial/query-params.md index 0b74b10f81c8c..dead131d45f8e 100644 --- a/docs/zh/docs/tutorial/query-params.md +++ b/docs/zh/docs/tutorial/query-params.md @@ -1,42 +1,42 @@ -# Query Parameters +# 查询参数 -When you declare other function parameters that are not part of the path parameters, they are automatically interpreted as "query" parameters. +声明不属于路径参数的其他函数参数时,它们将被自动解释为"查询字符串"参数 ```Python hl_lines="9" {!../../../docs_src/query_params/tutorial001.py!} ``` -The query is the set of key-value pairs that go after the `?` in a URL, separated by `&` characters. +查询字符串是键值对的集合,这些键值对位于 URL 的 `? ` 之后,并以 `&` 符号分隔。 -For example, in the URL: +例如,在以下 url 中: ``` http://127.0.0.1:8000/items/?skip=0&limit=10 ``` -...the query parameters are: +...查询参数为: -* `skip`: with a value of `0` -* `limit`: with a value of `10` +* `skip`:对应的值为 `0` +* `limit`:对应的值为 `10` -As they are part of the URL, they are "naturally" strings. +由于它们是 URL 的一部分,因此它们的"原始值"是字符串。 -But when you declare them with Python types (in the example above, as `int`), they are converted to that type and validated against it. +但是,当你为它们声明了 Python 类型(在上面的示例中为 `int`)时,它们将转换为该类型并针对该类型进行校验。 -All the same process that applied for path parameters also applies for query parameters: +应用于路径参数的所有相同过程也适用于查询参数: -* Editor support (obviously) -* Data "parsing" -* Data validation -* Automatic documentation +* (很明显的)编辑器支持 +* 数据"解析" +* 数据校验 +* 自动生成文档 -## Defaults +## 默认值 -As query parameters are not a fixed part of a path, they can be optional and can have default values. +由于查询参数不是路径的固定部分,因此它们可以是可选的,并且可以有默认值。 -In the example above they have default values of `skip=0` and `limit=10`. +在上面的示例中,它们具有 `skip=0` 和 `limit=10` 的默认值。 -So, going to the URL: +因此,访问 URL: ``` http://127.0.0.1:8000/items/ @@ -48,94 +48,97 @@ would be the same as going to: http://127.0.0.1:8000/items/?skip=0&limit=10 ``` -But if you go to, for example: +但是,如果你访问的是: ``` http://127.0.0.1:8000/items/?skip=20 ``` -The parameter values in your function will be: +函数中的参数值将会是: -* `skip=20`: because you set it in the URL -* `limit=10`: because that was the default value +* `skip=20`:在 URL 中设定的值 +* `limit=10`:使用默认值 -## Optional parameters +## 可选参数 -The same way, you can declare optional query parameters, by setting their default to `None`: +通过同样的方式,你可以将它们的默认值设置为 `None` 来声明可选查询参数: === "Python 3.10+" ```Python hl_lines="7" - {!> ../../../docs_src/query_params/tutorial002_py310.py!} + 将与访问以下地址相同: ``` === "Python 3.6+" ```Python hl_lines="9" - {!> ../../../docs_src/query_params/tutorial002.py!} + {!../../../docs_src/query_params/tutorial002.py!} ``` -In this case, the function parameter `q` will be optional, and will be `None` by default. +在这个例子中,函数参数 `q` 将是可选的,并且默认值为 `None`。 !!! check Also notice that **FastAPI** is smart enough to notice that the path parameter `item_id` is a path parameter and `q` is not, so, it's a query parameter. -## Query parameter type conversion +## 查询参数类型转换 -You can also declare `bool` types, and they will be converted: +你还可以声明 `bool` 类型,它们将被自动转换: === "Python 3.10+" ```Python hl_lines="7" - {!> ../../../docs_src/query_params/tutorial003_py310.py!} + {!../../../docs_src/query_params/tutorial003.py!} ``` === "Python 3.6+" ```Python hl_lines="9" - {!> ../../../docs_src/query_params/tutorial003.py!} + !!! check + 还要注意的是,FastAPI 足够聪明,能够分辨出参数 item_id 是路径参数而 q 不是,因此 q 是一个查询参数。 ``` + 是路径参数而 q 不是,因此 q 是一个查询参数。 + -In this case, if you go to: +这个例子中,如果你访问: ``` http://127.0.0.1:8000/items/foo?short=1 ``` -or +或 ``` http://127.0.0.1:8000/items/foo?short=True ``` -or +或 ``` http://127.0.0.1:8000/items/foo?short=true ``` -or +或 ``` http://127.0.0.1:8000/items/foo?short=on ``` -or +或 ``` http://127.0.0.1:8000/items/foo?short=yes ``` -or any other case variation (uppercase, first letter in uppercase, etc), your function will see the parameter `short` with a `bool` value of `True`. Otherwise as `False`. +或任何其他的变体形式(大写,首字母大写等等),你的函数接收的 `short` 参数都会是布尔值 `True`。 对于值为 `False` 的情况也是一样的。 -## Multiple path and query parameters +## 多个路径和查询参数 -You can declare multiple path parameters and query parameters at the same time, **FastAPI** knows which is which. +你可以同时声明多个路径参数和查询参数,**FastAPI** 能够识别它们。 -And you don't have to declare them in any specific order. +而且你不需要以任何特定的顺序来声明。 -They will be detected by name: +它们将通过名称被检测到: === "Python 3.10+" @@ -146,30 +149,30 @@ They will be detected by name: === "Python 3.6+" ```Python hl_lines="8 10" - {!> ../../../docs_src/query_params/tutorial004.py!} + {!../../../docs_src/query_params/tutorial004.py!} ``` -## Required query parameters +## 必需查询参数 -When you declare a default value for non-path parameters (for now, we have only seen query parameters), then it is not required. +当你为非路径参数声明了默认值时(目前而言,我们所知道的仅有查询参数),则该参数不是必需的。 -If you don't want to add a specific value but just make it optional, set the default as `None`. +如果你不想添加一个特定的值,而只是想使该参数成为可选的,则将默认值设置为 `None`。 -But when you want to make a query parameter required, you can just not declare any default value: +但当你想让一个查询参数成为必需的,不声明任何默认值就可以: ```Python hl_lines="6-7" {!../../../docs_src/query_params/tutorial005.py!} ``` -Here the query parameter `needy` is a required query parameter of type `str`. +这里的查询参数 `needy` 是类型为 `str` 的必需查询参数。 -If you open in your browser a URL like: +如果你在浏览器中打开一个像下面的 URL: ``` http://127.0.0.1:8000/items/foo-item ``` -...without adding the required parameter `needy`, you will see an error like: +...因为没有添加必需的参数 `needy`,你将看到类似以下的错误: ```JSON { @@ -186,13 +189,13 @@ http://127.0.0.1:8000/items/foo-item } ``` -As `needy` is a required parameter, you would need to set it in the URL: +由于 `needy` 是必需参数,因此你需要在 URL 中设置它的值: ``` http://127.0.0.1:8000/items/foo-item?needy=sooooneedy ``` -...this would work: +...这样就正常了: ```JSON { @@ -201,25 +204,28 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy } ``` -And of course, you can define some parameters as required, some as having a default value, and some entirely optional: +当然,你也可以定义一些参数为必需的,一些具有默认值,而某些则完全是可选的: === "Python 3.10+" ```Python hl_lines="8" - {!> ../../../docs_src/query_params/tutorial006_py310.py!} + {!../../../docs_src/query_params/tutorial006.py!} ``` === "Python 3.6+" ```Python hl_lines="10" - {!> ../../../docs_src/query_params/tutorial006.py!} + !!! tip + 你还可以像在 路径参数{.internal-link target=_blank} 中那样使用 Enum。 ``` +。 + -In this case, there are 3 query parameters: +在这个例子中,有3个查询参数: -* `needy`, a required `str`. -* `skip`, an `int` with a default value of `0`. -* `limit`, an optional `int`. +* `needy`,一个必需的 `str` 类型参数。 +* `skip`,一个默认值为 `0` 的 `int` 类型参数。 +* `limit`,一个可选的 `int` 类型参数。 !!! tip You could also use `Enum`s the same way as with [Path Parameters](path-params.md#predefined-values){.internal-link target=_blank}. From aa73a0eb510060f2f9cfd0588654a442aba608b4 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:55 +0800 Subject: [PATCH 149/163] New translations request-files.md (Chinese Simplified) --- docs/zh/docs/tutorial/request-files.md | 156 ++++++++++++------------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/docs/zh/docs/tutorial/request-files.md b/docs/zh/docs/tutorial/request-files.md index d2d649924f859..cf5d05139c827 100644 --- a/docs/zh/docs/tutorial/request-files.md +++ b/docs/zh/docs/tutorial/request-files.md @@ -1,28 +1,28 @@ -# Request Files +# 请求文件 -You can define files to be uploaded by the client using `File`. +`File` 用于定义客户端的上传文件。 !!! info To receive uploaded files, first install `python-multipart`. - E.g. `pip install python-multipart`. + E.g. 例如: `pip install python-multipart`。 - This is because uploaded files are sent as "form data". + 因为上传文件以「表单数据」形式发送。 -## Import `File` +## 导入 `File` -Import `File` and `UploadFile` from `fastapi`: +从 `fastapi` 导入 `File` 和 `UploadFile`: === "Python 3.9+" ```Python hl_lines="3" - {!> ../../../docs_src/request_files/tutorial001_an_py39.py!} + !!! info "说明" ``` === "Python 3.6+" ```Python hl_lines="1" - {!> ../../../docs_src/request_files/tutorial001_an.py!} + FastAPI 支持同时上传多个文件。 ``` === "Python 3.6+ non-Annotated" @@ -31,23 +31,23 @@ Import `File` and `UploadFile` from `fastapi`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="1" - {!> ../../../docs_src/request_files/tutorial001.py!} + {!../../../docs_src/request_files/tutorial001.py!} ``` -## Define `File` Parameters +## 定义 `File` 参数 -Create file parameters the same way you would for `Body` or `Form`: +创建文件(`File`)参数的方式与 `Body` 和 `Form` 一样: === "Python 3.9+" ```Python hl_lines="9" - {!> ../../../docs_src/request_files/tutorial001_an_py39.py!} + !!! info "说明" ``` === "Python 3.6+" ```Python hl_lines="8" - {!> ../../../docs_src/request_files/tutorial001_an.py!} + 使用 `async` 方法时,**FastAPI** 在线程池中执行文件方法,并 `await` 操作完成。 ``` === "Python 3.6+ non-Annotated" @@ -56,39 +56,38 @@ Create file parameters the same way you would for `Body` or `Form`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="7" - {!> ../../../docs_src/request_files/tutorial001.py!} + {!../../../docs_src/request_files/tutorial001.py!} ``` -!!! info - `File` is a class that inherits directly from `Form`. +!!! `File` 是直接继承自 `Form` 的类。 - But remember that when you import `Query`, `Path`, `File` and others from `fastapi`, those are actually functions that return special classes. + 注意,从 `fastapi` 导入的 `Query`、`Path`、`File` 等项,实际上是返回特定类的函数。 !!! tip To declare File bodies, you need to use `File`, because otherwise the parameters would be interpreted as query parameters or body (JSON) parameters. -The files will be uploaded as "form data". +文件作为「表单数据」上传。 -If you declare the type of your *path operation function* parameter as `bytes`, **FastAPI** will read the file for you and you will receive the contents as `bytes`. +如果把*路径操作函数*参数的类型声明为 `bytes`,**FastAPI** 将以 `bytes` 形式读取和接收文件内容。 Have in mind that this means that the whole contents will be stored in memory. This will work well for small files. -But there are several cases in which you might benefit from using `UploadFile`. +不过,很多情况下,`UploadFile` 更好用。 -## File Parameters with `UploadFile` +## 含 `UploadFile` 的文件参数 -Define a file parameter with a type of `UploadFile`: +定义文件参数时使用 `UploadFile`: === "Python 3.9+" ```Python hl_lines="14" - {!> ../../../docs_src/request_files/tutorial001_an_py39.py!} + !!! note "技术细节" ``` === "Python 3.6+" ```Python hl_lines="13" - {!> ../../../docs_src/request_files/tutorial001_an.py!} + 声明文件体必须使用 `File`,否则,FastAPI 会把该参数当作查询参数或请求体(JSON)参数。 ``` === "Python 3.6+ non-Annotated" @@ -97,45 +96,45 @@ Define a file parameter with a type of `UploadFile`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="12" - {!> ../../../docs_src/request_files/tutorial001.py!} + {!../../../docs_src/request_files/tutorial001.py!} ``` -Using `UploadFile` has several advantages over `bytes`: +`UploadFile` 与 `bytes` 相比有更多优势: -* You don't have to use `File()` in the default value of the parameter. -* It uses a "spooled" file: - * A file stored in memory up to a maximum size limit, and after passing this limit it will be stored in disk. -* This means that it will work well for large files like images, videos, large binaries, etc. without consuming all the memory. -* You can get metadata from the uploaded file. -* It has a file-like `async` interface. -* It exposes an actual Python `SpooledTemporaryFile` object that you can pass directly to other libraries that expect a file-like object. +* 本节介绍了如何用 `File` 把上传文件声明为(表单数据的)输入参数。 +* 使用 `spooled` 文件: + * 存储在内存的文件超出最大上限时,FastAPI 会把文件存入磁盘; +* 这种方式更适于处理图像、视频、二进制文件等大型文件,好处是不会占用所有内存; +* 可获取上传文件的元数据; +* 暴露的 Python `SpooledTemporaryFile` 对象,可直接传递给其他预期「file-like」对象的库。 +* 自带 file-like `async` 接口; ### `UploadFile` -`UploadFile` has the following attributes: +`UploadFile` 的属性如下: -* `filename`: A `str` with the original file name that was uploaded (e.g. `myimage.jpg`). -* `content_type`: A `str` with the content type (MIME type / media type) (e.g. `image/jpeg`). -* `file`: A `SpooledTemporaryFile` (a file-like object). This is the actual Python file that you can pass directly to other functions or libraries that expect a "file-like" object. +* `filename`:上传文件名字符串(`str`),例如, `myimage.jpg`; +* `content_type`:内容类型(MIME 类型 / 媒体类型)字符串(`str`),例如,`image/jpeg`; +* `file`: `SpooledTemporaryFile`file-like 对象)。 其实就是 Python文件,可直接传递给其他预期 `file-like` 对象的函数或支持库。 `UploadFile` has the following `async` methods. They all call the corresponding file methods underneath (using the internal `SpooledTemporaryFile`). -* `write(data)`: Writes `data` (`str` or `bytes`) to the file. -* `read(size)`: Reads `size` (`int`) bytes/characters of the file. -* `seek(offset)`: Goes to the byte position `offset` (`int`) in the file. - * E.g., `await myfile.seek(0)` would go to the start of the file. - * This is especially useful if you run `await myfile.read()` once and then need to read the contents again. -* `close()`: Closes the file. +* `write(data)`:把 `data` (`str` 或 `bytes`)写入文件; +* `read(size)`:按指定数量的字节或字符(`size` (`int`))读取文件内容; +* `seek(offset)`:移动至文件 `offset` (`int`)字节处的位置; + * 例如,`await myfile.seek(0)` 移动到文件开头; + * 执行 `await myfile.read()` 后,需再次读取已读取内容时,这种方法特别好用; +* `close()`:关闭文件。 -As all these methods are `async` methods, you need to "await" them. +因为上述方法都是 `async` 方法,要搭配「await」使用。 -For example, inside of an `async` *path operation function* you can get the contents with: +例如,在 `async` *路径操作函数* 内,要用以下方式读取文件内容: ```Python contents = await myfile.read() ``` -If you are inside of a normal `def` *path operation function*, you can access the `UploadFile.file` directly, for example: +在普通 `def` *路径操作函数* 内,则可以直接访问 `UploadFile.file`,例如: ```Python contents = myfile.file.read() @@ -146,45 +145,45 @@ contents = myfile.file.read() !!! note "Starlette Technical Details" **FastAPI**'s `UploadFile` inherits directly from **Starlette**'s `UploadFile`, but adds some necessary parts to make it compatible with **Pydantic** and the other parts of FastAPI. -## What is "Form Data" +## 什么是 「表单数据」 -The way HTML forms (`
`) sends the data to the server normally uses a "special" encoding for that data, it's different from JSON. +与 JSON 不同,HTML 表单(`
`)向服务器发送数据通常使用「特殊」的编码。 -**FastAPI** will make sure to read that data from the right place instead of JSON. +**FastAPI** 要确保从正确的位置读取数据,而不是读取 JSON。 -!!! note "Technical Details" - Data from forms is normally encoded using the "media type" `application/x-www-form-urlencoded` when it doesn't include files. +!!! 不包含文件时,表单数据一般用 `application/x-www-form-urlencoded`「媒体类型」编码。 - But when the form includes files, it is encoded as `multipart/form-data`. If you use `File`, **FastAPI** will know it has to get the files from the correct part of the body. + 但表单包含文件时,编码为 `multipart/form-data`。 使用了 `File`,**FastAPI** 就知道要从请求体的正确位置获取文件。 - If you want to read more about these encodings and form fields, head to the MDN web docs for POST. + 编码和表单字段详见 MDN Web 文档的 POST 小节。 -!!! warning - You can declare multiple `File` and `Form` parameters in a *path operation*, but you can't also declare `Body` fields that you expect to receive as JSON, as the request will have the body encoded using `multipart/form-data` instead of `application/json`. +!!! 可在一个*路径操作*中声明多个 `File` 和 `Form` 参数,但不能同时声明要接收 JSON 的 `Body` 字段。 因为此时请求体的编码是 `multipart/form-data`,不是 `application/json`。 - This is not a limitation of **FastAPI**, it's part of the HTTP protocol. + 这不是 **FastAPI** 的问题,而是 HTTP 协议的规定。 -## Optional File Upload +## 可选文件上传 -You can make a file optional by using standard type annotations and setting a default value of `None`: +您可以通过使用标准类型注解并将 None 作为默认值的方式将一个文件参数设为可选: === "Python 3.10+" ```Python hl_lines="9 17" - {!> ../../../docs_src/request_files/tutorial001_02_an_py310.py!} + 这种方式把文件的所有内容都存储在内存里,适用于小型文件。 ``` === "Python 3.9+" ```Python hl_lines="9 17" - {!> ../../../docs_src/request_files/tutorial001_02_an_py39.py!} + !!! warning "警告" ``` === "Python 3.6+" ```Python hl_lines="10 18" - {!> ../../../docs_src/request_files/tutorial001_02_an.py!} + !!! note "async 技术细节" ``` + 技术细节" + === "Python 3.10+ non-Annotated" @@ -204,20 +203,20 @@ You can make a file optional by using standard type annotations and setting a de {!> ../../../docs_src/request_files/tutorial001_02.py!} ``` -## `UploadFile` with Additional Metadata +## 带有额外元数据的 `UploadFile` -You can also use `File()` with `UploadFile`, for example, to set additional metadata: +您也可以将 `File()` 与 `UploadFile` 一起使用,例如,设置额外的元数据: === "Python 3.9+" ```Python hl_lines="9 15" - {!> ../../../docs_src/request_files/tutorial001_03_an_py39.py!} + !!! tip "提示" ``` === "Python 3.6+" ```Python hl_lines="8 14" - {!> ../../../docs_src/request_files/tutorial001_03_an.py!} + **FastAPI** 的 `UploadFile` 直接继承自 **Starlette** 的 `UploadFile`,但添加了一些必要功能,使之与 **Pydantic** 及 FastAPI 的其它部件兼容。 ``` === "Python 3.6+ non-Annotated" @@ -226,28 +225,30 @@ You can also use `File()` with `UploadFile`, for example, to set additional meta Prefer to use the `Annotated` version if possible. ```Python hl_lines="7 13" - {!> ../../../docs_src/request_files/tutorial001_03.py!} + 可用同一个「表单字段」发送含多个文件的「表单数据」。 ``` -## Multiple File Uploads +## 多文件上传 It's possible to upload several files at the same time. They would be associated to the same "form field" sent using "form data". -To use that, declare a list of `bytes` or `UploadFile`: +上传多个文件时,要声明含 `bytes` 或 `UploadFile` 的列表(`List`): === "Python 3.9+" ```Python hl_lines="10 15" - {!> ../../../docs_src/request_files/tutorial002_an_py39.py!} + !!! note "Starlette 技术细节" ``` === "Python 3.6+" ```Python hl_lines="11 16" - {!> ../../../docs_src/request_files/tutorial002_an.py!} + UploadFile 支持以下 async 方法,(使用内部 SpooledTemporaryFile)可调用相应的文件方法。 ``` + 支持以下 async 方法,(使用内部 SpooledTemporaryFile)可调用相应的文件方法。 + === "Python 3.9+ non-Annotated" @@ -267,27 +268,26 @@ To use that, declare a list of `bytes` or `UploadFile`: {!> ../../../docs_src/request_files/tutorial002.py!} ``` -You will receive, as declared, a `list` of `bytes` or `UploadFile`s. +接收的也是含 `bytes` 或 `UploadFile` 的列表(`list`)。 -!!! note "Technical Details" - You could also use `from starlette.responses import HTMLResponse`. +!!! 也可以使用 `from starlette.responses import HTMLResponse`。 - **FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. + `fastapi.responses` 其实与 `starlette.responses` 相同,只是为了方便开发者调用。 实际上,大多数 **FastAPI** 的响应都直接从 Starlette 调用。 But most of the available responses come directly from Starlette. -### Multiple File Uploads with Additional Metadata +### 带有额外元数据的多文件上传 -And the same way as before, you can use `File()` to set additional parameters, even for `UploadFile`: +和之前的方式一样, 您可以为 `File()` 设置额外参数, 即使是 `UploadFile`: === "Python 3.9+" ```Python hl_lines="11 18-20" - {!> ../../../docs_src/request_files/tutorial003_an_py39.py!} + !!! note "技术细节" ``` === "Python 3.6+" ```Python hl_lines="12 19-21" - {!> ../../../docs_src/request_files/tutorial003_an.py!} + {!../../../docs_src/request_files/tutorial001_03.py!} ``` === "Python 3.9+ non-Annotated" From 923cceb2be36162296331baebc60b888a0001e64 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:56 +0800 Subject: [PATCH 150/163] New translations request-forms-and-files.md (Chinese Simplified) --- .../docs/tutorial/request-forms-and-files.md | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/docs/zh/docs/tutorial/request-forms-and-files.md b/docs/zh/docs/tutorial/request-forms-and-files.md index 1818946c4e087..6dc779760bfbf 100644 --- a/docs/zh/docs/tutorial/request-forms-and-files.md +++ b/docs/zh/docs/tutorial/request-forms-and-files.md @@ -1,18 +1,17 @@ -# Request Forms and Files +# 请求表单与文件 -You can define files and form fields at the same time using `File` and `Form`. +FastAPI 支持同时使用 `File` 和 `Form` 定义文件和表单字段。 -!!! info - To receive uploaded files and/or form data, first install `python-multipart`. +!!! 接收上传文件或表单数据,要预先安装 <a href="https://andrew-d.github.io/python-multipart/" class="external-link" target="_blank">`python-multipart`</a>。 - E.g. `pip install python-multipart`. + E.g. 例如,`pip install python-multipart`。 -## Import `File` and `Form` +## 导入 `File` 与 `Form` === "Python 3.9+" ```Python hl_lines="3" - {!> ../../../docs_src/request_forms_and_files/tutorial001_an_py39.py!} + !!! warning "警告" ``` === "Python 3.6+" @@ -27,23 +26,23 @@ You can define files and form fields at the same time using `File` and `Form`. Prefer to use the `Annotated` version if possible. ```Python hl_lines="1" - {!> ../../../docs_src/request_forms_and_files/tutorial001.py!} + {!../../../docs_src/request_forms_and_files/tutorial001.py!} ``` -## Define `File` and `Form` parameters +## 定义 `File` 与 `Form` 参数 -Create file and form parameters the same way you would for `Body` or `Query`: +创建文件和表单参数的方式与 `Body` 和 `Query` 一样: === "Python 3.9+" ```Python hl_lines="10-12" - {!> ../../../docs_src/request_forms_and_files/tutorial001_an_py39.py!} + 小结 ``` === "Python 3.6+" ```Python hl_lines="9-11" - {!> ../../../docs_src/request_forms_and_files/tutorial001_an.py!} + !!! info "说明" ``` === "Python 3.6+ non-Annotated" @@ -52,18 +51,17 @@ Create file and form parameters the same way you would for `Body` or `Query`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="8" - {!> ../../../docs_src/request_forms_and_files/tutorial001.py!} + {!../../../docs_src/request_forms_and_files/tutorial001.py!} ``` -The files and form fields will be uploaded as form data and you will receive the files and form fields. +文件和表单字段作为表单数据上传与接收。 -And you can declare some of the files as `bytes` and some as `UploadFile`. +声明文件可以使用 `bytes` 或 `UploadFile` 。 -!!! warning - You can declare multiple `File` and `Form` parameters in a *path operation*, but you can't also declare `Body` fields that you expect to receive as JSON, as the request will have the body encoded using `multipart/form-data` instead of `application/json`. +!!! 可在一个*路径操作*中声明多个 `File` 与 `Form` 参数,但不能同时声明要接收 JSON 的 `Body` 字段。 因为此时请求体的编码为 `multipart/form-data`,不是 `application/json`。 - This is not a limitation of **FastAPI**, it's part of the HTTP protocol. + 这不是 **FastAPI** 的问题,而是 HTTP 协议的规定。 ## Recap -Use `File` and `Form` together when you need to receive data and files in the same request. +在同一个请求中接收数据和文件时,应同时使用 `File` 和 `Form`。 From 6b3d210d2212a6331824e1e0d83f51fa71be5919 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:57 +0800 Subject: [PATCH 151/163] New translations request-forms.md (Chinese Simplified) --- docs/zh/docs/tutorial/request-forms.md | 55 ++++++++++++-------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/docs/zh/docs/tutorial/request-forms.md b/docs/zh/docs/tutorial/request-forms.md index 1a608f33f5a3b..06300fee297d5 100644 --- a/docs/zh/docs/tutorial/request-forms.md +++ b/docs/zh/docs/tutorial/request-forms.md @@ -1,26 +1,25 @@ -# Form Data +# 表单数据 -When you need to receive form fields instead of JSON, you can use `Form`. +接收的不是 JSON,而是表单字段时,要使用 `Form`。 -!!! info - To use forms, first install `python-multipart`. +!!! 要使用表单,需预先安装 <a href="https://andrew-d.github.io/python-multipart/" class="external-link" target="_blank">`python-multipart`</a>。 - E.g. `pip install python-multipart`. + E.g. 例如,`pip install python-multipart`。 -## Import `Form` +## 导入 `Form` -Import `Form` from `fastapi`: +从 `fastapi` 导入 `Form`: === "Python 3.9+" ```Python hl_lines="3" - {!> ../../../docs_src/request_forms/tutorial001_an_py39.py!} + !!! tip "提示" ``` === "Python 3.6+" ```Python hl_lines="1" - {!> ../../../docs_src/request_forms/tutorial001_an.py!} + `Form` 是直接继承自 `Body` 的类。 ``` === "Python 3.6+ non-Annotated" @@ -29,23 +28,23 @@ Import `Form` from `fastapi`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="1" - {!> ../../../docs_src/request_forms/tutorial001.py!} + {!../../../docs_src/request_forms/tutorial001.py!} ``` -## Define `Form` parameters +## 定义 `Form` 参数 -Create form parameters the same way you would for `Body` or `Query`: +创建表单(`Form`)参数的方式与 `Body` 和 `Query` 一样: === "Python 3.9+" ```Python hl_lines="9" - {!> ../../../docs_src/request_forms/tutorial001_an_py39.py!} + !!! info "说明" ``` === "Python 3.6+" ```Python hl_lines="8" - {!> ../../../docs_src/request_forms/tutorial001_an.py!} + 声明表单体要显式使用 `Form` ,否则,FastAPI 会把该参数当作查询参数或请求体(JSON)参数。 ``` === "Python 3.6+ non-Annotated" @@ -54,14 +53,14 @@ Create form parameters the same way you would for `Body` or `Query`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="7" - {!> ../../../docs_src/request_forms/tutorial001.py!} + {!../../../docs_src/request_forms/tutorial001.py!} ``` -For example, in one of the ways the OAuth2 specification can be used (called "password flow") it is required to send a `username` and `password` as form fields. +例如,OAuth2 规范的 "密码流" 模式规定要通过表单字段发送 `username` 和 `password`。 -The spec requires the fields to be exactly named `username` and `password`, and to be sent as form fields, not JSON. +该规范要求字段必须命名为 `username` 和 `password`,并通过表单字段发送,不能用 JSON。 -With `Form` you can declare the same configurations as with `Body` (and `Query`, `Path`, `Cookie`), including validation, examples, an alias (e.g. `user-name` instead of `username`), etc. +使用 `Form` 可以声明与 `Body` (及 `Query`、`Path`、`Cookie`)相同的元数据和验证。 !!! info `Form` is a class that inherits directly from `Body`. @@ -69,24 +68,22 @@ With `Form` you can declare the same configurations as with `Body` (and `Query`, !!! tip To declare form bodies, you need to use `Form` explicitly, because without it the parameters would be interpreted as query parameters or body (JSON) parameters. -## About "Form Fields" +## 关于 "表单字段" -The way HTML forms (`
`) sends the data to the server normally uses a "special" encoding for that data, it's different from JSON. +与 JSON 不同,HTML 表单(`
`)向服务器发送数据通常使用「特殊」的编码。 -**FastAPI** will make sure to read that data from the right place instead of JSON. +**FastAPI** 要确保从正确的位置读取数据,而不是读取 JSON。 -!!! note "Technical Details" - Data from forms is normally encoded using the "media type" `application/x-www-form-urlencoded`. +!!! 表单数据的「媒体类型」编码一般为 `application/x-www-form-urlencoded`。 - But when the form includes files, it is encoded as `multipart/form-data`. You'll read about handling files in the next chapter. + 但包含文件的表单编码为 `multipart/form-data`。 You'll read about handling files in the next chapter. - If you want to read more about these encodings and form fields, head to the MDN web docs for POST. + 编码和表单字段详见 MDN Web 文档的 POST小节。 -!!! warning - You can declare multiple `Form` parameters in a *path operation*, but you can't also declare `Body` fields that you expect to receive as JSON, as the request will have the body encoded using `application/x-www-form-urlencoded` instead of `application/json`. +!!! 可在一个*路径操作*中声明多个 `Form` 参数,但不能同时声明要接收 JSON 的 `Body` 字段。 因为此时请求体的编码是 `application/x-www-form-urlencoded`,不是 `application/json`。 - This is not a limitation of **FastAPI**, it's part of the HTTP protocol. + 这不是 **FastAPI** 的问题,而是 HTTP 协议的规定。 ## Recap -Use `Form` to declare form data input parameters. +本节介绍了如何使用 `Form` 声明表单数据输入参数。 From a319c7bdd2bd8749bfb672d280027ab38b8635d4 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:58 +0800 Subject: [PATCH 152/163] New translations response-model.md (Chinese Simplified) --- docs/zh/docs/tutorial/response-model.md | 157 +++++++++++++----------- 1 file changed, 82 insertions(+), 75 deletions(-) diff --git a/docs/zh/docs/tutorial/response-model.md b/docs/zh/docs/tutorial/response-model.md index 9c849bf3a0453..121d6543453e4 100644 --- a/docs/zh/docs/tutorial/response-model.md +++ b/docs/zh/docs/tutorial/response-model.md @@ -1,6 +1,6 @@ -# Response Model - Return Type +# 响应模型 -You can declare the type used for the response by annotating the *path operation function* **return type**. +你可以在任意的*路径操作*中使用 `response_model` 参数来声明用于响应的模型: You can use **type annotations** the same way you would for input data in function **parameters**, you can use Pydantic models, lists, dictionaries, scalar values like integers, booleans, etc. @@ -13,7 +13,7 @@ You can use **type annotations** the same way you would for input data in functi === "Python 3.9+" ```Python hl_lines="18 23" - {!> ../../../docs_src/response_model/tutorial001_01_py39.py!} + https://fastapi.tiangolo.com/img/tutorial/response-model/image02.png ``` === "Python 3.6+" @@ -22,20 +22,22 @@ You can use **type annotations** the same way you would for input data in functi {!> ../../../docs_src/response_model/tutorial001_01.py!} ``` -FastAPI will use this return type to: +!!! info + 你还可以使用: * **Validate** the returned data. * If the data is invalid (e.g. you are missing a field), it means that *your* app code is broken, not returning what it should, and it will return a server error instead of returning incorrect data. This way you and your clients can be certain that they will receive the data and the data shape expected. -* Add a **JSON Schema** for the response, in the OpenAPI *path operation*. +* 在 OpenAPI 的*路径操作*中为响应添加一个 JSON Schema。 * This will be used by the **automatic docs**. * It will also be used by automatic client code generation tools. -But most importantly: +但最重要的是: * It will **limit and filter** the output data to what is defined in the return type. * This is particularly important for **security**, we'll see more of that below. -## `response_model` Parameter +## !!! tip + `{"name", "description"}` 语法创建一个具有这两个值的 `set`。 There are some cases where you need or want to return some data that is not exactly what the type declares. @@ -43,15 +45,17 @@ For example, you could want to **return a dictionary** or a database object, but If you added the return type annotation, tools and editors would complain with a (correct) error telling you that your function is returning a type (e.g. a dict) that is different from what you declared (e.g. a Pydantic model). -In those cases, you can use the *path operation decorator* parameter `response_model` instead of the return type. +!!! tip + 但是依然建议你使用上面提到的主意,使用多个类而不是这些参数。 -You can use the `response_model` parameter in any of the *path operations*: +!!! note "技术细节" + 响应模型在参数中被声明,而不是作为函数返回类型的注解,这是因为路径函数可能不会真正返回该响应模型,而是返回一个 `dict`、数据库对象或其他模型,然后再使用 `response_model` 来执行字段约束和序列化。 * `@app.get()` * `@app.post()` * `@app.put()` * `@app.delete()` -* etc. +* 等等。 === "Python 3.10+" @@ -68,32 +72,33 @@ You can use the `response_model` parameter in any of the *path operations*: === "Python 3.6+" ```Python hl_lines="17 22 24-27" - {!> ../../../docs_src/response_model/tutorial001.py!} + 并在自动生成文档系统中使用。 ``` -!!! note - Notice that `response_model` is a parameter of the "decorator" method (`get`, `post`, etc). Not of your *path operation function*, like all the parameters and body. +!!! !!! note + 注意,`response_model`是「装饰器」方法(`get`,`post` 等)的一个参数。 不像之前的所有参数和请求体,它不属于*路径操作函数*。 -`response_model` receives the same type you would declare for a Pydantic model field, so, it can be a Pydantic model, but it can also be, e.g. a `list` of Pydantic models, like `List[Item]`. +它接收的类型与你将为 Pydantic 模型属性所声明的类型相同,因此它可以是一个 Pydantic 模型,但也可以是一个由 Pydantic 模型组成的 `list`,例如 `List[Item]`。 -FastAPI will use this `response_model` to do all the data documentation, validation, etc. and also to **convert and filter the output data** to its type declaration. +将输出数据转换为其声明的类型。 !!! tip If you have strict type checks in your editor, mypy, etc, you can declare the function return type as `Any`. That way you tell the editor that you are intentionally returning anything. But FastAPI will still do the data documentation, validation, filtering, etc. with the `response_model`. -### `response_model` Priority +### 使用 `response_model_exclude_unset` 参数 -If you declare both a return type and a `response_model`, the `response_model` will take priority and be used by FastAPI. +FastAPI 将使用此 `response_model` 来: This way you can add correct type annotations to your functions even when you are returning a type different than the response model, to be used by the editor and tools like mypy. And still you can have FastAPI do the data validation, documentation, etc. using the `response_model`. -You can also use `response_model=None` to disable creating a response model for that *path operation*, you might need to do it if you are adding type annotations for things that are not valid Pydantic fields, you will see an example of that in one of the sections below. +!!! danger + 永远不要存储用户的明文密码,也不要在响应中发送密码。 -## Return the same input data +## 返回与输入相同的数据 -Here we are declaring a `UserIn` model, it will contain a plaintext password: +现在我们声明一个 `UserIn` 模型,它将包含一个明文密码属性。 === "Python 3.10+" @@ -104,7 +109,7 @@ Here we are declaring a `UserIn` model, it will contain a plaintext password: === "Python 3.6+" ```Python hl_lines="9 11" - {!> ../../../docs_src/response_model/tutorial002.py!} + {!../../../docs_src/response_model/tutorial001.py!} ``` !!! info @@ -113,7 +118,7 @@ Here we are declaring a `UserIn` model, it will contain a plaintext password: E.g. `pip install email-validator` or `pip install pydantic[email]`. -And we are using this model to declare our input and the same model to declare our output: +我们正在使用此模型声明输入数据,并使用同一模型声明输出数据: === "Python 3.10+" @@ -124,21 +129,21 @@ And we are using this model to declare our input and the same model to declare o === "Python 3.6+" ```Python hl_lines="18" - {!> ../../../docs_src/response_model/tutorial002.py!} + {!../../../docs_src/response_model/tutorial002.py!} ``` -Now, whenever a browser is creating a user with a password, the API will return the same password in the response. +现在,每当浏览器使用一个密码创建用户时,API 都会在响应中返回相同的密码。 -In this case, it might not be a problem, because it's the same user sending the password. +在这个案例中,这可能不算是问题,因为用户自己正在发送密码。 -But if we use the same model for another *path operation*, we could be sending our user's passwords to every client. +但是,如果我们在其他的*路径操作*中使用相同的模型,则可能会将用户的密码发送给每个客户端。 !!! danger Never store the plain password of a user or send it in a response like this, unless you know all the caveats and you know what you are doing. -## Add an output model +## 添加输出模型 -We can instead create an input model with the plaintext password and an output model without it: +相反,我们可以创建一个有明文密码的输入模型和一个没有明文密码的输出模型: === "Python 3.10+" @@ -149,10 +154,10 @@ We can instead create an input model with the plaintext password and an output m === "Python 3.6+" ```Python hl_lines="9 11 16" - {!> ../../../docs_src/response_model/tutorial003.py!} + {!../../../docs_src/response_model/tutorial003.py!} ``` -Here, even though our *path operation function* is returning the same input user that contains the password: +这样,即便我们的*路径操作函数*将会返回包含密码的相同输入用户: === "Python 3.10+" @@ -163,10 +168,10 @@ Here, even though our *path operation function* is returning the same input user === "Python 3.6+" ```Python hl_lines="24" - {!> ../../../docs_src/response_model/tutorial003.py!} + {!../../../docs_src/response_model/tutorial003.py!} ``` -...we declared the `response_model` to be our model `UserOut`, that doesn't include the password: +...我们已经将 `response_model` 声明为了不包含密码的 `UserOut` 模型: === "Python 3.10+" @@ -180,9 +185,9 @@ Here, even though our *path operation function* is returning the same input user {!> ../../../docs_src/response_model/tutorial003.py!} ``` -So, **FastAPI** will take care of filtering out all the data that is not declared in the output model (using Pydantic). +因此,**FastAPI** 将会负责过滤掉未在输出模型中声明的所有数据(使用 Pydantic)。 -### `response_model` or Return Type +### `response_model_include` 和 `response_model_exclude` In this case, because the two models are different, if we annotated the function return type as `UserOut`, the editor and tools would complain that we are returning an invalid type, as those are different classes. @@ -194,7 +199,7 @@ That's why in this example we have to declare it in the `response_model` paramet Let's continue from the previous example. We wanted to **annotate the function with one type** but return something that includes **more data**. -We want FastAPI to keep **filtering** the data using the response model. +校验数据。 In the previous example, because the classes were different, we had to use the `response_model` parameter. But that also means that we don't get the support from the editor and tools checking the function return type. @@ -236,13 +241,13 @@ FastAPI does several things internally with Pydantic to make sure that those sam This way, you can get the best of both worlds: type annotations with **tooling support** and **data filtering**. -## See it in the docs +## 在文档中查看 -When you see the automatic docs, you can check that the input model and output model will both have their own JSON Schema: +当你查看自动化文档时,你可以检查输入模型和输出模型是否都具有自己的 JSON Schema: -And both models will be used for the interactive API documentation: +并且两种模型都将在交互式 API 文档中使用: @@ -255,7 +260,7 @@ There might be cases where you return something that is not a valid Pydantic fie The most common case would be [returning a Response directly as explained later in the advanced docs](../advanced/response-directly.md){.internal-link target=_blank}. ```Python hl_lines="8 10-11" -{!> ../../../docs_src/response_model/tutorial003_02.py!} +{!../../../docs_src/response_model/tutorial002.py!} ``` This simple case is handled automatically by FastAPI because the return type annotation is the class (or a subclass) of `Response`. @@ -267,7 +272,7 @@ And tools will also be happy because both `RedirectResponse` and `JSONResponse` You can also use a subclass of `Response` in the type annotation: ```Python hl_lines="8-9" -{!> ../../../docs_src/response_model/tutorial003_03.py!} +{!../../../docs_src/response_model/tutorial003.py!} ``` This will also work because `RedirectResponse` is a subclass of `Response`, and FastAPI will automatically handle this simple case. @@ -298,7 +303,8 @@ Continuing from the example above, you might not want to have the default data v But you might want to still keep the return type annotation in the function to get the support from tools like editors and type checkers (e.g. mypy). -In this case, you can disable the response model generation by setting `response_model=None`: +!!! tip + 请注意默认值可以是任何值,而不仅是`None`。 === "Python 3.10+" @@ -314,9 +320,9 @@ In this case, you can disable the response model generation by setting `response This will make FastAPI skip the response model generation and that way you can have any return type annotations you need without it affecting your FastAPI application. 🤓 -## Response Model encoding parameters +## 响应模型编码参数 -Your response model could have default values, like: +你的响应模型可以具有默认值,例如: === "Python 3.10+" @@ -333,20 +339,21 @@ Your response model could have default values, like: === "Python 3.6+" ```Python hl_lines="11 13-14" - {!> ../../../docs_src/response_model/tutorial004.py!} + {!../../../docs_src/response_model/tutorial004.py!} ``` -* `description: Union[str, None] = None` (or `str | None = None` in Python 3.10) has a default of `None`. -* `tax: float = 10.5` has a default of `10.5`. -* `tags: List[str] = []` as a default of an empty list: `[]`. +* `description: Union[str, None] = None` 具有默认值 `None`。 +* `tax: float = 10.5` 具有默认值 `10.5`. +* `tags: List[str] = []` 具有一个空列表作为默认值: `[]`. -but you might want to omit them from the result if they were not actually stored. +但如果它们并没有存储实际的值,你可能想从结果中忽略它们的默认值。 -For example, if you have models with many optional attributes in a NoSQL database, but you don't want to send very long JSON responses full of default values. +举个例子,当你在 NoSQL 数据库中保存了具有许多可选属性的模型,但你又不想发送充满默认值的很长的 JSON 响应。 -### Use the `response_model_exclude_unset` parameter +### !!! info + FastAPI 通过 Pydantic 模型的 `.dict()` 配合 该方法的 `exclude_unset` 参数 来实现此功能。 -You can set the *path operation decorator* parameter `response_model_exclude_unset=True`: +你可以设置*路径操作装饰器*的 `response_model_exclude_unset=True` 参数: === "Python 3.10+" @@ -363,12 +370,12 @@ You can set the *path operation decorator* parameter `response_model_exclude_uns === "Python 3.6+" ```Python hl_lines="24" - {!> ../../../docs_src/response_model/tutorial004.py!} + {!../../../docs_src/response_model/tutorial004.py!} ``` -and those default values won't be included in the response, only the values actually set. +然后响应中将不会包含那些默认值,而是仅有实际设置的值。 -So, if you send a request to that *path operation* for the item with ID `foo`, the response (not including default values) will be: +因此,如果你向*路径操作*发送 ID 为 `foo` 的商品的请求,则响应(不包括默认值)将为: ```JSON { @@ -386,11 +393,11 @@ So, if you send a request to that *path operation* for the item with ID `foo`, t * `response_model_exclude_defaults=True` * `response_model_exclude_none=True` - as described in the Pydantic docs for `exclude_defaults` and `exclude_none`. + 参考 Pydantic 文档 中对 `exclude_defaults` 和 `exclude_none` 的描述。 -#### Data with values for fields with defaults +#### 默认值字段有实际值的数据 -But if your data has values for the model's fields with default values, like the item with ID `bar`: +但是,如果你的数据在具有默认值的模型字段中有实际的值,例如 ID 为 `bar` 的项: ```Python hl_lines="3 5" { @@ -401,11 +408,11 @@ But if your data has values for the model's fields with default values, like the } ``` -they will be included in the response. +这些值将包含在响应中。 -#### Data with the same values as the defaults +#### 具有与默认值相同值的数据 -If the data has the same values as the default ones, like the item with ID `baz`: +如果数据具有与默认值相同的值,例如 ID 为 `baz` 的项: ```Python hl_lines="3 5-6" { @@ -417,29 +424,29 @@ If the data has the same values as the default ones, like the item with ID `baz` } ``` -FastAPI is smart enough (actually, Pydantic is smart enough) to realize that, even though `description`, `tax`, and `tags` have the same values as the defaults, they were set explicitly (instead of taken from the defaults). +即使 `description`、`tax` 和 `tags` 具有与默认值相同的值,FastAPI 足够聪明 (实际上是 Pydantic 足够聪明) 去认识到这一点,它们的值被显式地所设定(而不是取自默认值)。 -So, they will be included in the JSON response. +因此,它们将包含在 JSON 响应中。 !!! tip Notice that the default values can be anything, not only `None`. - They can be a list (`[]`), a `float` of `10.5`, etc. + 它们可以是一个列表(`[]`),一个值为 `10.5`的 `float`,等等。 ### `response_model_include` and `response_model_exclude` -You can also use the *path operation decorator* parameters `response_model_include` and `response_model_exclude`. +你还可以使用*路径操作装饰器*的 `response_model_include` 和 `response_model_exclude` 参数。 -They take a `set` of `str` with the name of the attributes to include (omitting the rest) or to exclude (including the rest). +它们接收一个由属性名称 `str` 组成的 `set` 来包含(忽略其他的)或者排除(包含其他的)这些属性。 -This can be used as a quick shortcut if you have only one Pydantic model and want to remove some data from the output. +如果你只有一个 Pydantic 模型,并且想要从输出中移除一些数据,则可以使用这种快捷方法。 !!! tip But it is still recommended to use the ideas above, using multiple classes, instead of these parameters. - This is because the JSON Schema generated in your app's OpenAPI (and the docs) will still be the one for the complete model, even if you use `response_model_include` or `response_model_exclude` to omit some attributes. + 这是因为即使使用 `response_model_include` 或 `response_model_exclude` 来省略某些属性,在应用程序的 OpenAPI 定义(和文档)中生成的 JSON Schema 仍将是完整的模型。 - This also applies to `response_model_by_alias` that works similarly. + 这也适用于作用类似的 `response_model_by_alias`。 === "Python 3.10+" @@ -450,17 +457,17 @@ This can be used as a quick shortcut if you have only one Pydantic model and wan === "Python 3.6+" ```Python hl_lines="31 37" - {!> ../../../docs_src/response_model/tutorial005.py!} + {!../../../docs_src/response_model/tutorial005.py!} ``` !!! tip The syntax `{"name", "description"}` creates a `set` with those two values. - It is equivalent to `set(["name", "description"])`. + 等同于 `set(["name", "description"])`。 -#### Using `list`s instead of `set`s +#### 使用 `list` 而不是 `set` -If you forget to use a `set` and use a `list` or `tuple` instead, FastAPI will still convert it to a `set` and it will work correctly: +如果你忘记使用 `set` 而是使用 `list` 或 `tuple`,FastAPI 仍会将其转换为 `set` 并且正常工作: === "Python 3.10+" @@ -471,11 +478,11 @@ If you forget to use a `set` and use a `list` or `tuple` instead, FastAPI will s === "Python 3.6+" ```Python hl_lines="31 37" - {!> ../../../docs_src/response_model/tutorial006.py!} + {!../../../docs_src/response_model/tutorial006.py!} ``` ## Recap -Use the *path operation decorator's* parameter `response_model` to define response models and especially to ensure private data is filtered out. +使用*路径操作装饰器*的 `response_model` 参数来定义响应模型,特别是确保私有数据被过滤掉。 -Use `response_model_exclude_unset` to return only the values explicitly set. +使用 `response_model_exclude_unset` 来仅返回显式设定的值。 From fde986fc3553db2f42902c93029dbba8acda9533 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:54:59 +0800 Subject: [PATCH 153/163] New translations response-status-code.md (Chinese Simplified) --- docs/zh/docs/tutorial/response-status-code.md | 86 +++++++++---------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/docs/zh/docs/tutorial/response-status-code.md b/docs/zh/docs/tutorial/response-status-code.md index 8e26706e53d62..5a265909abcff 100644 --- a/docs/zh/docs/tutorial/response-status-code.md +++ b/docs/zh/docs/tutorial/response-status-code.md @@ -1,89 +1,89 @@ -# Response Status Code +# 响应状态码 -The same way you can specify a response model, you can also declare the HTTP status code used for the response with the parameter `status_code` in any of the *path operations*: +与指定响应模型的方式相同,你也可以在以下任意的*路径操作*中使用 `status_code` 参数来声明用于响应的 HTTP 状态码: * `@app.get()` * `@app.post()` * `@app.put()` * `@app.delete()` -* etc. +* 等等。 ```Python hl_lines="6" {!../../../docs_src/response_status_code/tutorial001.py!} ``` -!!! note - Notice that `status_code` is a parameter of the "decorator" method (`get`, `post`, etc). Not of your *path operation function*, like all the parameters and body. +!!! !!! note + 注意,`status_code` 是「装饰器」方法(`get`,`post` 等)的一个参数。 不像之前的所有参数和请求体,它不属于*路径操作函数*。 -The `status_code` parameter receives a number with the HTTP status code. +`status_code` 参数接收一个表示 HTTP 状态码的数字。 -!!! info - `status_code` can alternatively also receive an `IntEnum`, such as Python's `http.HTTPStatus`. +!!! !!! info + `status_code` 也能够接收一个 `IntEnum` 类型,比如 Python 的 `http.HTTPStatus`。 -It will: +它将会: -* Return that status code in the response. -* Document it as such in the OpenAPI schema (and so, in the user interfaces): +* 在响应中返回该状态码。 +* 在 OpenAPI 模式中(以及在用户界面中)将其记录为: -!!! note - Some response codes (see the next section) indicate that the response does not have a body. +!!! !!! note + 一些响应状态码(请参阅下一部分)表示响应没有响应体。 - FastAPI knows this, and will produce OpenAPI docs that state there is no response body. + FastAPI 知道这一点,并将生成表明没有响应体的 OpenAPI 文档。 -## About HTTP status codes +## 关于 HTTP 状态码 -!!! note - If you already know what HTTP status codes are, skip to the next section. +!!! !!! note + 如果你已经了解什么是 HTTP 状态码,请跳到下一部分。 -In HTTP, you send a numeric status code of 3 digits as part of the response. +在 HTTP 协议中,你将发送 3 位数的数字状态码作为响应的一部分。 -These status codes have a name associated to recognize them, but the important part is the number. +这些状态码有一个识别它们的关联名称,但是重要的还是数字。 -In short: +简而言之: -* `100` and above are for "Information". You rarely use them directly. Responses with these status codes cannot have a body. -* **`200`** and above are for "Successful" responses. These are the ones you would use the most. - * `200` is the default status code, which means everything was "OK". - * Another example would be `201`, "Created". It is commonly used after creating a new record in the database. - * A special case is `204`, "No Content". This response is used when there is no content to return to the client, and so the response must not have a body. -* **`300`** and above are for "Redirection". Responses with these status codes may or may not have a body, except for `304`, "Not Modified", which must not have one. -* **`400`** and above are for "Client error" responses. These are the second type you would probably use the most. - * An example is `404`, for a "Not Found" response. - * For generic errors from the client, you can just use `400`. -* `500` and above are for server errors. You almost never use them directly. When something goes wrong at some part in your application code, or server, it will automatically return one of these status codes. +* `100` 及以上状态码用于「消息」响应。 你很少直接使用它们。 具有这些状态代码的响应不能带有响应体。 +* **`200`** 及以上状态码用于「成功」响应。 这些是你最常使用的。 + * `200` 是默认状态代码,它表示一切「正常」。 + * 另一个例子会是 `201`,「已创建」。 它通常在数据库中创建了一条新记录后使用。 + * 一个特殊的例子是 `204`,「无内容」。 此响应在没有内容返回给客户端时使用,因此该响应不能包含响应体。 +* **`300`** 及以上状态码用于「重定向」。 具有这些状态码的响应可能有或者可能没有响应体,但 `304`「未修改」是个例外,该响应不得含有响应体。 +* **`400`** 及以上状态码用于「客户端错误」响应。 这些可能是你第二常使用的类型。 + * 一个例子是 `404`,用于「未找到」响应。 + * 对于来自客户端的一般错误,你可以只使用 `400`。 +* `500` 及以上状态码用于服务器端错误。 你几乎永远不会直接使用它们。 当你的应用程序代码或服务器中的某些部分出现问题时,它将自动返回这些状态代码之一。 -!!! tip - To know more about each status code and which code is for what, check the MDN documentation about HTTP status codes. +!!! !!! tip + 要了解有关每个状态代码以及适用场景的更多信息,请查看 MDN 关于 HTTP 状态码的文档。 -## Shortcut to remember the names +## 记住名称的捷径 -Let's see the previous example again: +让我们再次看看之前的例子: ```Python hl_lines="6" {!../../../docs_src/response_status_code/tutorial001.py!} ``` -`201` is the status code for "Created". +`201` 是表示「已创建」的状态码。 -But you don't have to memorize what each of these codes mean. +但是你不必去记住每个代码的含义。 -You can use the convenience variables from `fastapi.status`. +你可以使用来自 `fastapi.status` 的便捷变量。 ```Python hl_lines="1 6" {!../../../docs_src/response_status_code/tutorial002.py!} ``` -They are just a convenience, they hold the same number, but that way you can use the editor's autocomplete to find them: +它们只是一种便捷方式,它们具有同样的数字代码,但是这样使用你就可以使用编辑器的自动补全功能来查找它们: -!!! note "Technical Details" - You could also use `from starlette import status`. +!!! !!! note "技术细节" + 你也可以使用 `from starlette import status`。 - **FastAPI** provides the same `starlette.status` as `fastapi.status` just as a convenience for you, the developer. But it comes directly from Starlette. + 为了给你(即开发者)提供方便,**FastAPI** 提供了与 `starlette.status` 完全相同的 `fastapi.status`。 但它直接来自于 Starlette。 -## Changing the default +## 更改默认状态码 -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. +稍后,在[高级用户指南](../advanced/response-change-status-code.md){.internal-link target=_blank}中你将了解如何返回与在此声明的默认状态码不同的状态码。 From 78743df4e56761da0401c9a6c73fb03a167236fe Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:55:00 +0800 Subject: [PATCH 154/163] New translations schema-extra-example.md (Chinese Simplified) --- docs/zh/docs/tutorial/schema-extra-example.md | 55 ++++++++++--------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/docs/zh/docs/tutorial/schema-extra-example.md b/docs/zh/docs/tutorial/schema-extra-example.md index 9b24aa3702e80..14abdac8624b7 100644 --- a/docs/zh/docs/tutorial/schema-extra-example.md +++ b/docs/zh/docs/tutorial/schema-extra-example.md @@ -23,7 +23,7 @@ You can declare `examples` for a Pydantic model that will be added to the genera === "Python 3.6+ Pydantic v2" ```Python hl_lines="15-26" - {!> ../../../docs_src/schema_extra_example/tutorial001.py!} + {!../../../docs_src/schema_extra_example/tutorial001.py!} ``` === "Python 3.6+ Pydantic v1" @@ -58,7 +58,7 @@ That extra info will be added as-is to the output **JSON Schema** for that model You can read more at the end of this page. -## `Field` additional arguments +## `Field` 的附加参数 When using `Field()` with Pydantic models, you can also declare additional `examples`: @@ -71,10 +71,10 @@ When using `Field()` with Pydantic models, you can also declare additional `exam === "Python 3.6+" ```Python hl_lines="4 10-13" - {!> ../../../docs_src/schema_extra_example/tutorial002.py!} + {!../../../docs_src/schema_extra_example/tutorial002.py!} ``` -## `examples` in OpenAPI +## 所以,虽然 `example` 不是JSON Schema的一部分,但它是OpenAPI的一部分,这将被文档UI使用。 When using any of: @@ -88,9 +88,9 @@ When using any of: you can also declare a group of `examples` with additional information that will be added to **OpenAPI**. -### `Body` with `examples` +### 关于 `example` 和 `examples`... -Here we pass `examples` containing one example of the data expected in `Body()`: +比如,你可以将请求体的一个 `example` 传递给 `Body`: === "Python 3.10+" @@ -101,15 +101,18 @@ Here we pass `examples` containing one example of the data expected in `Body()`: === "Python 3.9+" ```Python hl_lines="22-29" - {!> ../../../docs_src/schema_extra_example/tutorial003_an_py39.py!} + !!! warning + 请记住,传递的那些额外参数不会添加任何验证,只会添加注释,用于文档的目的。 ``` === "Python 3.6+" ```Python hl_lines="23-30" - {!> ../../../docs_src/schema_extra_example/tutorial003_an.py!} + Pydantic schema_extra ``` + + === "Python 3.10+ non-Annotated" !!! tip @@ -125,16 +128,18 @@ Here we pass `examples` containing one example of the data expected in `Body()`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="20-27" - {!> ../../../docs_src/schema_extra_example/tutorial003.py!} + 您可以使用 Configschema_extra 为Pydantic模型声明一个示例,如Pydantic 文档:定制 Schema 中所述: ``` + 和 schema_extra 为Pydantic模型声明一个示例,如Pydantic 文档:定制 Schema 中所述: + -### Example in the docs UI +### 模式的额外信息 - 例子 -With any of the methods above it would look like this in the `/docs`: +使用上面的任何方法,它在 `/docs` 中看起来都是这样的: -### `Body` with multiple `examples` +### `Body` 额外参数 You can of course also pass multiple `examples`: @@ -153,7 +158,7 @@ You can of course also pass multiple `examples`: === "Python 3.6+" ```Python hl_lines="24-39" - {!> ../../../docs_src/schema_extra_example/tutorial004_an.py!} + 同样的方法,你可以添加你自己的额外信息,这些信息将被添加到每个模型的JSON模式中,例如定制前端用户界面,等等。 ``` === "Python 3.10+ non-Annotated" @@ -162,7 +167,7 @@ You can of course also pass multiple `examples`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="19-34" - {!> ../../../docs_src/schema_extra_example/tutorial004_py310.py!} + 其他信息 ``` === "Python 3.6+ non-Annotated" @@ -171,16 +176,16 @@ You can of course also pass multiple `examples`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="21-36" - {!> ../../../docs_src/schema_extra_example/tutorial004.py!} + {!../../../docs_src/schema_extra_example/tutorial003.py!} ``` -### Examples in the docs UI +### 文档 UI 中的例子 -With `examples` added to `Body()` the `/docs` would look like: +你可以通过传递额外信息给 `Field` 同样的方式操作`Path`, `Query`, `Body`等。 -## Technical Details +## 技术细节 !!! tip If you are already using **FastAPI** version **0.99.0 or above**, you can probably **skip** these details. @@ -196,16 +201,16 @@ With `examples` added to `Body()` the `/docs` would look like: Before OpenAPI 3.1.0, OpenAPI used an older and modified version of **JSON Schema**. -JSON Schema didn't have `examples`, so OpenAPI added it's own `example` field to its own modified version. +在 `Field`, `Path`, `Query`, `Body` 和其他你之后将会看到的工厂函数,你可以为JSON 模式声明额外信息,你也可以通过给工厂函数传递其他的任意参数来给JSON 模式声明额外信息,比如增加 `example`: OpenAPI also added `example` and `examples` fields to other parts of the specification: -* `Parameter Object` (in the specification) that was used by FastAPI's: +* 所以 OpenAPI为了相似的目的定义了自己的 `example` (使用 `example`, 而不是 `examples`), 这也是文档 UI 所使用的 (使用 Swagger UI). * `Path()` * `Query()` * `Header()` * `Cookie()` -* `Request Body Object`, in the field `content`, on the `Media Type Object` (in the specification) that was used by FastAPI's: +* 您可以在JSON模式中定义额外的信息。 * `Body()` * `File()` * `Form()` @@ -216,18 +221,18 @@ The shape of this field `examples` from OpenAPI is a `dict` with **multiple exam The keys of the `dict` identify each example, and each value is another `dict`. -Each specific example `dict` in the `examples` can contain: +一个常见的用例是添加一个将在文档中显示的`example`。 * `summary`: Short description for the example. * `description`: A long description that can contain Markdown text. * `value`: This is the actual example shown, e.g. a `dict`. * `externalValue`: alternative to `value`, a URL pointing to the example. Although this might not be supported by as many tools as `value`. -This applies to those other parts of the OpenAPI specification apart from JSON Schema. +这些额外的信息将按原样添加到输出的JSON模式中。 -### JSON Schema's `examples` field +### 有几种方法可以声明额外的 JSON 模式信息。 -But then JSON Schema added an `examples` field to a new version of the specification. +JSON Schema在最新的一个版本中定义了一个字段 `examples` ,但是 OpenAPI 基于之前的一个旧版JSON Schema,并没有 `examples`. And then the new OpenAPI 3.1.0 was based on the latest version (JSON Schema 2020-12) that included this new field `examples`. From 7d821a7a8d244016e09459f296cd9613152886dd Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:55:01 +0800 Subject: [PATCH 155/163] New translations first-steps.md (Chinese Simplified) --- docs/zh/docs/tutorial/security/first-steps.md | 466 +++++++++++------- 1 file changed, 298 insertions(+), 168 deletions(-) diff --git a/docs/zh/docs/tutorial/security/first-steps.md b/docs/zh/docs/tutorial/security/first-steps.md index 25c20b8df21d6..32827f42aac1b 100644 --- a/docs/zh/docs/tutorial/security/first-steps.md +++ b/docs/zh/docs/tutorial/security/first-steps.md @@ -1,35 +1,35 @@ -# Security - First Steps +# 安全 - 第一步 -Let's imagine that you have your **backend** API in some domain. +假设**后端** API 在某个域。 -And you have a **frontend** in another domain or in a different path of the same domain (or in a mobile application). +**前端**在另一个域,或(移动应用中)在同一个域的不同路径下。 -And you want to have a way for the frontend to authenticate with the backend, using a **username** and **password**. +并且,前端要使用后端的 **username** 与 **password** 验证用户身份。 -We can use **OAuth2** to build that with **FastAPI**. +固然,**FastAPI** 支持 **OAuth2** 身份验证。 But let's save you the time of reading the full long specification just to find those little pieces of information you need. -Let's use the tools provided by **FastAPI** to handle security. +我们建议使用 **FastAPI** 的安全工具。 ## How it looks -Let's first just use the code and see how it works, and then we'll come back to understand what's happening. +首先,看看下面的代码是怎么运行的,然后再回过头来了解其背后的原理。 -## Create `main.py` +## 创建 `main.py` -Copy the example in a file `main.py`: +把下面的示例代码复制到 `main.py`: === "Python 3.9+" ```Python - {!> ../../../docs_src/security/tutorial001_an_py39.py!} + !!! tip "提示" ``` === "Python 3.6+" ```Python - {!> ../../../docs_src/security/tutorial001_an.py!} + 令牌只是用于验证用户的字符串 ``` === "Python 3.6+ non-Annotated" @@ -38,20 +38,22 @@ Copy the example in a file `main.py`: Prefer to use the `Annotated` version if possible. ```Python - {!> ../../../docs_src/security/tutorial001.py!} + {!../../../docs_src/security/tutorial001.py!} ``` -## Run it +## 运行 -!!! info - First install `python-multipart`. +!!! 打开 API 文档: http://127.0.0.1:8000/docs。

- E.g. `pip install python-multipart`. - - This is because **OAuth2** uses "form data" for sending the `username` and `password`. +
先安装 <a href="https://andrew-d.github.io/python-multipart/" class="external-link" target="_blank">`python-multipart`</a>。 安装命令: `pip install python-multipart`。
 
-Run the example with:
+这是因为 **OAuth2** 使用**表单数据**发送 `username` 与 `password`。
+
+ +

+ 用下面的命令运行该示例: +

@@ -63,172 +65,300 @@ $ uvicorn main:app --reload
-## Check it +

+ Check it +

-Go to the interactive docs at:
http://127.0.0.1:8000/docs. +

+ Go to the interactive docs at: http://127.0.0.1:8000/docs. +

-You will see something like this: +

+ You will see something like this: +

- +

+ +

-!!! check "Authorize button!" +

+ !!! check "Authorize button!" You already have a shiny new "Authorize" button. +

- And your *path operation* has a little lock in the top-right corner that you can click. +
And your *path operation* has a little lock in the top-right corner that you can click.
+
-And if you click it, you have a little authorization form to type a `username` and `password` (and other optional fields): +

+ 点击 Authorize 按钮,弹出授权表单,输入 usernamepassword 及其它可选字段: +

- +

+ +

-!!! note +

+ !!! note It doesn't matter what you type in the form, it won't work yet. But we'll get there. +

+ +

+ 虽然此文档不是给前端最终用户使用的,但这个自动工具非常实用,可在文档中与所有 API 交互。 +

+ +

+ 前端团队(可能就是开发者本人)可以使用本工具。 +

+ +

+ 第三方应用与系统也可以调用本工具。 +

+ +

+ 开发者也可以用它来调试、检查、测试应用。 +

+ +

+ 密码流 +

+ +

+ Now let's go back a bit and understand what is all that. +

+ +

+ Password 是 OAuth2 定义的,用于处理安全与身份验证的方式()。 +

+ +

+ OAuth2 的设计目标是为了让后端或 API 独立于服务器验证用户身份。 +

+ +

+ 但在本例中,FastAPI 应用会处理 API 与身份验证。 +

+ +

+ So, let's review it from that simplified point of view: +

+ + + +

+ FastAPIOAuth2PasswordBearer +

+ +

+ FastAPI 提供了不同抽象级别的安全工具。 +

+ +

+ 本例使用 OAuth2Password 流以及 Bearer 令牌(Token)。 为此要使用 OAuth2PasswordBearer 类。 +

+ +

+ !!! `Bearer` 令牌不是唯一的选择。 +

+ +
但它是最适合这个用例的方案。
+
+甚至可以说,它是适用于绝大多数用例的最佳方案,除非您是 OAuth2 的专家,知道为什么其它方案更合适。
+
+本例中,**FastAPI** 还提供了构建工具。
+
+ +

+ 创建 OAuth2PasswordBearer 的类实例时,要传递 tokenUrl 参数。 该参数包含客户端(用户浏览器中运行的前端) 的 URL,用于发送 usernamepassword,并获取令牌。 +

+ +

+ === "Python 3.9+" + +

    !!! check "Authorize 按钮!"
+
+

+ +

+ === "Python 3.6+" + +

    !!! info "说明"
+
+

+ +

+ === "Python 3.6+ non-Annotated" +

+ +
!!! tip
+    Prefer to use the `Annotated` version if possible.
+
+ +
    {!../../../docs_src/security/tutorial001.py!}
+
+ +

+ !!! 在此,`tokenUrl="token"` 指向的是暂未创建的相对 URL `token`。 这个相对 URL 相当于 `./token`。 +

+ +
因为使用的是相对 URL,如果 API 位于 `https://example.com/`,则指向 `https://example.com/token`。 但如果 API 位于 `https://example.com/api/v1/`,它指向的就是`https://example.com/api/v1/token`。
+
+使用相对 URL 非常重要,可以确保应用在遇到[使用代理](../../advanced/behind-a-proxy.md){.internal-link target=_blank}这样的高级用例时,也能正常运行。
+
+ +

+ 该参数不会创建端点或路径操作,但会声明客户端用来获取令牌的 URL /token 。 此信息用于 OpenAPI 及 API 文档。 +

+ +

+ 接下来,学习如何创建实际的路径操作。 +

+ +

+ !!! 严苛的 **Pythonista** 可能不喜欢用 `tokenUrl` 这种命名风格代替 `token_url`。 +

+ +
这种命名方式是因为要使用与 OpenAPI 规范中相同的名字。 以便在深入校验安全方案时,能通过复制粘贴查找更多相关信息。
+
+ +

+ oauth2_scheme 变量是 OAuth2PasswordBearer 的实例,也是可调用项。 +

+ +

+ It could be called as: +

+ +
oauth2_scheme(some, parameters)
+
+ +

+ 因此,Depends 可以调用 oauth2_scheme 变量。 +

+ +

+ 使用 +

+ +

+ 接下来,使用 Dependsoauth2_scheme 传入依赖项。 +

-This is of course not the frontend for the final users, but it's a great automatic tool to document interactively all your API. - -It can be used by the frontend team (that can also be yourself). - -It can be used by third party applications and systems. - -And it can also be used by yourself, to debug, check and test the same application. - -## The `password` flow - -Now let's go back a bit and understand what is all that. - -The `password` "flow" is one of the ways ("flows") defined in OAuth2, to handle security and authentication. - -OAuth2 was designed so that the backend or API could be independent of the server that authenticates the user. - -But in this case, the same **FastAPI** application will handle the API and the authentication. - -So, let's review it from that simplified point of view: - -* The user types the `username` and `password` in the frontend, and hits `Enter`. -* The frontend (running in the user's browser) sends that `username` and `password` to a specific URL in our API (declared with `tokenUrl="token"`). -* The API checks that `username` and `password`, and responds with a "token" (we haven't implemented any of this yet). - * A "token" is just a string with some content that we can use later to verify this user. - * Normally, a token is set to expire after some time. - * So, the user will have to log in again at some point later. - * And if the token is stolen, the risk is less. It is not like a permanent key that will work forever (in most of the cases). -* The frontend stores that token temporarily somewhere. -* The user clicks in the frontend to go to another section of the frontend web app. -* The frontend needs to fetch some more data from the API. - * But it needs authentication for that specific endpoint. - * So, to authenticate with our API, it sends a header `Authorization` with a value of `Bearer` plus the token. - * If the token contains `foobar`, the content of the `Authorization` header would be: `Bearer foobar`. - -## **FastAPI**'s `OAuth2PasswordBearer` - -**FastAPI** provides several tools, at different levels of abstraction, to implement these security features. - -In this example we are going to use **OAuth2**, with the **Password** flow, using a **Bearer** token. We do that using the `OAuth2PasswordBearer` class. - -!!! info - A "bearer" token is not the only option. - - But it's the best one for our use case. - - And it might be the best for most use cases, unless you are an OAuth2 expert and know exactly why there's another option that suits better your needs. - - In that case, **FastAPI** also provides you with the tools to build it. - -When we create an instance of the `OAuth2PasswordBearer` class we pass in the `tokenUrl` parameter. This parameter contains the URL that the client (the frontend running in the user's browser) will use to send the `username` and `password` in order to get a token. - -=== "Python 3.9+" - - ```Python hl_lines="8" - {!> ../../../docs_src/security/tutorial001_an_py39.py!} - ``` - -=== "Python 3.6+" - - ```Python hl_lines="7" - {!> ../../../docs_src/security/tutorial001_an.py!} - ``` - -=== "Python 3.6+ non-Annotated" - - !!! tip - Prefer to use the `Annotated` version if possible. - - ```Python hl_lines="6" - {!> ../../../docs_src/security/tutorial001.py!} - ``` - -!!! tip - Here `tokenUrl="token"` refers to a relative URL `token` that we haven't created yet. As it's a relative URL, it's equivalent to `./token`. - - Because we are using a relative URL, if your API was located at `https://example.com/`, then it would refer to `https://example.com/token`. But if your API was located at `https://example.com/api/v1/`, then it would refer to `https://example.com/api/v1/token`. - - Using a relative URL is important to make sure your application keeps working even in an advanced use case like [Behind a Proxy](../../advanced/behind-a-proxy.md){.internal-link target=_blank}. - -This parameter doesn't create that endpoint / *path operation*, but declares that the URL `/token` will be the one that the client should use to get the token. That information is used in OpenAPI, and then in the interactive API documentation systems. - -We will soon also create the actual path operation. - -!!! info - If you are a very strict "Pythonista" you might dislike the style of the parameter name `tokenUrl` instead of `token_url`. - - That's because it is using the same name as in the OpenAPI spec. So that if you need to investigate more about any of these security schemes you can just copy and paste it to find more information about it. - -The `oauth2_scheme` variable is an instance of `OAuth2PasswordBearer`, but it is also a "callable". - -It could be called as: - -```Python -oauth2_scheme(some, parameters) -``` - -So, it can be used with `Depends`. - -### Use it - -Now you can pass that `oauth2_scheme` in a dependency with `Depends`. - -=== "Python 3.9+" - - ```Python hl_lines="12" - {!> ../../../docs_src/security/tutorial001_an_py39.py!} - ``` - -=== "Python 3.6+" - - ```Python hl_lines="11" - {!> ../../../docs_src/security/tutorial001_an.py!} - ``` - -=== "Python 3.6+ non-Annotated" - - !!! tip - Prefer to use the `Annotated` version if possible. - - ```Python hl_lines="10" - {!> ../../../docs_src/security/tutorial001.py!} - ``` - -This dependency will provide a `str` that is assigned to the parameter `token` of the *path operation function*. - -**FastAPI** will know that it can use this dependency to define a "security scheme" in the OpenAPI schema (and the automatic API docs). - -!!! info "Technical Details" - **FastAPI** will know that it can use the class `OAuth2PasswordBearer` (declared in a dependency) to define the security scheme in OpenAPI because it inherits from `fastapi.security.oauth2.OAuth2`, which in turn inherits from `fastapi.security.base.SecurityBase`. - - All the security utilities that integrate with OpenAPI (and the automatic API docs) inherit from `SecurityBase`, that's how **FastAPI** can know how to integrate them in OpenAPI. +

+ === "Python 3.9+" + +

    !!! info "说明"
+
+

-## What it does +

+ === "Python 3.6+" + +

    !!! info "说明"
+
+

-It will go and look in the request for that `Authorization` header, check if the value is `Bearer` plus some token, and will return the token as a `str`. +

+ === "Python 3.6+ non-Annotated" +

-If it doesn't see an `Authorization` header, or the value doesn't have a `Bearer` token, it will respond with a 401 status code error (`UNAUTHORIZED`) directly. +
!!! tip
+    Prefer to use the `Annotated` version if possible.
+
+ +
    {!../../../docs_src/security/tutorial001.py!}
+
+ +

+ 该依赖项使用字符串(str)接收路径操作函数的参数 token 。 +

+ +

+ FastAPI 使用依赖项在 OpenAPI 概图(及 API 文档)中定义安全方案。 +

+ +

+ !!! info "Technical Details" + FastAPI will know that it can use the class OAuth2PasswordBearer (declared in a dependency) to define the security scheme in OpenAPI because it inherits from fastapi.security.oauth2.OAuth2, which in turn inherits from fastapi.security.base.SecurityBase. +

-You don't even have to check if the token exists to return an error. You can be sure that if your function is executed, it will have a `str` in that token. +
所有与 OpenAPI(及 API 文档)集成的安全工具都继承自 `SecurityBase`, 这就是为什么 **FastAPI** 能把它们集成至 OpenAPI 的原因。
+
-You can try it already in the interactive docs: +

+ What it does +

- +

+ FastAPI 校验请求中的 Authorization 请求头,核对请求头的值是不是由 Bearer + 令牌组成, 并返回令牌字符串(str)。 +

-We are not verifying the validity of the token yet, but that's a start already. +

+ 如果没有找到 Authorization 请求头,或请求头的值不是 Bearer + 令牌。 FastAPI 直接返回 401 错误状态码(UNAUTHORIZED)。 +

-## Recap +

+ You don't even have to check if the token exists to return an error. You can be sure that if your function is executed, it will have a str in that token. +

-So, in just 3 or 4 extra lines, you already have some primitive form of security. +

+ You can try it already in the interactive docs: +

+ +

+ +

+ +

+ We are not verifying the validity of the token yet, but that's a start already. +

+ +

+ Recap +

+ +

+ 看到了吧,只要多写三四行代码,就可以添加基础的安全表单。 +

From aaa854a694ed073357b39622c5b2a2460b326e41 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:55:02 +0800 Subject: [PATCH 156/163] New translations get-current-user.md (Chinese Simplified) --- .../tutorial/security/get-current-user.md | 102 +++++++++--------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/docs/zh/docs/tutorial/security/get-current-user.md b/docs/zh/docs/tutorial/security/get-current-user.md index 1a8c5d9a8d8fd..c503a34059ba5 100644 --- a/docs/zh/docs/tutorial/security/get-current-user.md +++ b/docs/zh/docs/tutorial/security/get-current-user.md @@ -1,17 +1,18 @@ -# Get Current User +# 获取当前用户 -In the previous chapter the security system (which is based on the dependency injection system) was giving the *path operation function* a `token` as a `str`: +在上一章节中,(基于依赖项注入系统的)安全系统向*路径操作函数*提供了一个 `str` 类型的 `token`: === "Python 3.9+" ```Python hl_lines="12" - {!> ../../../docs_src/security/tutorial001_an_py39.py!} + 这些内容在下一章节。 ``` === "Python 3.6+" ```Python hl_lines="11" - {!> ../../../docs_src/security/tutorial001_an.py!} + !!! tip + 你可能还记得请求体也是使用 Pydantic 模型来声明的。 ``` === "Python 3.6+ non-Annotated" @@ -20,18 +21,18 @@ In the previous chapter the security system (which is based on the dependency in Prefer to use the `Annotated` version if possible. ```Python hl_lines="10" - {!> ../../../docs_src/security/tutorial001.py!} + {!../../../docs_src/security/tutorial002.py!} ``` -But that is still not that useful. +但这还不是很实用。 -Let's make it give us the current user. +让我们来使它返回当前用户给我们。 -## Create a user model +## 创建一个用户模型 -First, let's create a Pydantic user model. +首先,让我们来创建一个用户 Pydantic 模型。 -The same way we use Pydantic to declare bodies, we can use it anywhere else: +与使用 Pydantic 声明请求体的方式相同,我们可以在其他任何地方使用它: === "Python 3.10+" @@ -57,7 +58,7 @@ The same way we use Pydantic to declare bodies, we can use it anywhere else: Prefer to use the `Annotated` version if possible. ```Python hl_lines="3 10-14" - {!> ../../../docs_src/security/tutorial002_py310.py!} + {!../../../docs_src/security/tutorial002.py!} ``` === "Python 3.6+ non-Annotated" @@ -66,18 +67,18 @@ The same way we use Pydantic to declare bodies, we can use it anywhere else: Prefer to use the `Annotated` version if possible. ```Python hl_lines="5 12-16" - {!> ../../../docs_src/security/tutorial002.py!} + {!../../../docs_src/security/tutorial001.py!} ``` -## Create a `get_current_user` dependency +## 创建一个 `get_current_user` 依赖项 -Let's create a dependency `get_current_user`. +让我们来创建一个 `get_current_user` 依赖项。 -Remember that dependencies can have sub-dependencies? +还记得依赖项可以有子依赖项吗? -`get_current_user` will have a dependency with the same `oauth2_scheme` we created before. +`get_current_user` 将具有一个我们之前所创建的同一个 `oauth2_scheme` 作为依赖项。 -The same as we were doing before in the *path operation* directly, our new dependency `get_current_user` will receive a `token` as a `str` from the sub-dependency `oauth2_scheme`: +与我们之前直接在路径操作中所做的相同,我们新的依赖项 `get_current_user` 将从子依赖项 `oauth2_scheme` 中接收一个 `str` 类型的 `token`: === "Python 3.10+" @@ -103,7 +104,7 @@ The same as we were doing before in the *path operation* directly, our new depen Prefer to use the `Annotated` version if possible. ```Python hl_lines="23" - {!> ../../../docs_src/security/tutorial002_py310.py!} + {!../../../docs_src/security/tutorial002.py!} ``` === "Python 3.6+ non-Annotated" @@ -112,12 +113,12 @@ The same as we were doing before in the *path operation* directly, our new depen Prefer to use the `Annotated` version if possible. ```Python hl_lines="25" - {!> ../../../docs_src/security/tutorial002.py!} + {!../../../docs_src/security/tutorial002.py!} ``` -## Get the user +## 获取用户 -`get_current_user` will use a (fake) utility function we created, that takes a token as a `str` and returns our Pydantic `User` model: +`get_current_user` 将使用我们创建的(伪)工具函数,该函数接收 `str` 类型的令牌并返回我们的 Pydantic `User` 模型: === "Python 3.10+" @@ -155,9 +156,9 @@ The same as we were doing before in the *path operation* directly, our new depen {!> ../../../docs_src/security/tutorial002.py!} ``` -## Inject the current user +## 注入当前用户 -So now we can use the same `Depends` with our `get_current_user` in the *path operation*: +因此现在我们可以在*路径操作*中使用 `get_current_user` 作为 `Depends` 了: === "Python 3.10+" @@ -174,8 +175,11 @@ So now we can use the same `Depends` with our `get_current_user` in the *path op === "Python 3.6+" ```Python hl_lines="32" - {!> ../../../docs_src/security/tutorial002_an.py!} + !!! check + 这种依赖系统的设计方式使我们可以拥有不同的依赖项(不同的「可依赖类型」),并且它们都返回一个 User 模型。 ``` + 模型。 + === "Python 3.10+ non-Annotated" @@ -195,56 +199,56 @@ So now we can use the same `Depends` with our `get_current_user` in the *path op {!> ../../../docs_src/security/tutorial002.py!} ``` -Notice that we declare the type of `current_user` as the Pydantic model `User`. +注意我们将 `current_user` 的类型声明为 Pydantic 模型 `User`。 -This will help us inside of the function with all the completion and type checks. +这将帮助我们在函数内部使用所有的代码补全和类型检查。 !!! tip You might remember that request bodies are also declared with Pydantic models. - Here **FastAPI** won't get confused because you are using `Depends`. + 在这里 **FastAPI** 不会搞混,因为你正在使用的是 `Depends`。 !!! check The way this dependency system is designed allows us to have different dependencies (different "dependables") that all return a `User` model. - We are not restricted to having only one dependency that can return that type of data. + 我们并未被局限于只能有一个返回该类型数据的依赖项。 -## Other models +## 其他模型 -You can now get the current user directly in the *path operation functions* and deal with the security mechanisms at the **Dependency Injection** level, using `Depends`. +现在你可以直接在*路径操作函数*中获取当前用户,并使用 `Depends` 在**依赖注入**级别处理安全性机制。 -And you can use any model or data for the security requirements (in this case, a Pydantic model `User`). +你可以使用任何模型或数据来满足安全性要求(在这个示例中,使用的是 Pydantic 模型 `User`)。 -But you are not restricted to using some specific data model, class or type. +但是你并未被限制只能使用某些特定的数据模型,类或类型。 -Do you want to have an `id` and `email` and not have any `username` in your model? Sure. You can use these same tools. +你想要在模型中使用 `id` 和 `email` 而不使用任何的 `username`? 当然可以。 你可以同样地使用这些工具。 -Do you want to just have a `str`? Or just a `dict`? Or a database class model instance directly? It all works the same way. +你只想要一个 `str`? 或者仅仅一个 `dict`? 还是直接一个数据库模型类的实例? 它们的工作方式都是一样的。 -You actually don't have users that log in to your application but robots, bots, or other systems, that have just an access token? Again, it all works the same. +实际上你没有用户登录到你的应用程序,而是只拥有访问令牌的机器人,程序或其他系统? 再一次,它们的工作方式也是一样的。 -Just use any kind of model, any kind of class, any kind of database that you need for your application. **FastAPI** has you covered with the dependency injection system. +尽管去使用你的应用程序所需要的任何模型,任何类,任何数据库。 **FastAPI** 通过依赖项注入系统都帮你搞定。 -## Code size +## 代码体积 -This example might seem verbose. Have in mind that we are mixing security, data models, utility functions and *path operations* in the same file. +这个示例似乎看起来很冗长。 考虑到我们在同一文件中混合了安全性,数据模型工具函数和路径操作等代码。 -But here's the key point. +但关键的是。 -The security and dependency injection stuff is written once. +安全性和依赖项注入内容只需要编写一次。 -And you can make it as complex as you want. And still, have it written only once, in a single place. With all the flexibility. +你可以根据需要使其变得很复杂。 而且只需要在一个地方写一次。 但仍然具备所有的灵活性。 -But you can have thousands of endpoints (*path operations*) using the same security system. +但是,你可以有无数个使用同一安全系统的端点(*路径操作*)。 -And all of them (or any portion of them that you want) can take the advantage of re-using these dependencies or any other dependencies you create. +所有(或所需的任何部分)的端点,都可以利用对这些或你创建的其他依赖项进行复用所带来的优势。 -And all these thousands of *path operations* can be as small as 3 lines: +所有的这无数个*路径操作*甚至可以小到只需 3 行代码: === "Python 3.10+" ```Python hl_lines="30-32" - {!> ../../../docs_src/security/tutorial002_an_py310.py!} + 总结 ``` === "Python 3.9+" @@ -274,15 +278,15 @@ And all these thousands of *path operations* can be as small as 3 lines: Prefer to use the `Annotated` version if possible. ```Python hl_lines="30-32" - {!> ../../../docs_src/security/tutorial002.py!} + {!../../../docs_src/security/tutorial002.py!} ``` ## Recap -You can now get the current user directly in your *path operation function*. +现在你可以直接在*路径操作函数*中获取当前用户。 -We are already halfway there. +我们已经进行到一半了。 -We just need to add a *path operation* for the user/client to actually send the `username` and `password`. +我们只需要再为用户/客户端添加一个真正发送 `username` 和 `password` 的*路径操作*。 That comes next. From fbb2a997f479a6b0565f3ed58ec11c264ea17d97 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:55:04 +0800 Subject: [PATCH 157/163] New translations index.md (Chinese Simplified) --- docs/zh/docs/tutorial/security/index.md | 100 ++++++++++++------------ 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/docs/zh/docs/tutorial/security/index.md b/docs/zh/docs/tutorial/security/index.md index 9679cda427d6a..b4b26eb0e49a9 100644 --- a/docs/zh/docs/tutorial/security/index.md +++ b/docs/zh/docs/tutorial/security/index.md @@ -1,101 +1,101 @@ -# Security +# 安全性 -There are many ways to handle security, authentication and authorization. +有许多方法可以处理安全性、身份认证和授权等问题。 -And it normally is a complex and "difficult" topic. +而且这通常是一个复杂而「困难」的话题。 -In many frameworks and systems just handling security and authentication takes a big amount of effort and code (in many cases it can be 50% or more of all the code written). +在许多框架和系统中,仅处理安全性和身份认证就会花费大量的精力和代码(在许多情况下,可能占编写的所有代码的 50% 或更多)。 -**FastAPI** provides several tools to help you deal with **Security** easily, rapidly, in a standard way, without having to study and learn all the security specifications. +**FastAPI** 提供了多种工具,可帮助你以标准的方式轻松、快速地处理**安全性**,而无需研究和学习所有的安全规范。 -But first, let's check some small concepts. +但首先,让我们来看一些小的概念。 ## In a hurry? -If you don't care about any of these terms and you just need to add security with authentication based on username and password *right now*, skip to the next chapters. +如果你不关心这些术语,而只需要*立即*通过基于用户名和密码的身份认证来增加安全性,请跳转到下一章。 ## OAuth2 -OAuth2 is a specification that defines several ways to handle authentication and authorization. +OAuth2是一个规范,它定义了几种处理身份认证和授权的方法。 -It is quite an extensive specification and covers several complex use cases. +它是一个相当广泛的规范,涵盖了一些复杂的使用场景。 -It includes ways to authenticate using a "third party". +它包括了使用「第三方」进行身份认证的方法。 -That's what all the systems with "login with Facebook, Google, Twitter, GitHub" use underneath. +这就是所有带有「使用 Facebook,Google,Twitter,GitHub 登录」的系统背后所使用的机制。 ### OAuth 1 -There was an OAuth 1, which is very different from OAuth2, and more complex, as it included direct specifications on how to encrypt the communication. +有一个 OAuth 1,它与 OAuth2 完全不同,并且更为复杂,因为它直接包含了有关如何加密通信的规范。 -It is not very popular or used nowadays. +如今它已经不是很流行,没有被广泛使用了。 -OAuth2 doesn't specify how to encrypt the communication, it expects you to have your application served with HTTPS. +OAuth2 没有指定如何加密通信,它期望你为应用程序使用 HTTPS 进行通信。 -!!! tip - In the section about **deployment** you will see how to set up HTTPS for free, using Traefik and Let's Encrypt. +!!! !!! tip + 在有关**部署**的章节中,你将了解如何使用 Traefik 和 Let's Encrypt 免费设置 HTTPS。 ## OpenID Connect -OpenID Connect is another specification, based on **OAuth2**. +OpenID Connect 是另一个基于 **OAuth2** 的规范。 -It just extends OAuth2 specifying some things that are relatively ambiguous in OAuth2, to try to make it more interoperable. +它只是扩展了 OAuth2,并明确了一些在 OAuth2 中相对模糊的内容,以尝试使其更具互操作性。 -For example, Google login uses OpenID Connect (which underneath uses OAuth2). +例如,Google 登录使用 OpenID Connect(底层使用OAuth2)。 -But Facebook login doesn't support OpenID Connect. It has its own flavor of OAuth2. +但是 Facebook 登录不支持 OpenID Connect。 它具有自己的 OAuth2 风格。 -### OpenID (not "OpenID Connect") +### OpenID(非「OpenID Connect」) -There was also an "OpenID" specification. That tried to solve the same thing as **OpenID Connect**, but was not based on OAuth2. +还有一个「OpenID」规范。 它试图解决与 **OpenID Connect** 相同的问题,但它不是基于 OAuth2。 -So, it was a complete additional system. +因此,它是一个完整的附加系统。 -It is not very popular or used nowadays. +如今它已经不是很流行,没有被广泛使用了。 ## OpenAPI -OpenAPI (previously known as Swagger) is the open specification for building APIs (now part of the Linux Foundation). +OpenAPI(以前称为 Swagger)是用于构建 API 的开放规范(现已成为 Linux Foundation 的一部分)。 -**FastAPI** is based on **OpenAPI**. +**FastAPI** 基于 **OpenAPI**。 -That's what makes it possible to have multiple automatic interactive documentation interfaces, code generation, etc. +这就是使多个自动交互式文档界面,代码生成等成为可能的原因。 -OpenAPI has a way to define multiple security "schemes". +OpenAPI 有一种定义多个安全「方案」的方法。 -By using them, you can take advantage of all these standard-based tools, including these interactive documentation systems. +通过使用它们,你可以利用所有这些基于标准的工具,包括这些交互式文档系统。 -OpenAPI defines the following security schemes: +OpenAPI 定义了以下安全方案: -* `apiKey`: an application specific key that can come from: - * A query parameter. +* `apiKey`:一个特定于应用程序的密钥,可以来自: + * 查询参数。 * A header. - * A cookie. -* `http`: standard HTTP authentication systems, including: - * `bearer`: a header `Authorization` with a value of `Bearer` plus a token. This is inherited from OAuth2. - * HTTP Basic authentication. - * HTTP Digest, etc. -* `oauth2`: all the OAuth2 ways to handle security (called "flows"). - * Several of these flows are appropriate for building an OAuth 2.0 authentication provider (like Google, Facebook, Twitter, GitHub, etc): + * cookie。 +* `http`:标准的 HTTP 身份认证系统,包括: + * `bearer`: 一个值为 `Bearer` 加令牌字符串的 `Authorization` 请求头。 这是从 OAuth2 继承的。 + * HTTP Basic 认证方式。 + * HTTP Digest,等等。 +* `oauth2`:所有的 OAuth2 处理安全性的方式(称为「流程」)。 + * *以下几种流程适合构建 OAuth 2.0 身份认证的提供者(例如 Google,Facebook,Twitter,GitHub 等): * `implicit` * `clientCredentials` * `authorizationCode` * `implicit` * `clientCredentials` * `authorizationCode` - * But there is one specific "flow" that can be perfectly used for handling authentication in the same application directly: - * `password`: some next chapters will cover examples of this. -* `openIdConnect`: has a way to define how to discover OAuth2 authentication data automatically. - * This automatic discovery is what is defined in the OpenID Connect specification. + * 但是有一个特定的「流程」可以完美地用于直接在同一应用程序中处理身份认证: + * `password`:接下来的几章将介绍它的示例。 +* `openIdConnect`:提供了一种定义如何自动发现 OAuth2 身份认证数据的方法。 + * 此自动发现机制是 OpenID Connect 规范中定义的内容。 -!!! tip - Integrating other authentication/authorization providers like Google, Facebook, Twitter, GitHub, etc. is also possible and relatively easy. +!!! !!! tip + 集成其他身份认证/授权提供者(例如Google,Facebook,Twitter,GitHub等)也是可能的,而且较为容易。 - The most complex problem is building an authentication/authorization provider like those, but **FastAPI** gives you the tools to do it easily, while doing the heavy lifting for you. + 最复杂的问题是创建一个像这样的身份认证/授权提供程序,但是 **FastAPI** 为你提供了轻松完成任务的工具,同时为你解决了重活。 -## **FastAPI** utilities +## **FastAPI** 实用工具 -FastAPI provides several tools for each of these security schemes in the `fastapi.security` module that simplify using these security mechanisms. +FastAPI 在 `fastapi.security` 模块中为每个安全方案提供了几种工具,这些工具简化了这些安全机制的使用方法。 -In the next chapters you will see how to add security to your API using those tools provided by **FastAPI**. +在下一章中,你将看到如何使用 **FastAPI** 所提供的这些工具为你的 API 增加安全性。 -And you will also see how it gets automatically integrated into the interactive documentation system. +而且你还将看到它如何自动地被集成到交互式文档系统中。 From 52e84d4274174d24125bf94d3d151c186024a7d3 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:55:05 +0800 Subject: [PATCH 158/163] New translations oauth2-jwt.md (Chinese Simplified) --- docs/zh/docs/tutorial/security/oauth2-jwt.md | 201 +++++++++---------- 1 file changed, 100 insertions(+), 101 deletions(-) diff --git a/docs/zh/docs/tutorial/security/oauth2-jwt.md b/docs/zh/docs/tutorial/security/oauth2-jwt.md index b663abcb7b696..7e126a75d93d3 100644 --- a/docs/zh/docs/tutorial/security/oauth2-jwt.md +++ b/docs/zh/docs/tutorial/security/oauth2-jwt.md @@ -1,16 +1,16 @@ -# OAuth2 with Password (and hashing), Bearer with JWT tokens +# OAuth2 实现密码哈希与 Bearer JWT 令牌验证 -Now that we have all the security flow, let's make the application actually secure, using JWT tokens and secure password hashing. +至此,我们已经编写了所有安全流,本章学习如何使用 JWT 令牌(Token)和安全密码哈希(Hash)实现真正的安全机制。 -This code is something you can actually use in your application, save the password hashes in your database, etc. +本章的示例代码真正实现了在应用的数据库中保存哈希密码等功能。 We are going to start from where we left in the previous chapter and increment it. -## About JWT +## JWT 简介 -JWT means "JSON Web Tokens". +JWT 即**JSON 网络令牌**(JSON Web Tokens)。 -It's a standard to codify a JSON object in a long dense string without spaces. It looks like this: +JWT 是一种将 JSON 对象编码为没有空格,且难以理解的长字符串的标准。 It looks like this: ``` eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c @@ -20,111 +20,108 @@ It is not encrypted, so, anyone could recover the information from the contents. But it's signed. So, when you receive a token that you emitted, you can verify that you actually emitted it. -That way, you can create a token with an expiration of, let's say, 1 week. And then when the user comes back the next day with the token, you know that user is still logged in to your system. +使用 JWT 创建有效期为一周的令牌。 第二天,用户持令牌再次访问时,仍为登录状态。 -After a week, the token will be expired and the user will not be authorized and will have to sign in again to get a new token. And if the user (or a third party) tried to modify the token to change the expiration, you would be able to discover it, because the signatures would not match. +After a week, the token will be expired and the user will not be authorized and will have to sign in again to get a new token. 如果用户(或第三方)篡改令牌的过期时间,因为签名不匹配会导致身份验证失败。 -If you want to play with JWT tokens and see how they work, check https://jwt.io. +如需深入了解 JWT 令牌,了解它的工作方式,请参阅 https://jwt.io。 -## Install `python-jose` +## 安装 `python-jose` -We need to install `python-jose` to generate and verify the JWT tokens in Python: +安装 `python-jose`,在 Python 中生成和校验 JWT 令牌:
```console -$ pip install "python-jose[cryptography]" +$ pip install python-jose[cryptography] ---> 100% ```
-Python-jose requires a cryptographic backend as an extra. +Python-jose 需要安装配套的加密后端。 -Here we are using the recommended one: pyca/cryptography. +本教程推荐的后端是:pyca/cryptography。 -!!! tip - This tutorial previously used PyJWT. +!!! 本教程以前使用 <a href="https://pyjwt.readthedocs.io/" class="external-link" target="_blank">PyJWT</a>。 - But it was updated to use Python-jose instead as it provides all the features from PyJWT plus some extras that you might need later when building integrations with other tools. + 但后来换成了 Python-jose,因为 Python-jose 支持 PyJWT 的所有功能,还支持与其它工具集成时可能会用到的一些其它功能。 -## Password hashing +## 密码哈希 "Hashing" means converting some content (a password in this case) into a sequence of bytes (just a string) that looks like gibberish. -Whenever you pass exactly the same content (exactly the same password) you get exactly the same gibberish. +每次传入完全相同的内容时(比如,完全相同的密码),返回的都是完全相同的乱码。 -But you cannot convert from the gibberish back to the password. +但这个乱码无法转换回传入的密码。 -### Why use password hashing +### 为什么使用密码哈希 If your database is stolen, the thief won't have your users' plaintext passwords, only the hashes. -So, the thief won't be able to try to use that password in another system (as many users use the same password everywhere, this would be dangerous). +这样一来,窃贼就无法在其它应用中使用窃取的密码,要知道,很多用户在所有系统中都使用相同的密码,风险超大)。 -## Install `passlib` +## 安装 `passlib` -PassLib is a great Python package to handle password hashes. +Passlib 是处理密码哈希的 Python 包。 -It supports many secure hashing algorithms and utilities to work with them. +它支持很多安全哈希算法及配套工具。 The recommended algorithm is "Bcrypt". -So, install PassLib with Bcrypt: +因此,请先安装附带 Bcrypt 的 PassLib:
```console -$ pip install "passlib[bcrypt]" +$ pip install passlib[bcrypt] ---> 100% ```
-!!! tip - With `passlib`, you could even configure it to be able to read passwords created by **Django**, a **Flask** security plug-in or many others. +!!! `passlib` 甚至可以读取 Django、Flask 的安全插件等工具创建的密码。 - So, you would be able to, for example, share the same data from a Django application in a database with a FastAPI application. Or gradually migrate a Django application using the same database. + 例如,把 Django 应用的数据共享给 FastAPI 应用的数据库。 或利用同一个数据库,可以逐步把应用从 Django 迁移到 FastAPI。 - And your users would be able to login from your Django app or from your **FastAPI** app, at the same time. + 并且,用户可以同时从 Django 应用或 FastAPI 应用登录。 -## Hash and verify the passwords +## 密码哈希与校验 -Import the tools we need from `passlib`. +从 `passlib` 导入所需工具。 Create a PassLib "context". This is what will be used to hash and verify passwords. -!!! tip - The PassLib context also has functionality to use different hashing algorithms, including deprecated old ones only to allow verifying them, etc. +!!! PassLib 上下文还支持使用不同哈希算法的功能,包括只能校验的已弃用旧算法等。 - For example, you could use it to read and verify passwords generated by another system (like Django) but hash any new passwords with a different algorithm like Bcrypt. + 例如,用它读取和校验其它系统(如 Django)生成的密码,但要使用其它算法,如 Bcrypt,生成新的哈希密码。 - And be compatible with all of them at the same time. + 同时,这些功能都是兼容的。 -Create a utility function to hash a password coming from the user. +接下来,创建三个工具函数,其中一个函数用于哈希用户的密码。 And another utility to verify if a received password matches the hash stored. -And another one to authenticate and return a user. +第三个函数用于身份验证,并返回用户。 === "Python 3.10+" ```Python hl_lines="7 48 55-56 59-60 69-75" - {!> ../../../docs_src/security/tutorial004_an_py310.py!} + !!! tip "提示" ``` === "Python 3.9+" ```Python hl_lines="7 48 55-56 59-60 69-75" - {!> ../../../docs_src/security/tutorial004_an_py39.py!} + 接下来,我们紧接上一章,继续完善安全机制。 ``` === "Python 3.6+" ```Python hl_lines="7 49 56-57 60-61 70-76" - {!> ../../../docs_src/security/tutorial004_an.py!} + 而且,FastAPI 还提供了一些工具,在不影响灵活、稳定和安全的前提下,尽可能地简化安全机制。 ``` === "Python 3.10+ non-Annotated" @@ -133,7 +130,7 @@ And another one to authenticate and return a user. Prefer to use the `Annotated` version if possible. ```Python hl_lines="6 47 54-55 58-59 68-74" - {!> ../../../docs_src/security/tutorial004_py310.py!} + 原因很简单,假如数据库被盗,窃贼无法获取用户的明文密码,得到的只是哈希值。 ``` === "Python 3.6+ non-Annotated" @@ -142,19 +139,19 @@ And another one to authenticate and return a user. Prefer to use the `Annotated` version if possible. ```Python hl_lines="7 48 55-56 59-60 69-75" - {!> ../../../docs_src/security/tutorial004.py!} + {!../../../docs_src/security/tutorial004.py!} ``` !!! note If you check the new (fake) database `fake_users_db`, you will see how the hashed password looks like now: `"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"`. -## Handle JWT tokens +## 处理 JWT 令牌 -Import the modules installed. +导入已安装的模块。 -Create a random secret key that will be used to sign the JWT tokens. +创建用于 JWT 令牌签名的随机密钥。 -To generate a secure random secret key use the command: +使用以下命令,生成安全的随机密钥:
@@ -166,32 +163,32 @@ $ openssl rand -hex 32
-And copy the output to the variable `SECRET_KEY` (don't use the one in the example). +然后,把生成的密钥复制到变量**SECRET_KEY**,注意,不要使用本例所示的密钥。 -Create a variable `ALGORITHM` with the algorithm used to sign the JWT token and set it to `"HS256"`. +创建指定 JWT 令牌签名算法的变量 **ALGORITHM**,本例中的值为 `"HS256"`。 -Create a variable for the expiration of the token. +创建设置令牌过期时间的变量。 -Define a Pydantic Model that will be used in the token endpoint for the response. +定义令牌端点响应的 Pydantic 模型。 -Create a utility function to generate a new access token. +创建生成新的访问令牌的工具函数。 === "Python 3.10+" ```Python hl_lines="6 12-14 28-30 78-86" - {!> ../../../docs_src/security/tutorial004_an_py310.py!} + 注意,代码中没有明文密码**`secret`**,只保存了它的哈希值。 ``` === "Python 3.9+" ```Python hl_lines="6 12-14 28-30 78-86" - {!> ../../../docs_src/security/tutorial004_an_py39.py!} + FastAPI 还支持以相对简单的方式,使用 OAuth2 等安全、标准的协议。 ``` === "Python 3.6+" ```Python hl_lines="6 13-15 29-31 79-87" - {!> ../../../docs_src/security/tutorial004_an.py!} + 注意,请求中 `Authorization` 响应头的值以 `Bearer` 开头。 ``` === "Python 3.10+ non-Annotated" @@ -200,7 +197,7 @@ Create a utility function to generate a new access token. Prefer to use the `Annotated` version if possible. ```Python hl_lines="5 11-13 27-29 77-85" - {!> ../../../docs_src/security/tutorial004_py310.py!} + 第一个函数用于校验接收的密码是否匹配存储的哈希值。 ``` === "Python 3.6+ non-Annotated" @@ -209,33 +206,33 @@ Create a utility function to generate a new access token. Prefer to use the `Annotated` version if possible. ```Python hl_lines="6 12-14 28-30 78-86" - {!> ../../../docs_src/security/tutorial004.py!} + {!../../../docs_src/security/tutorial004.py!} ``` -## Update the dependencies +## 更新依赖项 -Update `get_current_user` to receive the same token as before, but this time, using JWT tokens. +更新 `get_current_user` 以接收与之前相同的令牌,但这里用的是 JWT 令牌。 -Decode the received token, verify it, and return the current user. +解码并校验接收到的令牌,然后,返回当前用户。 -If the token is invalid, return an HTTP error right away. +如果令牌无效,则直接返回 HTTP 错误。 === "Python 3.10+" ```Python hl_lines="89-106" - {!> ../../../docs_src/security/tutorial004_an_py310.py!} + !!! tip "提示" ``` === "Python 3.9+" ```Python hl_lines="89-106" - {!> ../../../docs_src/security/tutorial004_an_py39.py!} + 本教程推荐的算法是 Bcrypt。 ``` === "Python 3.6+" ```Python hl_lines="90-107" - {!> ../../../docs_src/security/tutorial004_an.py!} + 开发者可以灵活选择最适合项目的安全机制。 ``` === "Python 3.10+ non-Annotated" @@ -244,7 +241,7 @@ If the token is invalid, return an HTTP error right away. Prefer to use the `Annotated` version if possible. ```Python hl_lines="88-105" - {!> ../../../docs_src/security/tutorial004_py310.py!} + 创建用于密码哈希和身份校验的 PassLib 上下文。 ``` === "Python 3.6+ non-Annotated" @@ -253,32 +250,34 @@ If the token is invalid, return an HTTP error right away. Prefer to use the `Annotated` version if possible. ```Python hl_lines="89-106" - {!> ../../../docs_src/security/tutorial004.py!} + {!../../../docs_src/security/tutorial004.py!} ``` -## Update the `/token` *path operation* +## 更新 `/token` *路径操作* -Create a `timedelta` with the expiration time of the token. +用令牌过期时间创建 `timedelta` 对象。 -Create a real JWT access token and return it +创建并返回真正的 JWT 访问令牌。 === "Python 3.10+" ```Python hl_lines="117-132" - {!> ../../../docs_src/security/tutorial004_an_py310.py!} + !!! tip "提示" ``` === "Python 3.9+" ```Python hl_lines="117-132" - {!> ../../../docs_src/security/tutorial004_an_py39.py!} + !!! check "检查" ``` === "Python 3.6+" ```Python hl_lines="118-133" - {!> ../../../docs_src/security/tutorial004_an.py!} + OAuth2 支持scopes(作用域)。 ``` +(作用域)。 + === "Python 3.10+ non-Annotated" @@ -286,7 +285,7 @@ Create a real JWT access token and return it Prefer to use the `Annotated` version if possible. ```Python hl_lines="114-127" - {!> ../../../docs_src/security/tutorial004_py310.py!} + 查看新的(伪)数据库 `fake_users_db`,就能看到哈希后的密码:`"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"`。 ``` === "Python 3.6+ non-Annotated" @@ -295,51 +294,51 @@ Create a real JWT access token and return it Prefer to use the `Annotated` version if possible. ```Python hl_lines="115-128" - {!> ../../../docs_src/security/tutorial004.py!} + {!../../../docs_src/security/tutorial004.py!} ``` -### Technical details about the JWT "subject" `sub` +### JWT `sub` 的技术细节 -The JWT specification says that there's a key `sub`, with the subject of the token. +JWT 规范还包括 `sub` 键,值是令牌的主题。 -It's optional to use it, but that's where you would put the user's identification, so we are using it here. +该键是可选的,但要把用户标识放在这个键里,所以本例使用了该键。 -JWT might be used for other things apart from identifying a user and allowing them to perform operations directly on your API. +除了识别用户与许可用户在 API 上直接执行操作之外,JWT 还可能用于其它事情。 For example, you could identify a "car" or a "blog post". Then you could add permissions about that entity, like "drive" (for the car) or "edit" (for the blog). -And then, you could give that JWT token to a user (or bot), and they could use it to perform those actions (drive the car, or edit the blog post) without even needing to have an account, just with the JWT token your API generated for that. +JWT 字符串没有加密,任何人都能用它恢复原始信息。 -Using these ideas, JWT can be used for way more sophisticated scenarios. +同理,JWT 可以用于更复杂的场景。 -In those cases, several of those entities could have the same ID, let's say `foo` (a user `foo`, a car `foo`, and a blog post `foo`). +在这些情况下,多个实体的 ID 可能是相同的,以 ID `foo` 为例,用户的 ID 是 `foo`,车的 ID 是 `foo`,博客的 ID 也是 `foo`。 -So, to avoid ID collisions, when creating the JWT token for the user, you could prefix the value of the `sub` key, e.g. with `username:`. So, in this example, the value of `sub` could have been: `username:johndoe`. +为了避免 ID 冲突,在给用户创建 JWT 令牌时,可以为 `sub` 键的值加上前缀,例如 `username:`。 因此,在本例中,`sub` 的值可以是:`username:johndoe`。 -The important thing to have in mind is that the `sub` key should have a unique identifier across the entire application, and it should be a string. +注意,划重点,`sub` 键在整个应用中应该只有一个唯一的标识符,而且应该是字符串。 -## Check it +## 检查 -Run the server and go to the docs: http://127.0.0.1:8000/docs. +运行服务器并访问文档: http://127.0.0.1:8000/docs。 -You'll see the user interface like: +可以看到如下用户界面: -Authorize the application the same way as before. +用与上一章同样的方式实现应用授权。 -Using the credentials: +使用如下凭证: -Username: `johndoe` Password: `secret` +用户名: `johndoe` 密码: `secret` !!! check Notice that nowhere in the code is the plaintext password "`secret`", we only have the hashed version. -Call the endpoint `/users/me/`, you will get the response as: +调用 `/users/me/` 端点,收到下面的响应: ```JSON { @@ -352,41 +351,41 @@ Call the endpoint `/users/me/`, you will get the response as: -If you open the developer tools, you could see how the data sent only includes the token, the password is only sent in the first request to authenticate the user and get that access token, but not afterwards: +打开浏览器的开发者工具,查看数据是怎么发送的,而且数据里只包含了令牌,只有验证用户的第一个请求才发送密码,并获取访问令牌,但之后不会再发送密码: !!! note Notice the header `Authorization`, with a value that starts with `Bearer`. -## Advanced usage with `scopes` +## `scopes` 高级用法 OAuth2 has the notion of "scopes". You can use them to add a specific set of permissions to a JWT token. -Then you can give this token to a user directly or a third party, to interact with your API with a set of restrictions. +让持有令牌的用户或第三方在指定限制条件下与 API 交互。 -You can learn how to use them and how they are integrated into **FastAPI** later in the **Advanced User Guide**. +**高级用户指南**中将介绍如何使用 `scopes`,及如何把 `scopes` 集成至 **FastAPI**。 ## Recap -With what you have seen up to now, you can set up a secure **FastAPI** application using standards like OAuth2 and JWT. +至此,您可以使用 OAuth2 和 JWT 等标准配置安全的 **FastAPI** 应用。 -In almost any framework handling the security becomes a rather complex subject quite quickly. +几乎在所有框架中,处理安全问题很快都会变得非常复杂。 -Many packages that simplify it a lot have to make many compromises with the data model, database, and available features. And some of these packages that simplify things too much actually have security flaws underneath. +有些包为了简化安全流,不得不在数据模型、数据库和功能上做出妥协。 而有些过于简化的软件包其实存在了安全隐患。 --- -**FastAPI** doesn't make any compromise with any database, data model or tool. +**FastAPI** 不向任何数据库、数据模型或工具做妥协。 It gives you all the flexibility to choose the ones that fit your project the best. -And you can use directly many well maintained and widely used packages like `passlib` and `python-jose`, because **FastAPI** doesn't require any complex mechanisms to integrate external packages. +还可以直接使用 `passlib` 和 `python-jose` 等维护良好、使用广泛的包,这是因为 **FastAPI** 不需要任何复杂机制,就能集成外部的包。 But it provides you the tools to simplify the process as much as possible without compromising flexibility, robustness, or security. And you can use and implement secure, standard protocols, like OAuth2 in a relatively simple way. -You can learn more in the **Advanced User Guide** about how to use OAuth2 "scopes", for a more fine-grained permission system, following these same standards. OAuth2 with scopes is the mechanism used by many big authentication providers, like Facebook, Google, GitHub, Microsoft, Twitter, etc. to authorize third party applications to interact with their APIs on behalf of their users. +**高级用户指南**中详细介绍了 OAuth2**`scopes`**的内容,遵循同样的标准,实现更精密的权限系统。 OAuth2 的作用域是脸书、谷歌、GitHub、微软、推特等第三方身份验证应用使用的机制,让用户授权第三方应用与 API 交互。 From 3795b9893a8bead6121c15fc9b0428d72e918b75 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:55:06 +0800 Subject: [PATCH 159/163] New translations simple-oauth2.md (Chinese Simplified) --- .../docs/tutorial/security/simple-oauth2.md | 242 ++++++++++-------- 1 file changed, 131 insertions(+), 111 deletions(-) diff --git a/docs/zh/docs/tutorial/security/simple-oauth2.md b/docs/zh/docs/tutorial/security/simple-oauth2.md index f55ee6fb8f029..7b782c9e1bd33 100644 --- a/docs/zh/docs/tutorial/security/simple-oauth2.md +++ b/docs/zh/docs/tutorial/security/simple-oauth2.md @@ -1,53 +1,53 @@ -# Simple OAuth2 with Password and Bearer +# 使用密码和 Bearer 的简单 OAuth2 -Now let's build from the previous chapter and add the missing parts to have a complete security flow. +现在让我们接着上一章继续开发,并添加缺少的部分以实现一个完整的安全性流程。 -## Get the `username` and `password` +## 获取 `username` 和 `password` -We are going to use **FastAPI** security utilities to get the `username` and `password`. +我们将使用 **FastAPI** 的安全性实用工具来获取 `username` 和 `password`。 -OAuth2 specifies that when using the "password flow" (that we are using) the client/user must send a `username` and `password` fields as form data. +OAuth2 规定在使用(我们打算用的)「password 流程」时,客户端/用户必须将 `username` 和 `password` 字段作为表单数据发送。 -And the spec says that the fields have to be named like that. So `user-name` or `email` wouldn't work. +而且规范明确了字段必须这样命名。 因此 `user-name` 或 `email` 是行不通的。 -But don't worry, you can show it as you wish to your final users in the frontend. +不过不用担心,你可以在前端按照你的想法将它展示给最终用户。 -And your database models can use any other names you want. +而且你的数据库模型也可以使用你想用的任何其他名称。 -But for the login *path operation*, we need to use these names to be compatible with the spec (and be able to, for example, use the integrated API documentation system). +但是对于登录*路径操作*,我们需要使用这些名称来与规范兼容(以具备例如使用集成的 API 文档系统的能力)。 -The spec also states that the `username` and `password` must be sent as form data (so, no JSON here). +规范还写明了 `username` 和 `password` 必须作为表单数据发送(因此,此处不能使用 JSON)。 ### `scope` -The spec also says that the client can send another form field "`scope`". +规范还提到客户端可以发送另一个表单字段「`scope`」。 -The form field name is `scope` (in singular), but it is actually a long string with "scopes" separated by spaces. +这个表单字段的名称为 `scope`(单数形式),但实际上它是一个由空格分隔的「作用域」组成的长字符串。 Each "scope" is just a string (without spaces). -They are normally used to declare specific security permissions, for example: +它们通常用于声明特定的安全权限,例如: -* `users:read` or `users:write` are common examples. -* `instagram_basic` is used by Facebook / Instagram. -* `https://www.googleapis.com/auth/drive` is used by Google. +* `users:read` 或者 `users:write` 是常见的例子。 +* Facebook / Instagram 使用 `instagram_basic`。 +* Google 使用了 `https://www.googleapis.com/auth/drive` 。 !!! info In OAuth2 a "scope" is just a string that declares a specific permission required. - It doesn't matter if it has other characters like `:` or if it is a URL. + 它有没有 `:` 这样的其他字符或者是不是 URL 都没有关系。 - Those details are implementation specific. + 这些细节是具体的实现。 - For OAuth2 they are just strings. + 对 OAuth2 来说它们就只是字符串而已。 -## Code to get the `username` and `password` +## 获取 `username` 和 `password` 的代码 -Now let's use the utilities provided by **FastAPI** to handle this. +现在,让我们使用 **FastAPI** 提供的实用工具来处理此问题。 ### `OAuth2PasswordRequestForm` -First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depends` in the *path operation* for `/token`: +首先,导入 `OAuth2PasswordRequestForm`,然后在 `token` 的*路径操作*中通过 `Depends` 将其作为依赖项使用。 === "Python 3.10+" @@ -58,14 +58,18 @@ First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depe === "Python 3.9+" ```Python hl_lines="4 78" - {!> ../../../docs_src/security/tutorial003_an_py39.py!} + !!! tip + 在下一章中,你将看到一个真实的安全实现,使用了哈希密码和 JWT 令牌。 ``` === "Python 3.6+" ```Python hl_lines="4 79" - {!> ../../../docs_src/security/tutorial003_an.py!} + !!! info + OAuth2PasswordRequestForm 并不像 OAuth2PasswordBearer 一样是 FastAPI 的一个特殊的类。 ``` + 并不像 OAuth2PasswordBearer 一样是 FastAPI 的一个特殊的类。 + === "Python 3.10+ non-Annotated" @@ -73,7 +77,7 @@ First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depe Prefer to use the `Annotated` version if possible. ```Python hl_lines="2 74" - {!> ../../../docs_src/security/tutorial003_py310.py!} + 如果没有这个用户,我们将返回一个错误消息,提示「用户名或密码错误」。 ``` === "Python 3.6+ non-Annotated" @@ -82,62 +86,66 @@ First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depe Prefer to use the `Annotated` version if possible. ```Python hl_lines="4 76" - {!> ../../../docs_src/security/tutorial003.py!} + {!../../../docs_src/security/tutorial003.py!} ``` -`OAuth2PasswordRequestForm` is a class dependency that declares a form body with: +`OAuth2PasswordRequestForm` 是一个类依赖项,声明了如下的请求表单: -* The `username`. -* The `password`. -* An optional `scope` field as a big string, composed of strings separated by spaces. -* An optional `grant_type`. +* `username`。 +* `password`。 +* 一个可选的 `scope` 字段,是一个由空格分隔的字符串组成的大字符串。 +* 一个可选的 `grant_type`. !!! tip The OAuth2 spec actually *requires* a field `grant_type` with a fixed value of `password`, but `OAuth2PasswordRequestForm` doesn't enforce it. - If you need to enforce it, use `OAuth2PasswordRequestFormStrict` instead of `OAuth2PasswordRequestForm`. + 如果你需要强制要求这一点,请使用 `OAuth2PasswordRequestFormStrict` 而不是 `OAuth2PasswordRequestForm`。 -* An optional `client_id` (we don't need it for our example). -* An optional `client_secret` (we don't need it for our example). +* 一个可选的 `client_id`(我们的示例不需要它)。 +* 一个可选的 `client_secret`(我们的示例不需要它)。 !!! info The `OAuth2PasswordRequestForm` is not a special class for **FastAPI** as is `OAuth2PasswordBearer`. - `OAuth2PasswordBearer` makes **FastAPI** know that it is a security scheme. So it is added that way to OpenAPI. + `OAuth2PasswordBearer` 使得 **FastAPI** 明白它是一个安全方案。 所以它得以通过这种方式添加到 OpenAPI 中。 - But `OAuth2PasswordRequestForm` is just a class dependency that you could have written yourself, or you could have declared `Form` parameters directly. + 但 `OAuth2PasswordRequestForm` 只是一个你可以自己编写的类依赖项,或者你也可以直接声明 `Form` 参数。 - But as it's a common use case, it is provided by **FastAPI** directly, just to make it easier. + 但是由于这是一种常见的使用场景,因此 FastAPI 出于简便直接提供了它。 -### Use the form data +### 使用表单数据 !!! tip The instance of the dependency class `OAuth2PasswordRequestForm` won't have an attribute `scope` with the long string separated by spaces, instead, it will have a `scopes` attribute with the actual list of strings for each scope sent. - We are not using `scopes` in this example, but the functionality is there if you need it. + 在此示例中我们没有使用 `scopes`,但如果你需要的话可以使用该功能。 -Now, get the user data from the (fake) database, using the `username` from the form field. +现在,使用表单字段中的 `username` 从(伪)数据库中获取用户数据。 -If there is no such user, we return an error saying "incorrect username or password". +如果密码不匹配,我们将返回同一个错误。 -For the error, we use the exception `HTTPException`: +对于这个错误,我们使用 `HTTPException` 异常: === "Python 3.10+" ```Python hl_lines="3 79-81" - {!> ../../../docs_src/security/tutorial003_an_py310.py!} + !!! info + 我们在此处返回的值为 Bearer 的额外响应头 WWW-Authenticate 也是规范的一部分。 ``` + 的额外响应头 WWW-Authenticate 也是规范的一部分。 + === "Python 3.9+" ```Python hl_lines="3 79-81" - {!> ../../../docs_src/security/tutorial003_an_py39.py!} + https://fastapi.tiangolo.com/img/tutorial/security/image05.png ``` === "Python 3.6+" ```Python hl_lines="3 80-82" - {!> ../../../docs_src/security/tutorial003_an.py!} + !!! info + 在 OAuth2 中「作用域」只是一个声明所需特定权限的字符串。 ``` === "Python 3.10+ non-Annotated" @@ -146,7 +154,7 @@ For the error, we use the exception `HTTPException`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="1 75-77" - {!> ../../../docs_src/security/tutorial003_py310.py!} + {!../../../docs_src/security/tutorial003.py!} ``` === "Python 3.6+ non-Annotated" @@ -155,32 +163,32 @@ For the error, we use the exception `HTTPException`: Prefer to use the `Annotated` version if possible. ```Python hl_lines="3 77-79" - {!> ../../../docs_src/security/tutorial003.py!} + {!../../../docs_src/security/tutorial003.py!} ``` -### Check the password +### 校验密码 -At this point we have the user data from our database, but we haven't checked the password. +目前我们已经从数据库中获取了用户数据,但尚未校验密码。 -Let's put that data in the Pydantic `UserInDB` model first. +让我们首先将这些数据放入 Pydantic `UserInDB` 模型中。 -You should never save plaintext passwords, so, we'll use the (fake) password hashing system. +永远不要保存明文密码,因此,我们将使用(伪)哈希密码系统。 If the passwords don't match, we return the same error. -#### Password hashing +#### 哈希密码 -"Hashing" means: converting some content (a password in this case) into a sequence of bytes (just a string) that looks like gibberish. +「哈希」的意思是:将某些内容(在本例中为密码)转换为看起来像乱码的字节序列(只是一个字符串)。 -Whenever you pass exactly the same content (exactly the same password) you get exactly the same gibberish. +每次你传入完全相同的内容(完全相同的密码)时,你都会得到完全相同的乱码。 -But you cannot convert from the gibberish back to the password. +但是你不能从乱码转换回密码。 -##### Why use password hashing +##### 为什么使用哈希密码 -If your database is stolen, the thief won't have your users' plaintext passwords, only the hashes. +如果你的数据库被盗,小偷将无法获得用户的明文密码,只有哈希值。 -So, the thief won't be able to try to use those same passwords in another system (as many users use the same password everywhere, this would be dangerous). +因此,小偷将无法尝试在另一个系统中使用这些相同的密码(由于许多用户在任何地方都使用相同的密码,因此这很危险)。 === "Python 3.10+" @@ -191,14 +199,17 @@ So, the thief won't be able to try to use those same passwords in another system === "Python 3.9+" ```Python hl_lines="82-85" - {!> ../../../docs_src/security/tutorial003_an_py39.py!} + https://fastapi.tiangolo.com/img/tutorial/security/image04.png ``` === "Python 3.6+" ```Python hl_lines="83-86" - {!> ../../../docs_src/security/tutorial003_an.py!} + !!! tip + OAuth2 规范实际上要求 grant_type 字段使用一个固定的值 password,但是 OAuth2PasswordRequestForm 没有作强制约束。 ``` + 字段使用一个固定的值 password,但是 OAuth2PasswordRequestForm 没有作强制约束。 + === "Python 3.10+ non-Annotated" @@ -215,14 +226,14 @@ So, the thief won't be able to try to use those same passwords in another system Prefer to use the `Annotated` version if possible. ```Python hl_lines="80-83" - {!> ../../../docs_src/security/tutorial003.py!} + {!../../../docs_src/security/tutorial003.py!} ``` -#### About `**user_dict` +#### 关于 `**user_dict` -`UserInDB(**user_dict)` means: +`UserInDB(**user_dict)` 表示: -*Pass the keys and values of the `user_dict` directly as key-value arguments, equivalent to:* +*直接将 `user_dict` 的键和值作为关键字参数传递,等同于:* ```Python UserInDB( @@ -237,20 +248,20 @@ UserInDB( !!! info For a more complete explanation of `**user_dict` check back in [the documentation for **Extra Models**](../extra-models.md#about-user_indict){.internal-link target=_blank}. -## Return the token +## 返回令牌 -The response of the `token` endpoint must be a JSON object. +`token` 端点的响应必须是一个 JSON 对象。 -It should have a `token_type`. In our case, as we are using "Bearer" tokens, the token type should be "`bearer`". +它应该有一个 `token_type`。 在我们的例子中,由于我们使用的是「Bearer」令牌,因此令牌类型应为「`bearer`」。 -And it should have an `access_token`, with a string containing our access token. +并且还应该有一个 `access_token` 字段,它是一个包含我们的访问令牌的字符串。 -For this simple example, we are going to just be completely insecure and return the same `username` as the token. +对于这个简单的示例,我们将极其不安全地返回相同的 `username` 作为令牌。 !!! tip In the next chapter, you will see a real secure implementation, with password hashing and JWT tokens. - But for now, let's focus on the specific details we need. + 但现在,让我们仅关注我们需要的特定细节。 === "Python 3.10+" @@ -267,8 +278,11 @@ For this simple example, we are going to just be completely insecure and return === "Python 3.6+" ```Python hl_lines="88" - {!> ../../../docs_src/security/tutorial003_an.py!} + !!! info + 有关 user_dict 的更完整说明,请参阅额外的模型文档{.internal-link target=_blank}。 ``` + 的更完整说明,请参阅[**额外的模型**文档](../extra-models.md#about-user_indict){.internal-link target=_blank}。 + === "Python 3.10+ non-Annotated" @@ -285,29 +299,29 @@ For this simple example, we are going to just be completely insecure and return Prefer to use the `Annotated` version if possible. ```Python hl_lines="85" - {!> ../../../docs_src/security/tutorial003.py!} + {!../../../docs_src/security/tutorial003.py!} ``` !!! tip By the spec, you should return a JSON with an `access_token` and a `token_type`, the same as in this example. - This is something that you have to do yourself in your code, and make sure you use those JSON keys. + 这是你必须在代码中自行完成的工作,并且要确保使用了这些 JSON 字段。 - It's almost the only thing that you have to remember to do correctly yourself, to be compliant with the specifications. + 这几乎是唯一的你需要自己记住并正确地执行以符合规范的事情。 - For the rest, **FastAPI** handles it for you. + 其余的,**FastAPI** 都会为你处理。 -## Update the dependencies +## 更新依赖项 -Now we are going to update our dependencies. +现在我们将更新我们的依赖项。 -We want to get the `current_user` *only* if this user is active. +我们想要仅当此用户处于启用状态时才能获取 `current_user`。 -So, we create an additional dependency `get_current_active_user` that in turn uses `get_current_user` as a dependency. +因此,我们创建了一个额外的依赖项 `get_current_active_user`,而该依赖项又以 `get_current_user` 作为依赖项。 -Both of these dependencies will just return an HTTP error if the user doesn't exist, or if is inactive. +如果用户不存在或处于未启用状态,则这两个依赖项都将仅返回 HTTP 错误。 -So, in our endpoint, we will only get a user if the user exists, was correctly authenticated, and is active: +因此,在我们的端点中,只有当用户存在,身份认证通过且处于启用状态时,我们才能获得该用户: === "Python 3.10+" @@ -318,14 +332,17 @@ So, in our endpoint, we will only get a user if the user exists, was correctly a === "Python 3.9+" ```Python hl_lines="58-66 69-74 94" - {!> ../../../docs_src/security/tutorial003_an_py39.py!} + 每个「作用域」只是一个字符串(中间没有空格)。 ``` === "Python 3.6+" ```Python hl_lines="59-67 70-75 95" - {!> ../../../docs_src/security/tutorial003_an.py!} + !!! tip + 类依赖项 OAuth2PasswordRequestForm 的实例不会有用空格分隔的长字符串属性 scope,而是具有一个 scopes 属性,该属性将包含实际被发送的每个作用域字符串组成的列表。 ``` + 的实例不会有用空格分隔的长字符串属性 scope,而是具有一个 scopes 属性,该属性将包含实际被发送的每个作用域字符串组成的列表。 + === "Python 3.10+ non-Annotated" @@ -342,49 +359,52 @@ So, in our endpoint, we will only get a user if the user exists, was correctly a Prefer to use the `Annotated` version if possible. ```Python hl_lines="58-66 69-72 90" - {!> ../../../docs_src/security/tutorial003.py!} + !!! tip + 根据规范,你应该像本示例一样,返回一个带有 access_tokentoken_type 的 JSON。 ``` + 和 token_type 的 JSON。 + !!! info The additional header `WWW-Authenticate` with value `Bearer` we are returning here is also part of the spec. - Any HTTP (error) status code 401 "UNAUTHORIZED" is supposed to also return a `WWW-Authenticate` header. + 任何的 401「未认证」HTTP(错误)状态码都应该返回 `WWW-Authenticate` 响应头。 - In the case of bearer tokens (our case), the value of that header should be `Bearer`. + 对于 bearer 令牌(我们的例子),该响应头的值应为 `Bearer`。 - You can actually skip that extra header and it would still work. + 实际上你可以忽略这个额外的响应头,不会有什么问题。 - But it's provided here to be compliant with the specifications. + 但此处提供了它以符合规范。 - Also, there might be tools that expect and use it (now or in the future) and that might be useful for you or your users, now or in the future. + 而且,(现在或将来)可能会有工具期望得到并使用它,然后对你或你的用户有用处。 - That's the benefit of standards... + 这就是遵循标准的好处... ## See it in action -Open the interactive docs: http://127.0.0.1:8000/docs. +打开交互式文档:http://127.0.0.1:8000/docs。 -### Authenticate +### 身份认证 -Click the "Authorize" button. +点击「Authorize」按钮。 -Use the credentials: +使用以下凭证: -User: `johndoe` +用户名:`johndoe` -Password: `secret` +密码:`secret` -After authenticating in the system, you will see it like: +在系统中进行身份认证后,你将看到: -### Get your own user data +### 获取本人的用户数据 -Now use the operation `GET` with the path `/users/me`. +现在执行 `/users/me` 路径的 `GET` 操作。 -You will get your user's data, like: +你将获得你的用户数据,如: ```JSON { @@ -398,7 +418,7 @@ You will get your user's data, like: -If you click the lock icon and logout, and then try the same operation again, you will get an HTTP 401 error of: +如果你点击锁定图标并注销,然后再次尝试同一操作,则会得到 HTTP 401 错误: ```JSON { @@ -406,17 +426,17 @@ If you click the lock icon and logout, and then try the same operation again, yo } ``` -### Inactive user +### 未启用的用户 -Now try with an inactive user, authenticate with: +现在尝试使用未启用的用户,并通过以下方式进行身份认证: -User: `alice` +用户名:`alice` -Password: `secret2` +密码:`secret2` -And try to use the operation `GET` with the path `/users/me`. +然后尝试执行 `/users/me` 路径的 `GET` 操作。 -You will get an "inactive user" error, like: +你将得到一个「未启用的用户」错误,如: ```JSON { @@ -426,10 +446,10 @@ You will get an "inactive user" error, like: ## Recap -You now have the tools to implement a complete security system based on `username` and `password` for your API. +现在你掌握了为你的 API 实现一个基于 `username` 和 `password` 的完整安全系统的工具。 -Using these tools, you can make the security system compatible with any database and with any user or data model. +使用这些工具,你可以使安全系统与任何数据库以及任何用户或数据模型兼容。 -The only detail missing is that it is not actually "secure" yet. +唯一缺少的细节是它实际上还并不「安全」。 -In the next chapter you'll see how to use a secure password hashing library and JWT tokens. +在下一章中,你将看到如何使用一个安全的哈希密码库和 JWT 令牌。 From 0f287a698cafa38c5c58f75f5e49263fb4ce1662 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:55:07 +0800 Subject: [PATCH 160/163] New translations sql-databases.md (Chinese Simplified) --- docs/zh/docs/tutorial/sql-databases.md | 641 ++++++++++++++----------- 1 file changed, 353 insertions(+), 288 deletions(-) diff --git a/docs/zh/docs/tutorial/sql-databases.md b/docs/zh/docs/tutorial/sql-databases.md index 136946bddddda..53de41a7bd3c2 100644 --- a/docs/zh/docs/tutorial/sql-databases.md +++ b/docs/zh/docs/tutorial/sql-databases.md @@ -1,4 +1,4 @@ -# SQL (Relational) Databases +# SQL (关系型) 数据库 !!! info These docs are about to be updated. 🎉 @@ -7,73 +7,74 @@ The new docs will include Pydantic v2 and will use SQLModel (which is also based on SQLAlchemy) once it is updated to use Pydantic v2 as well. -**FastAPI** doesn't require you to use a SQL (relational) database. +**FastAPI**不需要你使用SQL(关系型)数据库。 -But you can use any relational database that you want. +但是您可以使用任何您想要的关系型数据库。 -Here we'll see an example using SQLAlchemy. +在这里,让我们看一个使用着[SQLAlchemy](https://www.sqlalchemy.org/)的示例。 -You can easily adapt it to any database supported by SQLAlchemy, like: +您可以很容易地将SQLAlchemy支持任何数据库,像: * PostgreSQL * MySQL * SQLite * Oracle -* Microsoft SQL Server, etc. +* Microsoft SQL Server,等等其它数据库 -In this example, we'll use **SQLite**, because it uses a single file and Python has integrated support. So, you can copy this example and run it as is. +在此示例中,我们将使用**SQLite**,因为它使用单个文件并且 在Python中具有集成支持。 因此,您可以复制此示例并按原样来运行它。 -Later, for your production application, you might want to use a database server like **PostgreSQL**. +稍后,对于您的产品级别的应用程序,您可能会要使用像**PostgreSQL**这样的数据库服务器。 -!!! tip - There is an official project generator with **FastAPI** and **PostgreSQL**, all based on **Docker**, including a frontend and more tools: https://github.com/tiangolo/full-stack-fastapi-postgresql +!!! !!! tip + 这儿有一个**FastAPI**和**PostgreSQL**的官方项目生成器,全部基于**Docker**,包括前端和更多工具:https://github.com/tiangolo/full-stack-fastapi-postgresql -!!! note - Notice that most of the code is the standard `SQLAlchemy` code you would use with any framework. +!!! !!! note + 请注意,大部分代码是`SQLAlchemy`的标准代码,您可以用于任何框架。 - The **FastAPI** specific code is as small as always. + FastAPI特定的代码和往常一样少。 ## ORMs -**FastAPI** works with any database and any style of library to talk to the database. +**FastAPI**可与任何数据库在任何样式的库中一起与 数据库进行通信。 -A common pattern is to use an "ORM": an "object-relational mapping" library. +一种常见的模式是使用“ORM”:对象关系映射。 -An ORM has tools to convert ("*map*") between *objects* in code and database tables ("*relations*"). +ORM 具有在代码和数据库表(“*关系型”)中的**对象**之间转换(“*映射*”)的工具。 -With an ORM, you normally create a class that represents a table in a SQL database, each attribute of the class represents a column, with a name and a type. +使用 ORM,您通常会在 SQL 数据库中创建一个代表映射的类,该类的每个属性代表一个列,具有名称和类型。 -For example a class `Pet` could represent a SQL table `pets`. +例如,一个类`Pet`可以表示一个 SQL 表`pets`。 And each *instance* object of that class represents a row in the database. -For example an object `orion_cat` (an instance of `Pet`) could have an attribute `orion_cat.type`, for the column `type`. And the value of that attribute could be, e.g. `"cat"`. +又例如,一个对象`orion_cat`(`Pet`的一个实例)可以有一个属性`orion_cat.type`, 对标数据库中的`type`列。 并且该属性的值可以是其它,例如`"cat"`。 -These ORMs also have tools to make the connections or relations between tables or entities. +这些 ORM 还具有在表或实体之间建立关系的工具(比如创建多表关系)。 -This way, you could also have an attribute `orion_cat.owner` and the owner would contain the data for this pet's owner, taken from the table *owners*. +因此,`orion_cat.owner.name`可能是该宠物主人的姓名(来自表`owners`中的列`name`)。 -So, `orion_cat.owner.name` could be the name (from the `name` column in the `owners` table) of this pet's owner. +这样,您还可以拥有一个属性`orion_cat.owner`,它包含该宠物所有者的数据,这些数据取自另外一个表。 -It could have a value like `"Arquilian"`. +它可能有一个像`"Arquilian"`(一种业务逻辑)。 -And the ORM will do all the work to get the information from the corresponding table *owners* when you try to access it from your pet object. +当您尝试从您的宠物对象访问它时,ORM 将完成所有工作以从相应的表*所有者那里再获取信息。

-Common ORMs are for example: Django-ORM (part of the Django framework), SQLAlchemy ORM (part of SQLAlchemy, independent of framework) and Peewee (independent of framework), among others. +常见的 ORM 例如:Django-ORM(Django 框架的一部分)、SQLAlchemy ORM(SQLAlchemy 的一部分,独立于框架)和 Peewee(独立于框架)等。 -Here we will see how to work with **SQLAlchemy ORM**. +在这里,我们将看到如何使用**SQLAlchemy ORM**。 -In a similar way you could use any other ORM. +以类似的方式,您也可以使用任何其他 ORM。 -!!! tip - There's an equivalent article using Peewee here in the docs. +!!! !!! tip + 在文档中也有一篇使用 Peewee 的等效的文章。 -## File structure +## 文件结构 -For these examples, let's say you have a directory named `my_super_project` that contains a sub-directory called `sql_app` with a structure like this: +对于这些示例,假设您有一个名为的目录`my_super_project`,其中包含一个名为的子目录`sql_app`,其结构如下: ``` . +. └── sql_app ├── __init__.py ├── crud.py @@ -83,13 +84,14 @@ For these examples, let's say you have a directory named `my_super_project` that └── schemas.py ``` -The file `__init__.py` is just an empty file, but it tells Python that `sql_app` with all its modules (Python files) is a package. +该文件`__init__.py`只是一个空文件,但它告诉 Python 其中`sql_app`的所有模块(Python 文件)都是一个包。 -Now let's see what each file/module does. +现在让我们看看每个文件/模块的作用。 ## Install `SQLAlchemy` -First you need to install `SQLAlchemy`: +!!! tip + SQLAlchemy 模型`User`包含一个`hashed_password`,它应该是一个包含散列的安全密码。
@@ -101,51 +103,51 @@ $ pip install sqlalchemy
-## Create the SQLAlchemy parts +## 创建 SQLAlchemy 部件 -Let's refer to the file `sql_app/database.py`. +该文件将位于文件中的同一目录中`sql_app.db`。 -### Import the SQLAlchemy parts +### 导入 SQLAlchemy 部件 ```Python hl_lines="1-3" {!../../../docs_src/sql_databases/sql_app/database.py!} ``` -### Create a database URL for SQLAlchemy +### 为 SQLAlchemy 定义数据库 URL地址 ```Python hl_lines="5-6" {!../../../docs_src/sql_databases/sql_app/database.py!} ``` -In this example, we are "connecting" to a SQLite database (opening a file with the SQLite database). +在这个例子中,我们正在“连接”到一个 SQLite 数据库(用 SQLite 数据库打开一个文件)。 -The file will be located at the same directory in the file `sql_app.db`. +让我们涉及到文件`sql_app/database.py`。 -That's why the last part is `./sql_app.db`. +这就是为什么最后一部分是`./sql_app.db`. -If you were using a **PostgreSQL** database instead, you would just have to uncomment the line: +如果您使用的是**PostgreSQL**数据库,则只需取消注释该行: ```Python SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db" ``` -...and adapt it with your database data and credentials (equivalently for MySQL, MariaDB or any other). +...并根据您的数据库数据和相关凭据(也适用于 MySQL、MariaDB 或任何其他)对其进行调整。 -!!! tip +!!! !!! tip - This is the main line that you would have to modify if you wanted to use a different database. + 如果您想使用不同的数据库,这是就是您必须修改的地方。 -### Create the SQLAlchemy `engine` +### 创建 SQLAlchemy 引擎 -The first step is to create a SQLAlchemy "engine". +第一步,创建一个 SQLAlchemy的“引擎”。 -We will later use this `engine` in other places. +我们稍后会将这个`engine`在其他地方使用。 ```Python hl_lines="8-10" {!../../../docs_src/sql_databases/sql_app/database.py!} ``` -#### Note +#### 注意 The argument: @@ -153,121 +155,119 @@ The argument: connect_args={"check_same_thread": False} ``` -...is needed only for `SQLite`. It's not needed for other databases. +...仅用于`SQLite`,在其他数据库不需要它。 It's not needed for other databases. -!!! info "Technical Details" +!!! !!! info "技术细节" - By default SQLite will only allow one thread to communicate with it, assuming that each thread would handle an independent request. + 默认情况下,SQLite 只允许一个线程与其通信,假设有多个线程的话,也只将处理一个独立的请求。 - This is to prevent accidentally sharing the same connection for different things (for different requests). + 这是为了防止意外地为不同的事物(不同的请求)共享相同的连接。 - But in FastAPI, using normal functions (`def`) more than one thread could interact with the database for the same request, so we need to make SQLite know that it should allow that with `connect_args={"check_same_thread": False}`. + 但是在 FastAPI 中,普遍使用def函数,多个线程可以为同一个请求与数据库交互,所以我们需要使用`connect_args={"check_same_thread": False}`来让SQLite允许这样。 - Also, we will make sure each request gets its own database connection session in a dependency, so there's no need for that default mechanism. + 此外,我们将确保每个请求都在依赖项中获得自己的数据库连接会话,因此不需要该默认机制。 -### Create a `SessionLocal` class +### 创建一个`SessionLocal`类 -Each instance of the `SessionLocal` class will be a database session. The class itself is not a database session yet. +每个实例`SessionLocal`都会是一个数据库会话。 当然该类本身还不是数据库会话。 -But once we create an instance of the `SessionLocal` class, this instance will be the actual database session. +但是一旦我们创建了一个`SessionLocal`类的实例,这个实例将是实际的数据库会话。 -We name it `SessionLocal` to distinguish it from the `Session` we are importing from SQLAlchemy. +我们命名它是`SessionLocal`为了将它与我们从 SQLAlchemy 导入的`Session`区别开来。 -We will use `Session` (the one imported from SQLAlchemy) later. +稍后我们将使用`Session`(从 SQLAlchemy 导入的那个)。 -To create the `SessionLocal` class, use the function `sessionmaker`: +要创建`SessionLocal`类,请使用函数`sessionmaker`: ```Python hl_lines="11" {!../../../docs_src/sql_databases/sql_app/database.py!} ``` -### Create a `Base` class +### 创建一个`Base`类 -Now we will use the function `declarative_base()` that returns a class. +现在我们将使用`declarative_base()`返回一个类。 -Later we will inherit from this class to create each of the database models or classes (the ORM models): +稍后我们将用这个类继承,来创建每个数据库模型或类(ORM 模型): ```Python hl_lines="13" {!../../../docs_src/sql_databases/sql_app/database.py!} ``` -## Create the database models +## 创建 Pydantic 模型 -Let's now see the file `sql_app/models.py`. +现在让我们看看文件`sql_app/models.py`。 -### Create SQLAlchemy models from the `Base` class +### 用`Base`类来创建 SQLAlchemy 模型 -We will use this `Base` class we created before to create the SQLAlchemy models. +我们将使用我们之前创建的`Base`类来创建 SQLAlchemy 模型。 -!!! tip - SQLAlchemy uses the term "**model**" to refer to these classes and instances that interact with the database. +!!! 使用您的数据创建一个 SQLAlchemy 模型*实例。

- But Pydantic also uses the term "**model**" to refer to something different, the data validation, conversion, and documentation classes and instances. + 而 Pydantic 也使用“模型”这个术语 来指代不同的东西,即数据验证、转换以及文档类和实例。 -Import `Base` from `database` (the file `database.py` from above). +从`database`(来自上面的`database.py`文件)导入`Base`。 -Create classes that inherit from it. +创建从它继承的类。 -These classes are the SQLAlchemy models. +这些类就是 SQLAlchemy 模型。 ```Python hl_lines="4 7-8 18-19" {!../../../docs_src/sql_databases/sql_app/models.py!} ``` -The `__tablename__` attribute tells SQLAlchemy the name of the table to use in the database for each of these models. +这个`__tablename__`属性是用来告诉 SQLAlchemy 要在数据库中为每个模型使用的数据库表的名称。 -### Create model attributes/columns +### 创建模型属性/列 -Now create all the model (class) attributes. +现在创建所有模型(类)属性。 -Each of these attributes represents a column in its corresponding database table. +这些属性中的每一个都代表其相应数据库表中的一列。 -We use `Column` from SQLAlchemy as the default value. +我们使用`Column`来表示 SQLAlchemy 中的默认值。 -And we pass a SQLAlchemy class "type", as `Integer`, `String`, and `Boolean`, that defines the type in the database, as an argument. +我们传递一个 SQLAlchemy “类型”,如`Integer`、`String`和`Boolean`,它定义了数据库中的类型,作为参数。 ```Python hl_lines="1 10-13 21-24" {!../../../docs_src/sql_databases/sql_app/models.py!} ``` -### Create the relationships +### 创建关系 -Now create the relationships. +现在创建关系。 -For this, we use `relationship` provided by SQLAlchemy ORM. +为此,我们使用SQLAlchemy ORM提供的`relationship`。 -This will become, more or less, a "magic" attribute that will contain the values from other tables related to this one. +这将或多或少会成为一种“神奇”属性,其中表示该表与其他相关的表中的值。 ```Python hl_lines="2 15 26" {!../../../docs_src/sql_databases/sql_app/models.py!} ``` -When accessing the attribute `items` in a `User`, as in `my_user.items`, it will have a list of `Item` SQLAlchemy models (from the `items` table) that have a foreign key pointing to this record in the `users` table. +当访问 user 中的属性`items`时,如 中`my_user.items`,它将有一个`Item`SQLAlchemy 模型列表(来自`items`表),这些模型具有指向`users`表中此记录的外键。 -When you access `my_user.items`, SQLAlchemy will actually go and fetch the items from the database in the `items` table and populate them here. +当您访问`my_user.items`时,SQLAlchemy 实际上会从`items`表中的获取一批记录并在此处填充进去。 -And when accessing the attribute `owner` in an `Item`, it will contain a `User` SQLAlchemy model from the `users` table. It will use the `owner_id` attribute/column with its foreign key to know which record to get from the `users` table. +同样,当访问 Item中的属性`owner`时,它将包含表中的`User`SQLAlchemy 模型`users`。 使用`owner_id`属性/列及其外键来了解要从`users`表中获取哪条记录。 -## Create the Pydantic models +## 创建数据库模型 -Now let's check the file `sql_app/schemas.py`. +现在让我们查看一下文件`sql_app/schemas.py`。 -!!! tip - To avoid confusion between the SQLAlchemy *models* and the Pydantic *models*, we will have the file `models.py` with the SQLAlchemy models, and the file `schemas.py` with the Pydantic models. +!!! 现在创建当从 API 返回数据时、将在读取数据时使用的Pydantic*模型(schemas)。

- These Pydantic models define more or less a "schema" (a valid data shape). + 这些 Pydantic 模型或多或少地定义了一个“schema”(一个有效的数据形状)。 - So this will help us avoiding confusion while using both. + 因此,这将帮助我们在使用两者时避免混淆。 -### Create initial Pydantic *models* / schemas +### 创建初始 Pydantic*模型*/模式 -Create an `ItemBase` and `UserBase` Pydantic *models* (or let's say "schemas") to have common attributes while creating or reading data. +创建一个`ItemBase`和`UserBase`Pydantic*模型*(或者我们说“schema”)以及在创建或读取数据时具有共同的属性。 -And create an `ItemCreate` and `UserCreate` that inherit from them (so they will have the same attributes), plus any additional data (attributes) needed for creation. +`ItemCreate`为 创建一个`UserCreate`继承自它们的所有属性(因此它们将具有相同的属性),以及创建所需的任何其他数据(属性)。 -So, the user will also have a `password` when creating it. +因此在创建时也应当有一个`password`属性。 -But for security, the `password` won't be in other Pydantic *models*, for example, it won't be sent from the API when reading a user. +但是为了安全起见,`password`不会出现在其他同类 Pydantic*模型*中,例如用户请求时不应该从 API 返回响应中包含它。 === "Python 3.10+" @@ -287,31 +287,32 @@ But for security, the `password` won't be in other Pydantic *models*, for exampl {!> ../../../docs_src/sql_databases/sql_app/schemas.py!} ``` -#### SQLAlchemy style and Pydantic style +#### SQLAlchemy 风格和 Pydantic 风格 -Notice that SQLAlchemy *models* define attributes using `=`, and pass the type as a parameter to `Column`, like in: +请注意,SQLAlchemy*模型*使用 `=`来定义属性,并将类型作为参数传递给`Column`,例如: ```Python name = Column(String) ``` -while Pydantic *models* declare the types using `:`, the new type annotation syntax/type hints: +虽然 Pydantic*模型*使用`:` 声明类型,但新的类型注释语法/类型提示是: ```Python name: str ``` -Have it in mind, so you don't get confused when using `=` and `:` with them. +请牢记这一点,这样您在使用`:`还是`=`时就不会感到困惑。 -### Create Pydantic *models* / schemas for reading / returning +### 创建用于读取/返回的Pydantic*模型/模式* -Now create Pydantic *models* (schemas) that will be used when reading data, when returning it from the API. +!!! tip + 请注意,读取用户(从 API 返回)时将使用不包括`password`的`User` Pydantic*模型*。 -For example, before creating an item, we don't know what will be the ID assigned to it, but when reading it (when returning it from the API) we will already know its ID. +例如,在创建一个项目之前,我们不知道分配给它的 ID 是什么,但是在读取它时(从 API 返回时)我们已经知道它的 ID。 -The same way, when reading a user, we can now declare that `items` will contain the items that belong to this user. +同样,当读取用户时,我们现在可以声明`items`,将包含属于该用户的项目。 -Not only the IDs of those items, but all the data that we defined in the Pydantic *model* for reading items: `Item`. +不仅是这些项目的 ID,还有我们在 Pydantic*模型*中定义的用于读取项目的所有数据:`Item`. === "Python 3.10+" @@ -334,13 +335,13 @@ Not only the IDs of those items, but all the data that we defined in the Pydanti !!! tip Notice that the `User`, the Pydantic *model* that will be used when reading a user (returning it from the API) doesn't include the `password`. -### Use Pydantic's `orm_mode` +### 使用 Pydantic 的`orm_mode` -Now, in the Pydantic *models* for reading, `Item` and `User`, add an internal `Config` class. +现在,在用于查询的 Pydantic*模型*`Item`中`User`,添加一个内部`Config`类。 -This `Config` class is used to provide configurations to Pydantic. +此类[`Config`](https://pydantic-docs.helpmanual.io/usage/model_config/)用于为 Pydantic 提供配置。 -In the `Config` class, set the attribute `orm_mode = True`. +在`Config`类中,设置属性`orm_mode = True`。 === "Python 3.10+" @@ -360,92 +361,92 @@ In the `Config` class, set the attribute `orm_mode = True`. {!> ../../../docs_src/sql_databases/sql_app/schemas.py!} ``` -!!! tip - Notice it's assigning a value with `=`, like: +!!! !!! tip + 请注意,它使用`=`分配一个值,例如: `orm_mode = True` - It doesn't use `:` as for the type declarations before. + 它不使用之前的`:`来类型声明。 - This is setting a config value, not declaring a type. + 这是设置配置值,而不是声明类型。 -Pydantic's `orm_mode` will tell the Pydantic *model* to read the data even if it is not a `dict`, but an ORM model (or any other arbitrary object with attributes). +Pydantic`orm_mode`将告诉 Pydantic*模型*读取数据,即它不是一个`dict`,而是一个 ORM 模型(或任何其他具有属性的任意对象)。 -This way, instead of only trying to get the `id` value from a `dict`, as in: +这样,而不是仅仅试图从`dict`上 `id` 中获取值,如下所示: ```Python id = data["id"] ``` -it will also try to get it from an attribute, as in: +尝试从属性中获取它,如: ```Python id = data.id ``` -And with this, the Pydantic *model* is compatible with ORMs, and you can just declare it in the `response_model` argument in your *path operations*. +有了这个,Pydantic*模型*与 ORM 兼容,您只需在*路径操作*`response_model`的参数中声明它即可。 -You will be able to return a database model and it will read the data from it. +您将能够返回一个数据库模型,它将从中读取数据。 -#### Technical Details about ORM mode +#### ORM 模式的技术细节 -SQLAlchemy and many others are by default "lazy loading". +SQLAlchemy 和许多其他默认情况下是“延迟加载”。 -That means, for example, that they don't fetch the data for relationships from the database unless you try to access the attribute that would contain that data. +这意味着,例如,除非您尝试访问包含该数据的属性,否则它们不会从数据库中获取关系数据。 -For example, accessing the attribute `items`: +例如,访问属性`items`: ```Python current_user.items ``` -would make SQLAlchemy go to the `items` table and get the items for this user, but not before. +将使 SQLAlchemy 转到`items`表并获取该用户的项目,在调用`.items`之前不会去查询数据库。 -Without `orm_mode`, if you returned a SQLAlchemy model from your *path operation*, it wouldn't include the relationship data. +没有`orm_mode`,如果您从*路径操作*返回一个 SQLAlchemy 模型,它不会包含关系数据。 -Even if you declared those relationships in your Pydantic models. +即使您在 Pydantic 模型中声明了这些关系,也没有用处。 -But with ORM mode, as Pydantic itself will try to access the data it needs from attributes (instead of assuming a `dict`), you can declare the specific data you want to return and it will be able to go and get it, even from ORMs. +但是在 ORM 模式下,由于 Pydantic 本身会尝试从属性访问它需要的数据(而不是假设为 `dict`),你可以声明你想要返回的特定数据,它甚至可以从 ORM 中获取它。 -## CRUD utils +## CRUD工具 -Now let's see the file `sql_app/crud.py`. +现在让我们看看文件`sql_app/crud.py`。 -In this file we will have reusable functions to interact with the data in the database. +在这个文件中,我们将编写可重用的函数用来与数据库中的数据进行交互。 -**CRUD** comes from: **C**reate, **R**ead, **U**pdate, and **D**elete. +**CRUD**分别为:**增加**、**查询**、**更改**和**删除**,即增删改查。 ...although in this example we are only creating and reading. -### Read data +### 读取数据 -Import `Session` from `sqlalchemy.orm`, this will allow you to declare the type of the `db` parameters and have better type checks and completion in your functions. +从 `sqlalchemy.orm`中导入`Session`,这将允许您声明`db`参数的类型,并在您的函数中进行更好的类型检查和完成。 -Import `models` (the SQLAlchemy models) and `schemas` (the Pydantic *models* / schemas). +导入之前的`models`(SQLAlchemy 模型)和`schemas`(Pydantic*模型*/模式)。 -Create utility functions to: +创建一些实用函数来完成: -* Read a single user by ID and by email. -* Read multiple users. +* 通过 ID 和电子邮件查询单个用户。 +* 查询多个用户。 * Read multiple items. ```Python hl_lines="1 3 6-7 10-11 14-15 27-28" {!../../../docs_src/sql_databases/sql_app/crud.py!} ``` -!!! tip - By creating functions that are only dedicated to interacting with the database (get a user or an item) independent of your *path operation function*, you can more easily reuse them in multiple parts and also add unit tests for them. +!!! 该类的每个*实例对象都代表数据库中的一行数据。

-### Create data +### 创建数据 -Now create utility functions to create data. +现在创建实用程序函数来创建数据。 -The steps are: +它的步骤是: -* Create a SQLAlchemy model *instance* with your data. -* `add` that instance object to your database session. +* !!! tip + 为了避免 SQLAlchemy*模型*和 Pydantic*模型*之间的混淆,我们将有`models.py`(SQLAlchemy 模型的文件)和`schemas.py`( Pydantic 模型的文件)。 +* 使用`add`来将该实例对象添加到您的数据库。 * `commit` the changes to the database (so that they are saved). -* `refresh` your instance (so that it contains any new data from the database, like the generated ID). +* 使用`refresh`来刷新您的数据库实例(以便它包含来自数据库的任何新数据,例如生成的 ID)。 ```Python hl_lines="18-24 31-36" {!../../../docs_src/sql_databases/sql_app/crud.py!} @@ -454,39 +455,39 @@ The steps are: !!! tip The SQLAlchemy model for `User` contains a `hashed_password` that should contain a secure hashed version of the password. - But as what the API client provides is the original password, you need to extract it and generate the hashed password in your application. + 但由于 API 客户端提供的是原始密码,因此您需要将其提取并在应用程序中生成散列密码。 - And then pass the `hashed_password` argument with the value to save. + 然后将hashed_password参数与要保存的值一起传递。 -!!! warning - This example is not secure, the password is not hashed. +!!! !!! warning + 此示例不安全,密码未经过哈希处理。 - In a real life application you would need to hash the password and never save them in plaintext. + 在现实生活中的应用程序中,您需要对密码进行哈希处理,并且永远不要以明文形式保存它们。 - For more details, go back to the Security section in the tutorial. + 有关更多详细信息,请返回教程中的安全部分。 - Here we are focusing only on the tools and mechanics of databases. + 在这里,我们只关注数据库的工具和机制。 -!!! tip - Instead of passing each of the keyword arguments to `Item` and reading each one of them from the Pydantic *model*, we are generating a `dict` with the Pydantic *model*'s data with: +!!! !!! tip + 这里不是将每个关键字参数传递给Item并从Pydantic模型中读取每个参数,而是先生成一个字典,其中包含Pydantic模型的数据: `item.dict()` - and then we are passing the `dict`'s key-value pairs as the keyword arguments to the SQLAlchemy `Item`, with: + 然后我们将dict的键值对 作为关键字参数传递给 SQLAlchemy `Item`: `Item(**item.dict())` - And then we pass the extra keyword argument `owner_id` that is not provided by the Pydantic *model*, with: + 然后我们传递 Pydantic模型未提供的额外关键字参数`owner_id`: `Item(**item.dict(), owner_id=user_id)` -## Main **FastAPI** app +## 主**FastAPI**应用程序 -And now in the file `sql_app/main.py` let's integrate and use all the other parts we created before. +现在在`sql_app/main.py`文件中 让我们集成和使用我们之前创建的所有其他部分。 -### Create the database tables +### 创建数据库表 -In a very simplistic way create the database tables: +以非常简单的方式创建数据库表: === "Python 3.9+" @@ -500,27 +501,27 @@ In a very simplistic way create the database tables: {!> ../../../docs_src/sql_databases/sql_app/main.py!} ``` -#### Alembic Note +#### Alembic 注意 -Normally you would probably initialize your database (create tables, etc) with Alembic. +通常你可能会使用 Alembic,来进行格式化数据库(创建表等)。 -And you would also use Alembic for "migrations" (that's its main job). +而且您还可以将 Alembic 用于“迁移”(这是它的主要工作)。 -A "migration" is the set of steps needed whenever you change the structure of your SQLAlchemy models, add a new attribute, etc. to replicate those changes in the database, add a new column, a new table, etc. +“迁移”是每当您更改 SQLAlchemy 模型的结构、添加新属性等以在数据库中复制这些更改、添加新列、新表等时所需的一组步骤。 -You can find an example of Alembic in a FastAPI project in the templates from [Project Generation - Template](../project-generation.md){.internal-link target=_blank}. Specifically in the `alembic` directory in the source code. +您可以在[Project Generation - Template](https://fastapi.tiangolo.com/zh/project-generation/)的模板中找到一个 FastAPI 项目中的 Alembic 示例。 具体在[`alembic`代码目录中](https://github.com/tiangolo/full-stack-fastapi-postgresql/tree/master/%7B%7Bcookiecutter.project_slug%7D%7D/backend/app/alembic/)。 -### Create a dependency +### 创建依赖项 -Now use the `SessionLocal` class we created in the `sql_app/database.py` file to create a dependency. +现在使用我们在`sql_app/database.py`文件中创建的`SessionLocal`来创建依赖项。 -We need to have an independent database session/connection (`SessionLocal`) per request, use the same session through all the request and then close it after the request is finished. +我们需要每个请求有一个独立的数据库会话/连接(`SessionLocal`),在所有请求中使用相同的会话,然后在请求完成后关闭它。 -And then a new session will be created for the next request. +然后将为下一个请求创建一个新会话。 -For that, we will create a new dependency with `yield`, as explained before in the section about [Dependencies with `yield`](dependencies/dependencies-with-yield.md){.internal-link target=_blank}. +为此,我们将创建一个新的依赖项`yield`,正如前面关于[Dependencies with`yield`](https://fastapi.tiangolo.com/zh/tutorial/dependencies/dependencies-with-yield/)的部分中所解释的那样。 -Our dependency will create a new SQLAlchemy `SessionLocal` that will be used in a single request, and then close it once the request is finished. +我们的依赖项将创建一个新的 SQLAlchemy `SessionLocal`,它将在单个请求中使用,然后在请求完成后关闭它。 === "Python 3.9+" @@ -534,18 +535,18 @@ Our dependency will create a new SQLAlchemy `SessionLocal` that will be used in {!> ../../../docs_src/sql_databases/sql_app/main.py!} ``` -!!! info - We put the creation of the `SessionLocal()` and handling of the requests in a `try` block. +!!! !!! info + 我们将`SessionLocal()`请求的创建和处理放在一个`try`块中。 - And then we close it in the `finally` block. + 然后我们在finally块中关闭它。 - This way we make sure the database session is always closed after the request. Even if there was an exception while processing the request. + 通过这种方式,我们确保数据库会话在请求后始终关闭。 即使在处理请求时出现异常。 - But you can't raise another exception from the exit code (after `yield`). See more in [Dependencies with `yield` and `HTTPException`](./dependencies/dependencies-with-yield.md#dependencies-with-yield-and-httpexception){.internal-link target=_blank} + 但是您不能从退出代码中引发另一个异常(在yield之后)。 可以查阅 [Dependencies with yield and HTTPException](https://fastapi.tiangolo.com/zh/tutorial/dependencies/dependencies-with-yield/#dependencies-with-yield-and-httpexception) -And then, when using the dependency in a *path operation function*, we declare it with the type `Session` we imported directly from SQLAlchemy. +*然后,当在路径操作函数*中使用依赖项时,我们使用`Session`,直接从 SQLAlchemy 导入的类型声明它。 -This will then give us better editor support inside the *path operation function*, because the editor will know that the `db` parameter is of type `Session`: +*这将为我们在路径操作函数*中提供更好的编辑器支持,因为编辑器将知道`db`参数的类型`Session`: === "Python 3.9+" @@ -559,14 +560,14 @@ This will then give us better editor support inside the *path operation function {!> ../../../docs_src/sql_databases/sql_app/main.py!} ``` -!!! info "Technical Details" - The parameter `db` is actually of type `SessionLocal`, but this class (created with `sessionmaker()`) is a "proxy" of a SQLAlchemy `Session`, so, the editor doesn't really know what methods are provided. +!!! !!! info "技术细节" + 参数`db`实际上是 type `SessionLocal`,但是这个类(用 创建`sessionmaker()`)是 SQLAlchemy 的“代理” `Session`,所以,编辑器并不真正知道提供了哪些方法。 - But by declaring the type as `Session`, the editor now can know the available methods (`.add()`, `.query()`, `.commit()`, etc) and can provide better support (like completion). The type declaration doesn't affect the actual object. + 但是通过将类型声明为Session,编辑器现在可以知道可用的方法(.add()、.query()、.commit()等)并且可以提供更好的支持(比如完成)。 类型声明不影响实际对象。 -### Create your **FastAPI** *path operations* +### 创建您的**FastAPI** *路径操作* -Now, finally, here's the standard **FastAPI** *path operations* code. +现在,到了最后,编写标准的**FastAPI** *路径操作*代码。 === "Python 3.9+" @@ -580,41 +581,41 @@ Now, finally, here's the standard **FastAPI** *path operations* code. {!> ../../../docs_src/sql_databases/sql_app/main.py!} ``` -We are creating the database session before each request in the dependency with `yield`, and then closing it afterwards. +我们在依赖项中的每个请求之前利用`yield`创建数据库会话,然后关闭它。 -And then we can create the required dependency in the *path operation function*, to get that session directly. +所以我们就可以在*路径操作函数*中创建需要的依赖,就能直接获取会话。 -With that, we can just call `crud.get_user` directly from inside of the *path operation function* and use that session. +这样,我们就可以直接从*路径操作函数*内部调用`crud.get_user`并使用该会话,来进行对数据库操作。 -!!! tip - Notice that the values you return are SQLAlchemy models, or lists of SQLAlchemy models. +!!! !!! tip + 请注意,您返回的值是 SQLAlchemy 模型或 SQLAlchemy 模型列表。 - But as all the *path operations* have a `response_model` with Pydantic *models* / schemas using `orm_mode`, the data declared in your Pydantic models will be extracted from them and returned to the client, with all the normal filtering and validation. + 但是由于所有路径操作的response_model都使用 Pydantic模型/使用orm_mode模式,因此您的 Pydantic 模型中声明的数据将从它们中提取并返回给客户端,并进行所有正常的过滤和验证。 -!!! tip - Also notice that there are `response_models` that have standard Python types like `List[schemas.Item]`. +!!! !!! tip + 另请注意,`response_models`应当是标准 Python 类型,例如`List[schemas.Item]`. - But as the content/parameter of that `List` is a Pydantic *model* with `orm_mode`, the data will be retrieved and returned to the client as normally, without problems. + 但是由于它的内容/参数List是一个 使用orm_mode模式的Pydantic模型,所以数据将被正常检索并返回给客户端,所以没有问题。 -### About `def` vs `async def` +### 关于 `def` 对比 `async def` -Here we are using SQLAlchemy code inside of the *path operation function* and in the dependency, and, in turn, it will go and communicate with an external database. +*在这里,我们在路径操作函数*和依赖项中都使用着 SQLAlchemy 模型,它将与外部数据库进行通信。 -That could potentially require some "waiting". +这会需要一些“等待时间”。 -But as SQLAlchemy doesn't have compatibility for using `await` directly, as would be with something like: +但是由于 SQLAlchemy 不具有`await`直接使用的兼容性,因此类似于: ```Python user = await db.query(User).first() ``` -...and instead we are using: +...相反,我们可以使用: ```Python user = db.query(User).first() ``` -Then we should declare the *path operation functions* and the dependency without `async def`, just with a normal `def`, as: +然后我们应该声明*路径操作函数*和不带 的依赖关系`async def`,只需使用普通的`def`,如下: ```Python hl_lines="2" @app.get("/users/{user_id}", response_model=schemas.User) @@ -623,31 +624,31 @@ def read_user(user_id: int, db: Session = Depends(get_db)): ... ``` -!!! info - If you need to connect to your relational database asynchronously, see [Async SQL (Relational) Databases](../advanced/async-sql-databases.md){.internal-link target=_blank}. +!!! !!! info + 如果您需要异步连接到关系数据库,请参阅[Async SQL (Relational) Databases](https://fastapi.tiangolo.com/zh/advanced/async-sql-databases/) -!!! note "Very Technical Details" - If you are curious and have a deep technical knowledge, you can check the very technical details of how this `async def` vs `def` is handled in the [Async](../async.md#very-technical-details){.internal-link target=_blank} docs. +!!! !!! note "Very Technical Details" + 如果您很好奇并且拥有深厚的技术知识,您可以在[Async](https://fastapi.tiangolo.com/zh/async/#very-technical-details)文档中查看有关如何处理 `async def`于`def`差别的技术细节。 -## Migrations +## 迁移 -Because we are using SQLAlchemy directly and we don't require any kind of plug-in for it to work with **FastAPI**, we could integrate database migrations with Alembic directly. +因为我们直接使用 SQLAlchemy,并且我们不需要任何类型的插件来使用**FastAPI**,所以我们可以直接将数据库迁移至[Alembic](https://alembic.sqlalchemy.org/)进行集成。 -And as the code related to SQLAlchemy and the SQLAlchemy models lives in separate independent files, you would even be able to perform the migrations with Alembic without having to install FastAPI, Pydantic, or anything else. +由于与 SQLAlchemy 和 SQLAlchemy 模型相关的代码位于单独的独立文件中,您甚至可以使用 Alembic 执行迁移,而无需安装 FastAPI、Pydantic 或其他任何东西。 -The same way, you would be able to use the same SQLAlchemy models and utilities in other parts of your code that are not related to **FastAPI**. +同样,您将能够在与**FastAPI**无关的代码的其他部分中使用相同的 SQLAlchemy 模型和实用程序。 -For example, in a background task worker with Celery, RQ, or ARQ. +例如,在具有[Celery](https://docs.celeryq.dev/)、[RQ](https://python-rq.org/)或[ARQ](https://arq-docs.helpmanual.io/)的后台任务工作者中。 -## Review all the files +## 审查所有文件 - Remember you should have a directory named `my_super_project` that contains a sub-directory called `sql_app`. + 最后回顾整个案例,您应该有一个名为的目录`my_super_project`,其中包含一个名为`sql_app`。 -`sql_app` should have the following files: +`sql_app`中应该有以下文件: -* `sql_app/__init__.py`: is an empty file. +* `sql_app/__init__.py`:这是一个空文件。 -* `sql_app/database.py`: +* `sql_app/database.py`: ```Python {!../../../docs_src/sql_databases/sql_app/database.py!} @@ -701,13 +702,13 @@ For example, in a background task worker with @@ -720,74 +721,138 @@ $ uvicorn sql_app.main:app --reload -And then, you can open your browser at http://127.0.0.1:8000/docs. - -And you will be able to interact with your **FastAPI** application, reading data from a real database: - - - -## Interact with the database directly - -If you want to explore the SQLite database (file) directly, independently of FastAPI, to debug its contents, add tables, columns, records, modify data, etc. you can use DB Browser for SQLite. - -It will look like this: - - - -You can also use an online SQLite browser like SQLite Viewer or ExtendsClass. - -## Alternative DB session with middleware - -If you can't use dependencies with `yield` -- for example, if you are not using **Python 3.7** and can't install the "backports" mentioned above for **Python 3.6** -- you can set up the session in a "middleware" in a similar way. - -A "middleware" is basically a function that is always executed for each request, with some code executed before, and some code executed after the endpoint function. - -### Create a middleware - -The middleware we'll add (just a function) will create a new SQLAlchemy `SessionLocal` for each request, add it to the request and then close it once the request is finished. - -=== "Python 3.9+" - - ```Python hl_lines="12-20" - {!> ../../../docs_src/sql_databases/sql_app_py39/alt_main.py!} - ``` - -=== "Python 3.6+" - - ```Python hl_lines="14-22" - {!> ../../../docs_src/sql_databases/sql_app/alt_main.py!} - ``` - -!!! info - We put the creation of the `SessionLocal()` and handling of the requests in a `try` block. - - And then we close it in the `finally` block. - - This way we make sure the database session is always closed after the request. Even if there was an exception while processing the request. - -### About `request.state` - -`request.state` is a property of each `Request` object. It is there to store arbitrary objects attached to the request itself, like the database session in this case. You can read more about it in Starlette's docs about `Request` state. - -For us in this case, it helps us ensure a single database session is used through all the request, and then closed afterwards (in the middleware). - -### Dependencies with `yield` or middleware - -Adding a **middleware** here is similar to what a dependency with `yield` does, with some differences: - -* It requires more code and is a bit more complex. -* The middleware has to be an `async` function. - * If there is code in it that has to "wait" for the network, it could "block" your application there and degrade performance a bit. - * Although it's probably not very problematic here with the way `SQLAlchemy` works. - * But if you added more code to the middleware that had a lot of I/O waiting, it could then be problematic. -* A middleware is run for *every* request. - * So, a connection will be created for every request. - * Even when the *path operation* that handles that request didn't need the DB. - -!!! tip - It's probably better to use dependencies with `yield` when they are enough for the use case. - -!!! info - Dependencies with `yield` were added recently to **FastAPI**. - - A previous version of this tutorial only had the examples with a middleware and there are probably several applications using the middleware for database session management. +打开浏览器进入 http://127.0.0.1:8000/docs。

+ +

+ 您将能够与您的FastAPI应用程序交互,从真实数据库中读取数据: +

+ +

+ +

+ +

+ 直接与数据库交互 +

+ +

+ 如果您想独立于 FastAPI 直接浏览 SQLite 数据库(文件)以调试其内容、添加表、列、记录、修改数据等,您可以使用SQLite 的 DB Browser +

+ +

+ 它看起来像这样: +

+ +

+ +

+ +

+ 您还可以使用SQLite ViewerExtendsClass等在线 SQLite 浏览器。 +

+ +

+ 中间件替代数据库会话 +

+ +

+ 如果你不能使用依赖项yield——例如,如果你没有使用Python 3.7并且不能安装上面提到的Python 3.6的“backports” ——你可以在类似的“中间件”中设置会话方法。 +

+ +

+ “中间件”基本功能是一个为每个请求执行的函数在请求之前进行执行相应的代码,以及在请求执行之后执行相应的代码。 +

+ +

+ 创建中间件 +

+ +

+ 我们将添加中间件(只是一个函数)将为每个请求创建一个新的 SQLAlchemySessionLocal,将其添加到请求中,然后在请求完成后关闭它。 +

+ +

+ === "Python 3.9+" + +

    {!> ../../../docs_src/sql_databases/sql_app_py39/alt_main.py!}
+
+

+ +

+ === "Python 3.6+" + +

    {!> ../../../docs_src/sql_databases/sql_app/alt_main.py!}
+
+

+ +

+ !!! !!! info + 我们将SessionLocal()请求的创建和处理放在一个try块中。 +

+ +
然后我们在finally块中关闭它。
+
+通过这种方式,我们确保数据库会话在请求后始终关闭,即使在处理请求时出现异常也会关闭。 Even if there was an exception while processing the request.
+
+ +

+ 关于request.state +

+ +

+ request.state是每个Request对象的属性。 它用于存储附加到请求本身的任意对象,例如本例中的数据库会话。 您可以在Starlette 的关于Requeststate的文档中了解更多信息。 +

+ +

+ 对于这种情况下,它帮助我们确保在所有请求中使用单个数据库会话,然后关闭(在中间件中)。 +

+ +

+ 使用yield依赖项与使用中间件的区别 +

+ +

+ !!! info + yield的依赖项是最近刚加入FastAPI中的。 +

+ + + +

+ !!! !!! tip + tyield当依赖项 足以满足用例时,使用tyield依赖项方法会更好。 +

+ +

+ !!! 在此处添加中间件yield的依赖项的作用效果类似,但也有一些区别: +

+ +
所以本教程的先前版本只有带有中间件的示例,并且可能有多个应用程序使用中间件进行数据库会话管理。
+
From fc7ccbbab057700719f83dfa862b7939e410dbf3 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:55:08 +0800 Subject: [PATCH 161/163] New translations static-files.md (Chinese Simplified) --- docs/zh/docs/tutorial/static-files.md | 38 +++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/zh/docs/tutorial/static-files.md b/docs/zh/docs/tutorial/static-files.md index 7a0c36af3f4c5..48c361d914e67 100644 --- a/docs/zh/docs/tutorial/static-files.md +++ b/docs/zh/docs/tutorial/static-files.md @@ -1,39 +1,39 @@ -# Static Files +# 静态文件 -You can serve static files automatically from a directory using `StaticFiles`. +您可以使用 `StaticFiles`从目录中自动提供静态文件。 -## Use `StaticFiles` +## 使用`StaticFiles` -* Import `StaticFiles`. -* "Mount" a `StaticFiles()` instance in a specific path. +* 导入`StaticFiles`。 +* "挂载"(Mount) 一个 `StaticFiles()` 实例到一个指定路径。 ```Python hl_lines="2 6" {!../../../docs_src/static_files/tutorial001.py!} ``` -!!! note "Technical Details" - You could also use `from starlette.staticfiles import StaticFiles`. +!!! !!! note "技术细节" + 你也可以用 `from starlette.staticfiles import StaticFiles`。 - **FastAPI** provides the same `starlette.staticfiles` as `fastapi.staticfiles` just as a convenience for you, the developer. But it actually comes directly from Starlette. + **FastAPI** 提供了和 `starlette.staticfiles` 相同的 `fastapi.staticfiles` ,只是为了方便你,开发者。 但它确实来自Starlette。 -### What is "Mounting" +### 什么是"挂载"(Mounting) -"Mounting" means adding a complete "independent" application in a specific path, that then takes care of handling all the sub-paths. +"挂载" 表示在特定路径添加一个完全"独立的"应用,然后负责处理所有子路径。 -This is different from using an `APIRouter` as a mounted application is completely independent. The OpenAPI and docs from your main application won't include anything from the mounted application, etc. +这与使用`APIRouter`不同,因为安装的应用程序是完全独立的。 OpenAPI和来自你主应用的文档不会包含已挂载应用的任何东西等等。 -You can read more about this in the **Advanced User Guide**. +你可以在**高级用户指南**中了解更多。 -## Details +## 细节 -The first `"/static"` refers to the sub-path this "sub-application" will be "mounted" on. So, any path that starts with `"/static"` will be handled by it. +这个 "子应用" 会被 "挂载" 到第一个 `"/static"` 指向的子路径。 因此,任何以`"/static"`开头的路径都会被它处理。 -The `directory="static"` refers to the name of the directory that contains your static files. +`directory="static"` 指向包含你的静态文件的目录名字。 -The `name="static"` gives it a name that can be used internally by **FastAPI**. +`name="static"` 提供了一个能被**FastAPI**内部使用的名字。 -All these parameters can be different than "`static`", adjust them with the needs and specific details of your own application. +所有这些参数可以不同于"`static`",根据你应用的需要和具体细节调整它们。 -## More info +## 更多信息 -For more details and options check Starlette's docs about Static Files. +更多细节和选择查阅 Starlette's docs about Static Files. From aeed60812a725298a3765e153fd520e4e665c086 Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:55:10 +0800 Subject: [PATCH 162/163] New translations testing.md (Chinese Simplified) --- docs/zh/docs/tutorial/testing.md | 121 ++++++++++++++++--------------- 1 file changed, 62 insertions(+), 59 deletions(-) diff --git a/docs/zh/docs/tutorial/testing.md b/docs/zh/docs/tutorial/testing.md index 3609d39d9f5b1..b1e467f73482c 100644 --- a/docs/zh/docs/tutorial/testing.md +++ b/docs/zh/docs/tutorial/testing.md @@ -1,114 +1,117 @@ -# Testing +# 测试 -Thanks to Starlette, testing **FastAPI** applications is easy and enjoyable. +感谢 Starlette,测试**FastAPI** 应用轻松又愉快。 -It is based on HTTPX, which in turn is designed based on Requests, so it's very familiar and intuitive. +它基于 HTTPX, 而HTTPX又是基于Requests设计的,所以很相似且易懂。 -With it, you can use pytest directly with **FastAPI**. +有了它,你可以直接与**FastAPI**一起使用 pytest。 -## Using `TestClient` +## 使用 `TestClient` -!!! info - To use `TestClient`, first install `httpx`. +!!! !!! 信息 + 要使用 `TestClient`,先要安装 `httpx`. - E.g. `pip install httpx`. + E.g. 例:`pip install httpx`. -Import `TestClient`. +导入 `TestClient`. -Create a `TestClient` by passing your **FastAPI** application to it. +通过传入你的**FastAPI**应用创建一个 `TestClient` 。 -Create functions with a name that starts with `test_` (this is standard `pytest` conventions). +创建名字以 `test_` 开头的函数(这是标准的 `pytest` 约定)。 -Use the `TestClient` object the same way as you do with `httpx`. +像使用 `httpx` 那样使用 `TestClient` 对象。 -Write simple `assert` statements with the standard Python expressions that you need to check (again, standard `pytest`). +为你需要检查的地方用标准的Python表达式写个简单的 `assert` 语句(重申,标准的`pytest`)。 ```Python hl_lines="2 12 15-18" {!../../../docs_src/app_testing/tutorial001.py!} ``` -!!! tip - Notice that the testing functions are normal `def`, not `async def`. +!!! !!! 提示 + 注意测试函数是普通的 `def`,不是 `async def`。 - And the calls to the client are also normal calls, not using `await`. + 还有client的调用也是普通的调用,不是用 `await`。 - This allows you to use `pytest` directly without complications. + 这让你可以直接使用 `pytest` 而不会遇到麻烦。 -!!! note "Technical Details" - You could also use `from starlette.testclient import TestClient`. +!!! !!! note "技术细节" + 你也可以用 `from starlette.testclient import TestClient`。 - **FastAPI** provides the same `starlette.testclient` as `fastapi.testclient` just as a convenience for you, the developer. But it comes directly from Starlette. + **FastAPI** 提供了和 `starlette.testclient` 一样的 `fastapi.testclient`,只是为了方便开发者。 但它直接来自Starlette。 -!!! tip - If you want to call `async` functions in your tests apart from sending requests to your FastAPI application (e.g. asynchronous database functions), have a look at the [Async Tests](../advanced/async-tests.md){.internal-link target=_blank} in the advanced tutorial. +!!! !!! 提示 + 除了发送请求之外,如果你还想测试时在FastAPI应用中调用 `async` 函数(例如异步数据库函数), 可以在高级教程中看下 [Async Tests](../advanced/async-tests.md){.internal-link target=_blank} 。 -## Separating tests +## 分离测试 -In a real application, you probably would have your tests in a different file. +在实际应用中,你可能会把你的测试放在另一个文件里。 -And your **FastAPI** application might also be composed of several files/modules, etc. +您的**FastAPI**应用程序也可能由一些文件/模块组成等等。 -### **FastAPI** app file +### **FastAPI** app 文件 -Let's say you have a file structure as described in [Bigger Applications](./bigger-applications.md){.internal-link target=_blank}: +假设你有一个像 [更大的应用](./bigger-applications.md){.internal-link target=_blank} 中所描述的文件结构: ``` . +. ├── app │   ├── __init__.py │   └── main.py ``` -In the file `main.py` you have your **FastAPI** app: +在 `main.py` 文件中你有一个 **FastAPI** app: ```Python {!../../../docs_src/app_testing/main.py!} ``` -### Testing file +### 测试文件 -Then you could have a file `test_main.py` with your tests. It could live on the same Python package (the same directory with a `__init__.py` file): +然后你会有一个包含测试的文件 `test_main.py` 。 app可以像Python包那样存在(一样是目录,但有个 `__init__.py` 文件): ``` hl_lines="5" . +. ├── app │   ├── __init__.py │   ├── main.py │   └── test_main.py ``` -Because this file is in the same package, you can use relative imports to import the object `app` from the `main` module (`main.py`): +因为这文件在同一个包中,所以你可以通过相对导入从 `main` 模块(`main.py`)导入`app`对象: ```Python hl_lines="3" {!../../../docs_src/app_testing/test_main.py!} ``` -...and have the code for the tests just like before. +...然后测试代码和之前一样的。 -## Testing: extended example +## 测试:扩展示例 -Now let's extend this example and add more details to see how to test different parts. +现在让我们扩展这个例子,并添加更多细节,看下如何测试不同部分。 -### Extended **FastAPI** app file +### 扩展后的 **FastAPI** app 文件 -Let's continue with the same file structure as before: +让我们继续之前的文件结构: ``` . +. ├── app │   ├── __init__.py │   ├── main.py │   └── test_main.py ``` -Let's say that now the file `main.py` with your **FastAPI** app has some other **path operations**. +假设现在包含**FastAPI** app的文件 `main.py` 有些其他**路径操作**。 -It has a `GET` operation that could return an error. +有个 `GET` 操作会返回错误。 -It has a `POST` operation that could return several errors. +有个 `POST` 操作会返回一些错误。 -Both *path operations* require an `X-Token` header. +所有*路径操作* 都需要一个`X-Token` 头。 === "Python 3.10+" @@ -130,7 +133,7 @@ Both *path operations* require an `X-Token` header. === "Python 3.10+ non-Annotated" - !!! tip + !!! !!! tip Prefer to use the `Annotated` version if possible. ```Python @@ -139,43 +142,43 @@ Both *path operations* require an `X-Token` header. === "Python 3.6+ non-Annotated" - !!! tip + !!! !!! tip Prefer to use the `Annotated` version if possible. ```Python {!> ../../../docs_src/app_testing/app_b/main.py!} ``` -### Extended testing file +### 扩展后的测试文件 -You could then update `test_main.py` with the extended tests: +然后您可以使用扩展后的测试更新`test_main.py`: ```Python {!> ../../../docs_src/app_testing/app_b/test_main.py!} ``` -Whenever you need the client to pass information in the request and you don't know how to, you can search (Google) how to do it in `httpx`, or even how to do it with `requests`, as HTTPX's design is based on Requests' design. +每当你需要客户端在请求中传递信息,但你不知道如何传递时,你可以通过搜索(谷歌)如何用 `httpx`做,或者是用 `requests` 做,毕竟HTTPX的设计是基于Requests的设计的。 -Then you just do the same in your tests. +接着只需在测试中同样操作。 E.g.: -* To pass a *path* or *query* parameter, add it to the URL itself. -* To pass a JSON body, pass a Python object (e.g. a `dict`) to the parameter `json`. -* If you need to send *Form Data* instead of JSON, use the `data` parameter instead. -* To pass *headers*, use a `dict` in the `headers` parameter. -* For *cookies*, a `dict` in the `cookies` parameter. +* 传一个*路径* 或*查询* 参数,添加到URL上。 +* 传一个JSON体,传一个Python对象(例如一个`dict`)到参数 `json`。 +* 如果你需要发送 *Form Data* 而不是 JSON,使用 `data` 参数。 +* 要发送 *headers*,传 `dict` 给 `headers` 参数。 +* 对于 *cookies*,传 `dict` 给 `cookies` 参数。 -For more information about how to pass data to the backend (using `httpx` or the `TestClient`) check the HTTPX documentation. +关于如何传数据给后端的更多信息 (使用`httpx` 或 `TestClient`),请查阅 HTTPX 文档. -!!! info - Note that the `TestClient` receives data that can be converted to JSON, not Pydantic models. +!!! !!! 信息 + 注意 `TestClient` 接收可以被转化为JSON的数据,而不是Pydantic模型。 - If you have a Pydantic model in your test and you want to send its data to the application during testing, you can use the `jsonable_encoder` described in [JSON Compatible Encoder](encoder.md){.internal-link target=_blank}. + 如果你在测试中有一个Pydantic模型,并且你想在测试时发送它的数据给应用,你可以使用在[JSON Compatible Encoder](encoder.md){.internal-link target=_blank}介绍的`jsonable_encoder` 。 -## Run it +## 运行起来 -After that, you just need to install `pytest`: +之后,你只需要安装 `pytest`:
@@ -187,9 +190,9 @@ $ pip install pytest
-It will detect the files and tests automatically, execute them, and report the results back to you. +他会自动检测文件和测试,执行测试,然后向你报告结果。 -Run the tests with: +执行测试:
From 69732d84d40bc928498bace0e73da94e7634ae0e Mon Sep 17 00:00:00 2001 From: Sefank <12670778+Sefank@users.noreply.github.com> Date: Wed, 26 Jul 2023 18:33:00 +0800 Subject: [PATCH 163/163] New translations index.md (Chinese Simplified) --- docs/zh/docs/tutorial/index.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/zh/docs/tutorial/index.md b/docs/zh/docs/tutorial/index.md index 2753a8e3c9442..e9f55adc42962 100644 --- a/docs/zh/docs/tutorial/index.md +++ b/docs/zh/docs/tutorial/index.md @@ -2,11 +2,11 @@ 本教程将一步步向你展示如何使用 **FastAPI** 的绝大部分特性。 -Each section gradually builds on the previous ones, but it's structured to separate topics, so that you can go directly to any specific one to solve your specific API needs. +各个章节的内容循序渐进,但是又围绕着单独的主题,所以你可以直接跳转到某个章节以解决你的特定需求。 -It is also built to work as a future reference. +本教程同样可以作为将来的参考手册。 -So you can come back and see exactly what you need. +你可以随时回到本教程并查阅你需要的内容。 ## 运行代码 @@ -30,7 +30,7 @@ $ uvicorn main:app --reload
-It is **HIGHLY encouraged** that you write or copy the code, edit it and run it locally. +**强烈建议**你在本地编写或复制代码,对其进行编辑并运行。 在编辑器中使用 FastAPI 会真正地展现出它的优势:只需要编写很少的代码,所有的类型检查,代码补全等等。 @@ -54,7 +54,7 @@ $ pip install "fastapi[all]" ......以上安装还包括了 `uvicorn`,你可以将其用作运行代码的服务器。 -!!! !!! note +!!! note "说明" 你也可以分开来安装。 假如你想将应用程序部署到生产环境,你可能要执行以下操作: