Browse Source

🌐 Update Russian translations for existing pages (LLM-generated) (#14123)

* Update Russian translations for modified pages

* docs: fix translation for multiprocessing

* Update Russian translations for other existing pages

* Apply changes from latest PRs: 13917 and 14099

---------

Co-authored-by: vldmrdev <[email protected]>
pull/14136/head
Motov Yurii 4 days ago
committed by GitHub
parent
commit
977abe2396
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      docs/ru/docs/about/index.md
  2. 30
      docs/ru/docs/advanced/additional-status-codes.md
  3. 14
      docs/ru/docs/advanced/async-tests.md
  4. 8
      docs/ru/docs/advanced/index.md
  5. 20
      docs/ru/docs/advanced/response-change-status-code.md
  6. 31
      docs/ru/docs/advanced/response-cookies.md
  7. 12
      docs/ru/docs/advanced/response-directly.md
  8. 42
      docs/ru/docs/advanced/websockets.md
  9. 456
      docs/ru/docs/alternatives.md
  10. 481
      docs/ru/docs/async.md
  11. 45
      docs/ru/docs/benchmarks.md
  12. 322
      docs/ru/docs/deployment/concepts.md
  13. 639
      docs/ru/docs/deployment/docker.md
  14. 248
      docs/ru/docs/deployment/https.md
  15. 10
      docs/ru/docs/deployment/index.md
  16. 178
      docs/ru/docs/deployment/manually.md
  17. 44
      docs/ru/docs/deployment/versions.md
  18. 23
      docs/ru/docs/environment-variables.md
  19. 18
      docs/ru/docs/fastapi-cli.md
  20. 135
      docs/ru/docs/features.md
  21. 242
      docs/ru/docs/help-fastapi.md
  22. 28
      docs/ru/docs/history-design-future.md
  23. 321
      docs/ru/docs/index.md
  24. 2
      docs/ru/docs/learn/index.md
  25. 112
      docs/ru/docs/project-generation.md
  26. 481
      docs/ru/docs/python-types.md
  27. 80
      docs/ru/docs/tutorial/background-tasks.md
  28. 53
      docs/ru/docs/tutorial/bigger-applications.md
  29. 21
      docs/ru/docs/tutorial/body-fields.md
  30. 32
      docs/ru/docs/tutorial/body-multiple-params.md
  31. 100
      docs/ru/docs/tutorial/body-nested-models.md
  32. 42
      docs/ru/docs/tutorial/body-updates.md
  33. 148
      docs/ru/docs/tutorial/body.md
  34. 12
      docs/ru/docs/tutorial/cookie-param-models.md
  35. 24
      docs/ru/docs/tutorial/cookie-params.md
  36. 57
      docs/ru/docs/tutorial/cors.md
  37. 10
      docs/ru/docs/tutorial/debugging.md
  38. 123
      docs/ru/docs/tutorial/dependencies/classes-as-dependencies.md
  39. 18
      docs/ru/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
  40. 183
      docs/ru/docs/tutorial/dependencies/dependencies-with-yield.md
  41. 10
      docs/ru/docs/tutorial/dependencies/global-dependencies.md
  42. 194
      docs/ru/docs/tutorial/dependencies/index.md
  43. 18
      docs/ru/docs/tutorial/dependencies/sub-dependencies.md
  44. 12
      docs/ru/docs/tutorial/encoder.md
  45. 18
      docs/ru/docs/tutorial/extra-data-types.md
  46. 40
      docs/ru/docs/tutorial/extra-models.md
  47. 260
      docs/ru/docs/tutorial/first-steps.md
  48. 34
      docs/ru/docs/tutorial/handling-errors.md
  49. 12
      docs/ru/docs/tutorial/header-param-models.md
  50. 14
      docs/ru/docs/tutorial/header-params.md
  51. 94
      docs/ru/docs/tutorial/index.md
  52. 44
      docs/ru/docs/tutorial/metadata.md
  53. 35
      docs/ru/docs/tutorial/middleware.md
  54. 22
      docs/ru/docs/tutorial/path-operation-configuration.md
  55. 58
      docs/ru/docs/tutorial/path-params-numeric-validations.md
  56. 87
      docs/ru/docs/tutorial/path-params.md
  57. 12
      docs/ru/docs/tutorial/query-param-models.md
  58. 393
      docs/ru/docs/tutorial/query-params-str-validations.md
  59. 46
      docs/ru/docs/tutorial/query-params.md
  60. 39
      docs/ru/docs/tutorial/request-files.md
  61. 18
      docs/ru/docs/tutorial/request-form-models.md
  62. 22
      docs/ru/docs/tutorial/request-forms-and-files.md
  63. 38
      docs/ru/docs/tutorial/request-forms.md
  64. 270
      docs/ru/docs/tutorial/response-model.md
  65. 38
      docs/ru/docs/tutorial/response-status-code.md
  66. 204
      docs/ru/docs/tutorial/schema-extra-example.md
  67. 152
      docs/ru/docs/tutorial/security/first-steps.md
  68. 82
      docs/ru/docs/tutorial/security/get-current-user.md
  69. 68
      docs/ru/docs/tutorial/security/oauth2-jwt.md
  70. 165
      docs/ru/docs/tutorial/security/simple-oauth2.md
  71. 223
      docs/ru/docs/tutorial/sql-databases.md
  72. 18
      docs/ru/docs/tutorial/static-files.md
  73. 40
      docs/ru/docs/tutorial/testing.md
  74. 333
      docs/ru/docs/virtual-environments.md

4
docs/ru/docs/about/index.md

@ -1,3 +1,3 @@
# О проекте
# О проекте { #about }
FastAPI: внутреннее устройство, повлиявшие технологии и всё такое прочее. 🤓
О FastAPI, его дизайне, источниках вдохновения и многом другом. 🤓

30
docs/ru/docs/advanced/additional-status-codes.md

@ -1,28 +1,28 @@
# Дополнительные статус коды
# Дополнительные статус-коды { #additional-status-codes }
По умолчанию **FastAPI** возвращает ответы, используя `JSONResponse`, помещая содержимое, которое вы возвращаете из вашей *операции пути*, внутрь этого `JSONResponse`.
По умолчанию **FastAPI** будет возвращать ответы, используя `JSONResponse`, помещая содержимое, которое вы возвращаете из вашей *операции пути*, внутрь этого `JSONResponse`.
Он будет использовать код статуса по умолчанию или тот, который вы укажете в вашей *операции пути*.
Он будет использовать статус-код по умолчанию или тот, который вы укажете в вашей *операции пути*.
## Дополнительные статус коды
## Дополнительные статус-коды { #additional-status-codes_1 }
Если вы хотите возвращать дополнительный статус код помимо основного, вы можете сделать это, возвращая объект `Response` напрямую, как `JSONResponse`, и устанавливая нужный статус код напрямую.
Если вы хотите возвращать дополнительные статус-коды помимо основного, вы можете сделать это, возвращая `Response` напрямую, например `JSONResponse`, и устанавливая дополнительный статус-код напрямую.
Например, скажем, вы хотите создать *операцию пути*, которая позволяет обновлять элементы и возвращает HTTP-код 200 "OK" при успешном выполнении.
Например, предположим, что вы хотите иметь *операцию пути*, которая позволяет обновлять элементы и возвращает HTTP статус-код 200 «OK» при успешном выполнении.
Но вы также хотите, чтобы она принимала новые элементы. И если элемент ранее не существовал, он создаётся, и возвращался HTTP-код 201 "Created".
Но вы также хотите, чтобы она принимала новые элементы. И если элементы ранее не существовали, она создаёт их и возвращает HTTP статус-код 201 «Created».
Чтобы реализовать это, импортируйте `JSONResponse` и возвращайте ваш контент напрямую, устанавливая нужный `status_code`:
Чтобы добиться этого, импортируйте `JSONResponse` и верните туда свой контент напрямую, установив нужный вам `status_code`:
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
/// warning | Внимание
Когда вы возвращаете объект `Response` напрямую, как в примере выше, он будет возвращён как есть.
Когда вы возвращаете `Response` напрямую, как в примере выше, он будет возвращён как есть.
Он не будет сериализован при помощи модели и т.д.
Он не будет сериализован с помощью модели и т.п.
Убедитесь, что в нём содержатся именно те данные, которые вы хотите, и что значения являются валидным JSON (если вы используете `JSONResponse`).
Убедитесь, что в нём именно те данные, которые вы хотите, и что значения являются валидным JSON (если вы используете `JSONResponse`).
///
@ -30,12 +30,12 @@
Вы также можете использовать `from starlette.responses import JSONResponse`.
**FastAPI** предоставляет тот же `starlette.responses` через `fastapi.responses` просто для вашего удобства, как разработчика. Но большинство доступных Response-классов поступают напрямую из Starlette. То же самое касается и `status`.
**FastAPI** предоставляет тот же `starlette.responses` через `fastapi.responses` просто для вашего удобства как разработчика. Но большинство доступных Response-классов приходят напрямую из Starlette. То же самое со `status`.
///
## OpenAPI и документация API
## OpenAPI и документация API { #openapi-and-api-docs }
Если вы возвращаете дополнительные коды статусов и ответы напрямую, они не будут включены в схему OpenAPI (документацию API), потому что FastAPI не может заранее знать, что вы собираетесь вернуть.
Если вы возвращаете дополнительные статус-коды и ответы напрямую, они не будут включены в схему OpenAPI (документацию API), потому что у FastAPI нет способа заранее знать, что вы собираетесь вернуть.
Но вы можете задокументировать это в вашем коде, используя: [Дополнительные ответы в OpenAPI](additional-responses.md){.internal-link target=_blank}.
Но вы можете задокументировать это в своём коде, используя: [Дополнительные ответы](additional-responses.md){.internal-link target=_blank}.

14
docs/ru/docs/advanced/async-tests.md

@ -1,4 +1,4 @@
# Асинхронное тестирование
# Асинхронное тестирование { #async-tests }
Вы уже видели как тестировать **FastAPI** приложение, используя имеющийся класс `TestClient`. К этому моменту вы видели только как писать тесты в синхронном стиле без использования `async` функций.
@ -6,11 +6,11 @@
Давайте рассмотрим, как мы можем это реализовать.
## pytest.mark.anyio
## pytest.mark.anyio { #pytest-mark-anyio }
Если мы хотим вызывать асинхронные функции в наших тестах, то наши тестовые функции должны быть асинхронными. AnyIO предоставляет для этого отличный плагин, который позволяет нам указывать, какие тестовые функции должны вызываться асинхронно.
## HTTPX
## HTTPX { #httpx }
Даже если **FastAPI** приложение использует обычные функции `def` вместо `async def`, это все равно `async` приложение 'под капотом'.
@ -18,7 +18,7 @@
`TestClient` основан на <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>, и, к счастью, мы можем использовать его (`HTTPX`) напрямую для тестирования API.
## Пример
## Пример { #example }
В качестве простого примера, давайте рассмотрим файловую структуру, схожую с описанной в [Большие приложения](../tutorial/bigger-applications.md){.internal-link target=_blank} и [Тестирование](../tutorial/testing.md){.internal-link target=_blank}:
@ -38,7 +38,7 @@
{* ../../docs_src/async_tests/test_main.py *}
## Запуск тестов
## Запуск тестов { #run-it }
Вы можете запустить свои тесты как обычно:
@ -52,7 +52,7 @@ $ pytest
</div>
## Подробнее
## Подробнее { #in-detail }
Маркер `@pytest.mark.anyio` говорит pytest, что тестовая функция должна быть вызвана асинхронно:
@ -88,7 +88,7 @@ response = client.get('/')
///
## Вызов других асинхронных функций
## Вызов других асинхронных функций { #other-asynchronous-function-calls }
Теперь тестовая функция стала асинхронной, поэтому внутри нее вы можете вызывать также и другие `async` функции, не связанные с отправлением запросов в ваше FastAPI приложение. Как если бы вы вызывали их в любом другом месте вашего кода.

8
docs/ru/docs/advanced/index.md

@ -1,12 +1,12 @@
# Расширенное руководство пользователя
# Расширенное руководство пользователя { #advanced-user-guide }
## Дополнительные возможности
## Дополнительные возможности { #additional-features }
Основное [Учебник - Руководство пользователя](../tutorial/index.md){.internal-link target=_blank} должно быть достаточно, чтобы познакомить вас со всеми основными функциями **FastAPI**.
В следующих разделах вы увидите другие варианты, конфигурации и дополнительные возможности.
/// tip
/// tip | Совет
Следующие разделы **не обязательно являются "продвинутыми"**.
@ -14,7 +14,7 @@
///
## Сначала прочитайте Учебник - Руководство пользователя
## Сначала прочитайте Учебник - Руководство пользователя { #read-the-tutorial-first }
Вы все еще можете использовать большинство функций **FastAPI** со знаниями из [Учебник - Руководство пользователя](../tutorial/index.md){.internal-link target=_blank}.

20
docs/ru/docs/advanced/response-change-status-code.md

@ -1,22 +1,22 @@
# Response - Изменение cтатус кода
# Response - Изменение статус-кода { #response-change-status-code }
Вы, вероятно, уже читали о том, что можно установить [Состояние ответа по умолчанию](../tutorial/response-status-code.md){.internal-link target=_blank}.
Вы, вероятно, уже читали о том, что можно установить [статус-код ответа по умолчанию](../tutorial/response-status-code.md){.internal-link target=_blank}.
Но в некоторых случаях вам нужно вернуть код состояния, отличный от установленного по умолчанию.
Но в некоторых случаях нужно вернуть другой статус-код, отличный от значения по умолчанию.
## Пример использования
## Пример использования { #use-case }
Например, представьте, что вы хотите возвращать HTTP код состояния "OK" `200` по умолчанию.
Например, представьте, что вы хотите по умолчанию возвращать HTTP статус-код «OK» `200`.
Но если данные не существовали, вы хотите создать их и вернуть HTTP код состояния "CREATED" `201`.
Но если данные не существовали, вы хотите создать их и вернуть HTTP статус-код «CREATED» `201`.
При этом вы всё ещё хотите иметь возможность фильтровать и преобразовывать возвращаемые данные с помощью `response_model`.
Для таких случаев вы можете использовать параметр `Response`.
## Использование параметра `Response`
## Использование параметра `Response` { #use-a-response-parameter }
Вы можете объявить параметр типа `Response` в вашей *функции обработки пути* (так же как для cookies и headers).
Вы можете объявить параметр типа `Response` в вашей *функции обработки пути* (как и для cookies и HTTP-заголовков).
И затем вы можете установить `status_code` в этом *временном* объекте ответа.
@ -26,6 +26,6 @@
И если вы объявили `response_model`, он всё равно будет использоваться для фильтрации и преобразования возвращаемого объекта.
**FastAPI** будет использовать этот *временный* ответ для извлечения кода состояния (а также cookies и headers) и поместит их в финальный ответ, который содержит возвращаемое вами значение, отфильтрованное любым `response_model`.
**FastAPI** будет использовать этот *временный* ответ для извлечения статус-кода (а также cookies и HTTP-заголовков) и поместит их в финальный ответ, который содержит возвращаемое вами значение, отфильтрованное любым `response_model`.
Вы также можете объявить параметр `Response` в зависимостях и установить код состояния в них. Но помните, что последнее установленное значение будет иметь приоритет.
Вы также можете объявить параметр `Response` в зависимостях и установить в них статус-код. Но помните, что последнее установленное значение будет иметь приоритет.

31
docs/ru/docs/advanced/response-cookies.md

@ -1,9 +1,8 @@
# Cookies в ответе { #response-cookies }
# Cookies в ответе
## Использование параметра `Response` { #use-a-response-parameter }
## Использование параметра `Response`
Вы можете объявить параметр типа `Response` в вашей функции эндпоинта.
Вы можете объявить параметр типа `Response` в вашей функции-обработчике пути.
Затем установить cookies в этом временном объекте ответа.
@ -13,36 +12,40 @@
Если вы указали `response_model`, он всё равно будет использоваться для фильтрации и преобразования возвращаемого объекта.
**FastAPI** извлечет cookies (а также заголовки и коды состояния) из временного ответа и включит их в окончательный ответ, содержащий ваше возвращаемое значение, отфильтрованное через `response_model`.
**FastAPI** извлечет cookies (а также HTTP-заголовки и статус-код) из временного ответа и включит их в окончательный ответ, содержащий ваше возвращаемое значение, отфильтрованное через `response_model`.
Вы также можете объявить параметр типа Response в зависимостях и устанавливать cookies (и заголовки) там.
Вы также можете объявить параметр типа `Response` в зависимостях и устанавливать cookies (и HTTP-заголовки) там.
## Возвращение `Response` напрямую
## Возвращение `Response` напрямую { #return-a-response-directly }
Вы также можете установить cookies, если возвращаете `Response` напрямую в вашем коде.
Вы также можете установить Cookies, если возвращаете `Response` напрямую в вашем коде.
Для этого создайте объект `Response`, как описано в разделе [Возвращение ответа напрямую](response-directly.md){.target=_blank}.
Для этого создайте объект `Response`, как описано в разделе [Возвращение ответа напрямую](response-directly.md){.internal-link target=_blank}.
Затем установите cookies и верните этот объект:
{* ../../docs_src/response_cookies/tutorial001.py hl[10:12] *}
/// tip | Примечание
Имейте в виду, что если вы возвращаете ответ напрямую, вместо использования параметра `Response`, **FastAPI** отправит его без дополнительной обработки.
/// tip | Совет
Имейте в виду, что если вы возвращаете ответ напрямую, вместо использования параметра `Response`, FastAPI вернёт его напрямую.
Убедитесь, что ваши данные имеют корректный тип. Например, они должны быть совместимы с JSON, если вы используете `JSONResponse`.
Убедитесь, что ваши данные имеют корректный тип. Например, они должны быть совместимы с JSON, если вы возвращаете `JSONResponse`.
Также убедитесь, что вы не отправляете данные, которые должны были быть отфильтрованы через `response_model`.
///
### Дополнительная информация
### Дополнительная информация { #more-info }
/// note | Технические детали
Вы также можете использовать `from starlette.responses import Response` или `from starlette.responses import JSONResponse`.
**FastAPI** предоставляет `fastapi.responses`, которые являются теми же объектами, что и `starlette.responses`, просто для удобства. Однако большинство доступных типов ответов поступает непосредственно из **Starlette**.
Для установки заголовков и cookies `Response` используется часто, поэтому **FastAPI** также предоставляет его через `fastapi.responses`.
И так как `Response` часто используется для установки HTTP-заголовков и cookies, **FastAPI** также предоставляет его как `fastapi.Response`.
///
Чтобы увидеть все доступные параметры и настройки, ознакомьтесь с <a href="https://www.starlette.io/responses/#set-cookie" class="external-link" target="_blank">документацией Starlette</a>.

12
docs/ru/docs/advanced/response-directly.md

@ -1,4 +1,4 @@
# Возврат ответа напрямую
# Возврат ответа напрямую { #return-a-response-directly }
Когда вы создаёте **FastAPI** *операцию пути*, вы можете возвращать из неё любые данные: `dict`, `list`, Pydantic-модель, модель базы данных и т.д.
@ -8,9 +8,9 @@
Но вы можете возвращать `JSONResponse` напрямую из ваших *операций пути*.
Это может быть полезно, например, если нужно вернуть пользовательские заголовки или куки.
Это может быть полезно, например, если нужно вернуть пользовательские HTTP-заголовки или cookie.
## Возврат `Response`
## Возврат `Response` { #return-a-response }
На самом деле, вы можете возвращать любой объект `Response` или его подкласс.
@ -26,7 +26,7 @@
Это даёт вам большую гибкость. Вы можете возвращать любые типы данных, переопределять любые объявления или валидацию данных и т.д.
## Использование `jsonable_encoder` в `Response`
## Использование `jsonable_encoder` в `Response` { #using-the-jsonable-encoder-in-a-response }
Поскольку **FastAPI** не изменяет объект `Response`, который вы возвращаете, вы должны убедиться, что его содержимое готово к отправке.
@ -44,7 +44,7 @@
///
## Возврат пользовательского `Response`
## Возврат пользовательского `Response` { #returning-a-custom-response }
Пример выше показывает все необходимые части, но он пока не очень полезен, так как вы могли бы просто вернуть `item` напрямую, и **FastAPI** поместил бы его в `JSONResponse`, преобразовав в `dict` и т.д. Всё это происходит по умолчанию.
@ -56,7 +56,7 @@
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
## Примечания
## Примечания { #notes }
Когда вы возвращаете объект `Response` напрямую, его данные не валидируются, не преобразуются (не сериализуются) и не документируются автоматически.

42
docs/ru/docs/advanced/websockets.md

@ -1,10 +1,10 @@
# Веб-сокеты
# Веб-сокеты { #websockets }
Вы можете использовать <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" class="external-link" target="_blank">веб-сокеты</a> в **FastAPI**.
## Установка `WebSockets`
## Установка `websockets` { #install-websockets }
Убедитесь, что [виртуальная среда](../virtual-environments.md){.internal-link target=_blank} создана, активируйте её и установите `websockets`:
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его и установили `websockets` (библиотека Python, упрощающая работу с протоколом "WebSocket"):
<div class="termy">
@ -16,31 +16,31 @@ $ pip install websockets
</div>
## Клиент WebSockets
## Клиент WebSockets { #websockets-client }
### Рабочее приложение
### В продакшн { #in-production }
Скорее всего, в вашей реальной продуктовой системе есть фронтенд, реализованный при помощи современных фреймворков React, Vue.js или Angular.
В продакшн у вас, вероятно, есть фронтенд, созданный с помощью современного фреймворка вроде React, Vue.js или Angular.
И наверняка для взаимодействия с бекендом через веб-сокеты вы будете использовать средства фронтенда.
И для взаимодействия с бекендом по WebSocket вы, скорее всего, будете использовать инструменты вашего фронтенда.
Также у вас может быть нативное мобильное приложение, коммуницирующее непосредственно с веб-сокетами на бекенд-сервере.
Также у вас может быть нативное мобильное приложение, которое напрямую, нативным кодом, взаимодействует с вашим WebSocket-бекендом.
Либо вы можете сделать какой-либо другой способ взаимодействия с веб-сокетами.
Либо у вас может быть любой другой способ взаимодействия с WebSocket-эндпоинтом.
---
Но для этого примера мы воспользуемся очень простым HTML документом с небольшими вставками JavaScript кода.
Но для этого примера мы воспользуемся очень простым HTML‑документом с небольшим JavaScript, всё внутри одной длинной строки.
Конечно же это неоптимально, и на практике так делать не стоит.
Конечно же, это неоптимально, и вы бы не использовали это в продакшн.
В реальных приложениях стоит воспользоваться одним из вышеупомянутых способов.
В продакшн у вас был бы один из вариантов выше.
Для примера нам нужен наиболее простой способ, который позволит сосредоточиться на серверной части веб-сокетов и получить рабочий код:
Для примера нам нужен наиболее простой способ, который позволит сосредоточиться на серверной части вебсокетов и получить рабочий код:
{* ../../docs_src/websockets/tutorial001.py hl[2,6:38,41:43] *}
## Создание `websocket`
## Создание `websocket` { #create-a-websocket }
Создайте `websocket` в своем **FastAPI** приложении:
@ -54,7 +54,7 @@ $ pip install websockets
///
## Ожидание и отправка сообщений
## Ожидание и отправка сообщений { #await-for-messages-and-send-messages }
Через эндпоинт веб-сокета вы можете получать и отправлять сообщения.
@ -62,7 +62,7 @@ $ pip install websockets
Вы можете получать и отправлять двоичные, текстовые и JSON данные.
## Проверка в действии
## Проверка в действии { #try-it }
Если ваш файл называется `main.py`, то запустите приложение командой:
@ -96,7 +96,7 @@ $ fastapi dev main.py
И все они будут использовать одно и то же веб-сокет соединение.
## Использование `Depends` и не только
## Использование `Depends` и не только { #using-depends-and-others }
Вы можете импортировать из `fastapi` и использовать в эндпоинте вебсокета:
@ -119,7 +119,7 @@ $ fastapi dev main.py
///
### Веб-сокеты с зависимостями: проверка в действии
### Веб-сокеты с зависимостями: проверка в действии { #try-the-websockets-with-dependencies }
Если ваш файл называется `main.py`, то запустите приложение командой:
@ -150,7 +150,7 @@ $ fastapi dev main.py
<img src="/img/tutorial/websockets/image05.png">
## Обработка отключений и работа с несколькими клиентами
## Обработка отключений и работа с несколькими клиентами { #handling-disconnections-and-multiple-clients }
Если веб-сокет соединение закрыто, то `await websocket.receive_text()` вызовет исключение `WebSocketDisconnect`, которое можно поймать и обработать как в этом примере:
@ -168,7 +168,7 @@ $ fastapi dev main.py
Client #1596980209979 left the chat
```
/// tip | Примечание
/// tip | Подсказка
Приложение выше - это всего лишь простой минимальный пример, демонстрирующий обработку и передачу сообщений нескольким веб-сокет соединениям.
@ -178,7 +178,7 @@ Client #1596980209979 left the chat
///
## Дополнительная информация
## Дополнительная информация { #more-info }
Для более глубокого изучения темы воспользуйтесь документацией Starlette:

456
docs/ru/docs/alternatives.md

@ -1,104 +1,94 @@
# Альтернативы, источники вдохновения и сравнения
# Альтернативы, источники вдохновения и сравнения { #alternatives-inspiration-and-comparisons }
Что вдохновило на создание **FastAPI**, сравнение его с альтернативами и чему он научился у них.
Что вдохновило **FastAPI**, сравнение с альтернативами и чему он у них научился.
## Введение
## Введение { #intro }
**FastAPI** не существовал бы, если б не было более ранних работ других людей.
**FastAPI** не существовал бы без предыдущих работ других людей.
Они создали большое количество инструментов, которые вдохновили меня на создание **FastAPI**.
Было создано множество инструментов, которые вдохновили на его появление.
Я всячески избегал создания нового фреймворка в течение нескольких лет.
Сначала я пытался собрать все нужные функции, которые ныне есть в **FastAPI**, используя множество различных фреймворков, плагинов и инструментов.
Я несколько лет избегал создания нового фреймворка. Сначала пытался закрыть все возможности, которые сейчас предоставляет **FastAPI**, с помощью множества разных фреймворков, плагинов и инструментов.
Но в какой-то момент не осталось другого выбора, кроме как создать что-то, что предоставляло бы все эти функции сразу.
Взять самые лучшие идеи из предыдущих инструментов и, используя новые возможности Python (которых не было до версии 3.6, то есть подсказки типов), объединить их.
Но в какой-то момент не осталось другого варианта, кроме как создать что-то, что включает все эти возможности, взяв лучшие идеи из прежних инструментов и совместив их максимально удачным образом, используя возможности языка, которых прежде не было (аннотации типов в Python 3.6+).
## Предшествующие инструменты
## Предшествующие инструменты { #previous-tools }
### <a href="https://www.djangoproject.com/" class="external-link" target="_blank">Django</a>
### <a href="https://www.djangoproject.com/" class="external-link" target="_blank">Django</a> { #django }
Это самый популярный Python-фреймворк, и он пользуется доверием.
Он используется для создания проектов типа Instagram.
Это самый популярный Python-фреймворк, ему широко доверяют. Он используется для построения систем вроде Instagram.
Django довольно тесно связан с реляционными базами данных (такими как MySQL или PostgreSQL), потому использовать NoSQL базы данных (например, Couchbase, MongoDB, Cassandra и т.п.) в качестве основного хранилища данных - непросто.
Он относительно тесно связан с реляционными базами данных (например, MySQL или PostgreSQL), поэтому использовать NoSQL-базу данных (например, Couchbase, MongoDB, Cassandra и т. п.) в качестве основного хранилища не очень просто.
Он был создан для генерации HTML-страниц на сервере, а не для создания API, используемых современными веб-интерфейсами (React, Vue.js, Angular и т.п.) или другими системами (например, <abbr title="Интернет вещей">IoT</abbr>) взаимодействующими с сервером.
Он был создан для генерации HTML на бэкенде, а не для создания API, используемых современным фронтендом (например, React, Vue.js и Angular) или другими системами (например, устройствами <abbr title="Internet of Things – Интернет вещей">IoT</abbr>), которые с ним общаются.
### <a href="https://www.django-rest-framework.org/" class="external-link" target="_blank">Django REST Framework</a>
### <a href="https://www.django-rest-framework.org/" class="external-link" target="_blank">Django REST Framework</a> { #django-rest-framework }
Фреймворк Django REST был создан, как гибкий инструментарий для создания веб-API на основе Django.
Django REST Framework был создан как гибкий набор инструментов для построения веб-API поверх Django, чтобы улучшить его возможности в части API.
DRF использовался многими компаниями, включая Mozilla, Red Hat и Eventbrite.
Он используется многими компаниями, включая Mozilla, Red Hat и Eventbrite.
Это был один из первых примеров **автоматического документирования API** и это была одна из первых идей, вдохновивших на создание **FastAPI**.
Это был один из первых примеров **автоматической документации API**, и именно эта идея одной из первых вдохновила на «поиск» **FastAPI**.
/// note | Заметка
Django REST Framework был создан Tom Christie.
Он же создал Starlette и Uvicorn, на которых основан **FastAPI**.
Django REST Framework был создан Томом Кристи. Он же создал Starlette и Uvicorn, на которых основан **FastAPI**.
///
/// check | Идея для **FastAPI**
/// check | Вдохновило **FastAPI** на
Должно быть автоматическое создание документации API с пользовательским веб-интерфейсом.
Наличие пользовательского веб-интерфейса с автоматической документацией API.
///
### <a href="https://flask.palletsprojects.com" class="external-link" target="_blank">Flask</a>
### <a href="https://flask.palletsprojects.com" class="external-link" target="_blank">Flask</a> { #flask }
Flask - это "микрофреймворк", в нём нет интеграции с базами данных и многих других вещей, которые предустановлены в Django.
Flask — это «микрофреймворк», он не включает интеграции с базами данных и многие другие вещи, которые в Django идут «из коробки».
Его простота и гибкость дают широкие возможности, такие как использование баз данных NoSQL в качестве основной системы хранения данных.
Эта простота и гибкость позволяет, например, использовать NoSQL-базы в качестве основной системы хранения данных.
Он очень прост, его изучение интуитивно понятно, хотя в некоторых местах документация довольно техническая.
Он очень прост, его относительно легко интуитивно освоить, хотя местами документация довольно техническая.
Flask часто используется и для приложений, которым не нужна база данных, настройки прав доступа для пользователей и прочие из множества функций, предварительно встроенных в Django.
Хотя многие из этих функций могут быть добавлены с помощью плагинов.
Его также часто используют для приложений, которым не нужна база данных, управление пользователями или многие другие функции, предварительно встроенные в Django. Хотя многие из этих возможностей можно добавить плагинами.
Такое разделение на части и то, что это "микрофреймворк", который можно расширить, добавляя необходимые возможности, было ключевой особенностью, которую я хотел сохранить.
Такое разбиение на части и то, что это «микрофреймворк», который можно расширять ровно под нужды, — ключевая особенность, которую хотелось сохранить.
Простота Flask, показалась мне подходящей для создания API.
Но ещё нужно было найти "Django REST Framework" для Flask.
С учётом простоты Flask он казался хорошим вариантом для создания API. Следующим было найти «Django REST Framework» для Flask.
/// check | Идеи для **FastAPI**
/// check | Вдохновило **FastAPI** на
Это будет микрофреймворк. К нему легко будет добавить необходимые инструменты и части.
Быть микро-фреймворком. Облегчить комбинирование необходимых инструментов и компонентов.
Должна быть простая и лёгкая в использовании система маршрутизации запросов.
Иметь простую и удобную систему маршрутизации.
///
### <a href="https://requests.readthedocs.io" class="external-link" target="_blank">Requests</a>
### <a href="https://requests.readthedocs.io" class="external-link" target="_blank">Requests</a> { #requests }
На самом деле **FastAPI** не является альтернативой **Requests**.
Их область применения очень разная.
**FastAPI** на самом деле не альтернатива **Requests**. Их области применения очень различны.
В принципе, можно использовать Requests *внутри* приложения FastAPI.
Обычно Requests используют даже внутри приложения FastAPI.
Но всё же я использовал в FastAPI некоторые идеи из Requests.
И всё же **FastAPI** во многом вдохновлялся Requests.
**Requests** - это библиотека для взаимодействия с API в качестве клиента,
в то время как **FastAPI** - это библиотека для *создания* API (то есть сервера).
**Requests** — это библиотека для взаимодействия с API (как клиент), а **FastAPI** — библиотека для создания API (как сервер).
Они, так или иначе, диаметрально противоположны и дополняют друг друга.
Они, в каком-то смысле, находятся на противоположных концах и дополняют друг друга.
Requests имеет очень простой и понятный дизайн, очень прост в использовании и имеет разумные значения по умолчанию.
И в то же время он очень мощный и настраиваемый.
Requests имеет очень простой и понятный дизайн, им очень легко пользоваться, есть разумные значения по умолчанию. И при этом он очень мощный и настраиваемый.
Вот почему на официальном сайте написано:
Именно поэтому на официальном сайте сказано:
> Requests - один из самых загружаемых пакетов Python всех времен
> Requests — один из самых загружаемых Python-пакетов всех времён
Использовать его очень просто. Например, чтобы выполнить запрос `GET`, Вы бы написали:
Пользоваться им очень просто. Например, чтобы сделать запрос `GET`, вы бы написали:
```Python
response = requests.get("http://example.com/some/url")
```
Противоположная *операция пути* в FastAPI может выглядеть следующим образом:
Соответствующая в FastAPI API-операция пути могла бы выглядеть так:
```Python hl_lines="1"
@app.get("/some/url")
@ -106,428 +96,390 @@ def read_url():
return {"message": "Hello World"}
```
Глядите, как похоже `requests.get(...)` и `@app.get(...)`.
Посмотрите, насколько похожи `requests.get(...)` и `@app.get(...)`.
/// check | Идеи для **FastAPI**
/// check | Вдохновило **FastAPI** на
* Должен быть простой и понятный API.
* Нужно использовать названия HTTP-методов (операций) для упрощения понимания происходящего.
* Должны быть разумные настройки по умолчанию и широкие возможности их кастомизации.
* Иметь простой и понятный API.
* Использовать названия HTTP-методов (операций) напрямую, простым и интуитивным образом.
* Иметь разумные значения по умолчанию, но и мощные настройки.
///
### <a href="https://swagger.io/" class="external-link" target="_blank">Swagger</a> / <a href="https://github.com/OAI/OpenAPI-Specification/" class="external-link" target="_blank">OpenAPI</a>
### <a href="https://swagger.io/" class="external-link" target="_blank">Swagger</a> / <a href="https://github.com/OAI/OpenAPI-Specification/" class="external-link" target="_blank">OpenAPI</a> { #swagger-openapi }
Главной функцией, которую я хотел унаследовать от Django REST Framework, была автоматическая документация API.
Главной возможностью, которую хотелось взять из Django REST Framework, была автоматическая документация API.
Но потом я обнаружил, что существует стандарт документирования API, использующий JSON (или YAML, расширение JSON) под названием Swagger.
Затем я обнаружил, что есть стандарт для документирования API с использованием JSON (или YAML — расширения JSON), под названием Swagger.
И к нему уже был создан пользовательский веб-интерфейс.
Таким образом, возможность генерировать документацию Swagger для API позволила бы использовать этот интерфейс.
И уже существовал веб-интерфейс для Swagger API. Поэтому возможность генерировать документацию Swagger для API позволила бы автоматически использовать этот веб-интерфейс.
В какой-то момент Swagger был передан Linux Foundation и переименован в OpenAPI.
Вот почему, когда говорят о версии 2.0, обычно говорят "Swagger", а для версии 3+ "OpenAPI".
Вот почему, говоря о версии 2.0, обычно говорят «Swagger», а о версии 3+ — «OpenAPI».
/// check | Идеи для **FastAPI**
/// check | Вдохновило **FastAPI** на
Использовать открытые стандарты для спецификаций API вместо самодельных схем.
Использовать открытый стандарт для спецификаций API вместо самодельной схемы.
Совместимость с основанными на стандартах пользовательскими интерфейсами:
И интегрировать основанные на стандартах инструменты пользовательского интерфейса:
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>
* <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>
Они были выбраны за популярность и стабильность.
Но сделав беглый поиск, Вы можете найти десятки альтернативных пользовательских интерфейсов для OpenAPI, которые Вы можете использовать с **FastAPI**.
Эти два инструмента выбраны за популярность и стабильность, но даже при беглом поиске можно найти десятки альтернативных интерфейсов для OpenAPI (которые можно использовать с **FastAPI**).
///
### REST фреймворки для Flask
### REST-фреймворки для Flask { #flask-rest-frameworks }
Существует несколько REST фреймворков для Flask, но потратив время и усилия на их изучение, я обнаружил, что многие из них не обновляются или заброшены и имеют нерешённые проблемы из-за которых они непригодны к использованию.
Существует несколько REST-фреймворков для Flask, но, вложив время и усилия в исследование, я обнаружил, что многие из них прекращены или заброшены, с несколькими нерешёнными Issue (тикет\обращение), из-за которых они непригодны.
### <a href="https://marshmallow.readthedocs.io/en/stable/" class="external-link" target="_blank">Marshmallow</a>
### <a href="https://marshmallow.readthedocs.io/en/stable/" class="external-link" target="_blank">Marshmallow</a> { #marshmallow }
Одной из основных функций, необходимых системам API, является "<abbr title="также называют маршаллингом или преобразованием">сериализация</abbr>" данных, то есть преобразование данных из кода (Python) во что-то, что может быть отправлено по сети.
Например, превращение объекта содержащего данные из базы данных в объект JSON, конвертация объекта `datetime` в строку и т.п.
Одна из основных возможностей, нужных системам API, — «<abbr title="также называемая маршаллингом или преобразованием">сериализация</abbr>» данных, то есть преобразование данных из кода (Python) во что-то, что можно отправить по сети. Например, преобразование объекта с данными из базы в JSON-объект. Преобразование объектов `datetime` в строки и т. п.
Еще одна важная функция, необходимая API — проверка данных, позволяющая убедиться, что данные действительны и соответствуют заданным параметрам.
Как пример, можно указать, что ожидаются данные типа `int`, а не какая-то произвольная строка.
Это особенно полезно для входящих данных.
Ещё одна важная возможность, востребованная API, — валидация данных: убеждаться, что данные валидны с учётом заданных параметров. Например, что какое-то поле — `int`, а не произвольная строка. Это особенно полезно для входящих данных.
Без системы проверки данных Вам пришлось бы прописывать все проверки вручную.
Без системы валидации данных вам пришлось бы выполнять все проверки вручную в коде.
Именно для обеспечения этих функций и была создана Marshmallow.
Это отличная библиотека и я много раз пользовался ею раньше.
Именно для этих возможностей и был создан Marshmallow. Это отличная библиотека, я много ей пользовался раньше.
Но она была создана до того, как появились подсказки типов Python.
Итак, чтобы определить каждую <abbr title="Формат данных">схему</abbr>,
Вам нужно использовать определенные утилиты и классы, предоставляемые Marshmallow.
Но она появилась до того, как в Python появились аннотации типов. Поэтому для определения каждой <abbr title="описание того, как данные должны быть сформированы">схемы</abbr> нужно использовать специальные утилиты и классы, предоставляемые Marshmallow.
/// check | Идея для **FastAPI**
/// check | Вдохновило **FastAPI** на
Использовать код программы для автоматического создания "схем", определяющих типы данных и их проверку.
Использовать код для автоматического определения «схем», задающих типы данных и их валидацию.
///
### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a>
### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a> { #webargs }
Другая немаловажная функция API - <abbr title="чтение и преобразование данных в объекты Python">парсинг</abbr> данных из входящих запросов.
Ещё одна важная возможность для API — <abbr title="чтение и преобразование данных в объекты Python">парсинг</abbr> данных из входящих HTTP-запросов.
Webargs - это инструмент, который был создан для этого и поддерживает несколько фреймворков, включая Flask.
Webargs — это инструмент, созданный для этого поверх нескольких фреймворков, включая Flask.
Для проверки данных он использует Marshmallow и создан теми же авторами.
Он использует Marshmallow для валидации данных. И создан теми же разработчиками.
Это превосходный инструмент и я тоже часто пользовался им до **FastAPI**.
Это отличный инструмент, и я тоже много им пользовался до появления **FastAPI**.
/// info | Информация
Webargs бы создан разработчиками Marshmallow.
Webargs был создан теми же разработчиками, что и Marshmallow.
///
/// check | Идея для **FastAPI**
/// check | Вдохновило **FastAPI** на
Должна быть автоматическая проверка входных данных.
Автоматическую валидацию входящих данных HTTP-запроса.
///
### <a href="https://apispec.readthedocs.io/en/stable/" class="external-link" target="_blank">APISpec</a>
### <a href="https://apispec.readthedocs.io/en/stable/" class="external-link" target="_blank">APISpec</a> { #apispec }
Marshmallow и Webargs осуществляют проверку, анализ и сериализацию данных как плагины.
Marshmallow и Webargs предоставляют валидацию, парсинг и сериализацию как плагины.
Но документации API всё ещё не было. Тогда был создан APISpec.
Но документации всё ещё не было. Тогда появился APISpec.
Это плагин для множества фреймворков, в том числе и для Starlette.
Это плагин для многих фреймворков (есть плагин и для Starlette).
Он работает так - Вы записываете определение схем, используя формат YAML, внутри докстринга каждой функции, обрабатывающей маршрут.
Он работает так: вы пишете определение схемы в формате YAML внутри докстринга каждой функции, обрабатывающей маршрут.
Используя эти докстринги, он генерирует схему OpenAPI.
И он генерирует схемы OpenAPI.
Так это работает для Flask, Starlette, Responder и т.п.
Так это работает во Flask, Starlette, Responder и т. д.
Но теперь у нас возникает новая проблема - наличие постороннего микро-синтаксиса внутри кода Python (большие YAML).
Но у нас снова возникает проблема: появляется микро-синтаксис внутри строки Python (большой YAML).
Редактор кода не особо может помочь в такой парадигме.
А изменив какие-то параметры или схемы для Marshmallow можно забыть отредактировать докстринг с YAML и сгенерированная схема становится недействительной.
Редактор кода мало чем может помочь. И если мы изменим параметры или схемы Marshmallow и забудем также изменить YAML в докстринге, сгенерированная схема устареет.
/// info | Информация
APISpec тоже был создан авторами Marshmallow.
APISpec был создан теми же разработчиками, что и Marshmallow.
///
/// check | Идея для **FastAPI**
/// check | Вдохновило **FastAPI** на
Необходима поддержка открытого стандарта для API - OpenAPI.
Поддержку открытого стандарта для API — OpenAPI.
///
### <a href="https://flask-apispec.readthedocs.io/en/latest/" class="external-link" target="_blank">Flask-apispec</a>
### <a href="https://flask-apispec.readthedocs.io/en/latest/" class="external-link" target="_blank">Flask-apispec</a> { #flask-apispec }
Это плагин для Flask, который связан с Webargs, Marshmallow и APISpec.
Это плагин для Flask, который связывает Webargs, Marshmallow и APISpec.
Он получает информацию от Webargs и Marshmallow, а затем использует APISpec для автоматического создания схемы OpenAPI.
Он использует информацию из Webargs и Marshmallow, чтобы автоматически генерировать схемы OpenAPI с помощью APISpec.
Это отличный, но крайне недооценённый инструмент.
Он должен быть более популярен, чем многие плагины для Flask.
Возможно, это связано с тем, что его документация слишком скудна и абстрактна.
Отличный и недооценённый инструмент. Он заслуживает большей популярности, чем многие плагины для Flask. Возможно, из-за слишком краткой и абстрактной документации.
Он избавил от необходимости писать чужеродный синтаксис YAML внутри докстрингов.
Это решило проблему необходимости писать YAML (ещё один синтаксис) в докстрингах Python.
Такое сочетание Flask, Flask-apispec, Marshmallow и Webargs было моим любимым стеком при построении бэкенда до появления **FastAPI**.
Комбинация Flask, Flask-apispec с Marshmallow и Webargs была моим любимым бэкенд-стеком до создания **FastAPI**.
Использование этого стека привело к созданию нескольких генераторов проектов. Я и некоторые другие команды до сих пор используем их:
Его использование привело к созданию нескольких full-stack генераторов на Flask. Это основные стеки, которые я (и несколько внешних команд) использовали до сих пор:
* <a href="https://github.com/tiangolo/full-stack" class="external-link" target="_blank">https://github.com/tiangolo/full-stack</a>
* <a href="https://github.com/tiangolo/full-stack-flask-couchbase" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchbase</a>
* <a href="https://github.com/tiangolo/full-stack-flask-couchdb" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchdb</a>
Эти генераторы проектов также стали основой для [Генераторов проектов с **FastAPI**](project-generation.md){.internal-link target=_blank}.
И эти же full-stack генераторы стали основой для [Генераторов проектов **FastAPI**](project-generation.md){.internal-link target=_blank}.
/// info | Информация
Как ни странно, но Flask-apispec тоже создан авторами Marshmallow.
Flask-apispec был создан теми же разработчиками, что и Marshmallow.
///
/// check | Идея для **FastAPI**
/// check | Вдохновило **FastAPI** на
Схема OpenAPI должна создаваться автоматически и использовать тот же код, который осуществляет сериализацию и проверку данных.
Автоматическую генерацию схемы OpenAPI из того же кода, который определяет сериализацию и валидацию.
///
### <a href="https://nestjs.com/" class="external-link" target="_blank">NestJS</a><a href="https://angular.io/" class="external-link" target="_blank">Angular</a>)
### <a href="https://nestjs.com/" class="external-link" target="_blank">NestJS</a><a href="https://angular.io/" class="external-link" target="_blank">Angular</a>) { #nestjs-and-angular }
Здесь даже не используется Python. NestJS - этот фреймворк написанный на JavaScript (TypeScript), основанный на NodeJS и вдохновлённый Angular.
Это даже не Python. NestJS — это JavaScript/TypeScript-фреймворк на NodeJS, вдохновлённый Angular.
Он позволяет получить нечто похожее на то, что можно сделать с помощью Flask-apispec.
Он достигает чего-то отчасти похожего на то, что можно сделать с Flask-apispec.
В него встроена система внедрения зависимостей, ещё одна идея взятая от Angular.
Однако требуется предварительная регистрация "внедрений" (как и во всех других известных мне системах внедрения зависимостей), что увеличивает количество и повторяемость кода.
В нём встроена система внедрения зависимостей, вдохновлённая Angular 2. Требуется предварительная регистрация «инжектируемых» компонентов (как и во всех известных мне системах внедрения зависимостей), что добавляет многословности и повторяемости кода.
Так как параметры в нём описываются с помощью типов TypeScript (аналогично подсказкам типов в Python), поддержка редактора работает довольно хорошо.
Поскольку параметры описываются с помощью типов TypeScript (аналог аннотаций типов в Python), поддержка редактора весьма хороша.
Но поскольку данные из TypeScript не сохраняются после компиляции в JavaScript, он не может полагаться на подсказки типов для определения проверки данных, сериализации и документации.
Из-за этого и некоторых дизайнерских решений, для валидации, сериализации и автоматической генерации схем, приходится во многих местах добавлять декораторы.
Таким образом, это становится довольно многословным.
Но так как данные о типах TypeScript не сохраняются после компиляции в JavaScript, он не может полагаться на типы для одновременного определения валидации, сериализации и документации. Из‑за этого и некоторых проектных решений для получения валидации, сериализации и автоматической генерации схем приходится добавлять декораторы во многих местах. В итоге это становится довольно многословным.
Кроме того, он не очень хорошо справляется с вложенными моделями.
Если в запросе имеется объект JSON, внутренние поля которого, в свою очередь, являются вложенными объектами JSON, это не может быть должным образом задокументировано и проверено.
Он плохо справляется с вложенными моделями. Если JSON-тело запроса — это объект JSON, содержащий внутренние поля, которые сами являются вложенными объектами JSON, это нельзя как следует задокументировать и провалидировать.
/// check | Идеи для **FastAPI**
/// check | Вдохновило **FastAPI** на
Нужно использовать подсказки типов, чтоб воспользоваться поддержкой редактора кода.
Использовать типы Python для отличной поддержки в редакторе кода.
Нужна мощная система внедрения зависимостей. Необходим способ для уменьшения повторов кода.
Иметь мощную систему внедрения зависимостей. Найти способ минимизировать повторение кода.
///
### <a href="https://sanic.readthedocs.io/en/latest/" class="external-link" target="_blank">Sanic</a>
### <a href="https://sanic.readthedocs.io/en/latest/" class="external-link" target="_blank">Sanic</a> { #sanic }
Sanic был одним из первых чрезвычайно быстрых Python-фреймворков основанных на `asyncio`.
Он был сделан очень похожим на Flask.
Это был один из первых чрезвычайно быстрых Python-фреймворков на основе `asyncio`. Он был сделан очень похожим на Flask.
/// note | Технические детали
В нём использован <a href="https://github.com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a> вместо стандартного цикла событий `asyncio`, что и сделало его таким быстрым.
Он использовал <a href="https://github.com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a> вместо стандартного цикла `asyncio` в Python. Это и сделало его таким быстрым.
Он явно вдохновил создателей Uvicorn и Starlette, которые в настоящее время быстрее Sanic в открытых бенчмарках.
Он явно вдохновил Uvicorn и Starlette, которые сейчас быстрее Sanic в открытых бенчмарках.
///
/// check | Идеи для **FastAPI**
/// check | Вдохновило **FastAPI** на
Должна быть сумасшедшая производительность.
Поиск способа достичь сумасшедшей производительности.
Для этого **FastAPI** основан на Starlette, самом быстром из доступных фреймворков (по замерам незаинтересованных лиц).
Именно поэтому **FastAPI** основан на Starlette, так как это самый быстрый доступный фреймворк (по данным сторонних бенчмарков).
///
### <a href="https://falconframework.org/" class="external-link" target="_blank">Falcon</a>
### <a href="https://falconframework.org/" class="external-link" target="_blank">Falcon</a> { #falcon }
Falcon - ещё один высокопроизводительный Python-фреймворк.
В нём минимум функций и он создан, чтоб быть основой для других фреймворков, например, Hug.
Falcon — ещё один высокопроизводительный Python-фреймворк, он минималистичен и служит основой для других фреймворков, таких как Hug.
Функции в нём получают два параметра - "запрос к серверу" и "ответ сервера".
Затем Вы "читаете" часть запроса и "пишите" часть ответа.
Из-за такой конструкции невозможно объявить параметры запроса и тела сообщения со стандартными подсказками типов Python в качестве параметров функции.
Он спроектирован так, что функции получают два параметра: «request» и «response». Затем вы «читаете» части из запроса и «пишете» части в ответ. Из‑за такого дизайна невозможно объявить параметры запроса и тело запроса стандартными аннотациями типов Python как параметры функции.
Таким образом, и валидацию данных, и их сериализацию, и документацию нужно прописывать вручную.
Либо эти функции должны быть встроены во фреймворк, сконструированный поверх Falcon, как в Hug.
Такая же особенность присутствует и в других фреймворках, вдохновлённых идеей Falcon, использовать только один объект запроса и один объект ответа.
Поэтому валидация данных, сериализация и документация должны выполняться в коде вручную, не автоматически. Либо должны быть реализованы во фреймворке поверх Falcon, как в Hug. Та же особенность есть и в других фреймворках, вдохновлённых дизайном Falcon — с одним объектом запроса и одним объектом ответа в параметрах.
/// check | Идея для **FastAPI**
/// check | Вдохновило **FastAPI** на
Найдите способы добиться отличной производительности.
Поиск способов получить отличную производительность.
Объявлять параметры `ответа сервера` в функциях, как в Hug.
Вместе с Hug (так как Hug основан на Falcon) вдохновило **FastAPI** объявлять параметр `response` в функциях.
Хотя в FastAPI это необязательно и используется в основном для установки заголовков, куки и альтернативных кодов состояния.
Хотя в FastAPI это необязательно, и используется в основном для установки HTTP-заголовков, cookie и альтернативных статус-кодов.
///
### <a href="https://moltenframework.com/" class="external-link" target="_blank">Molten</a>
### <a href="https://moltenframework.com/" class="external-link" target="_blank">Molten</a> { #molten }
Molten мне попался на начальной стадии написания **FastAPI**. В нём были похожие идеи:
Я обнаружил Molten на ранних этапах создания **FastAPI**. И у него были очень похожие идеи:
* Использование подсказок типов.
* Валидация и документация исходя из этих подсказок.
* Основан на аннотациях типов Python.
* Валидация и документация из этих типов.
* Система внедрения зависимостей.
В нём не используются сторонние библиотеки (такие, как Pydantic) для валидации, сериализации и документации.
Поэтому переиспользовать эти определения типов непросто.
Он не использует стороннюю библиотеку для валидации, сериализации и документации, такую как Pydantic, — у него своя. Поэтому такие определения типов данных будет сложнее переиспользовать.
Также требуется более подробная конфигурация и используется стандарт WSGI, который не предназначен для использования с высокопроизводительными инструментами, такими как Uvicorn, Starlette и Sanic, в отличие от ASGI.
Требуются более многословные конфигурации. И так как он основан на WSGI (вместо ASGI), он не предназначен для использования преимуществ высокой производительности инструментов вроде Uvicorn, Starlette и Sanic.
Его система внедрения зависимостей требует предварительной регистрации, и зависимости определяются, как объявления типов.
Из-за этого невозможно объявить более одного "компонента" (зависимости), который предоставляет определенный тип.
Система внедрения зависимостей требует предварительной регистрации зависимостей, а зависимости разрешаются по объявленным типам. Поэтому невозможно объявить более одного «компонента», предоставляющего определённый тип.
Маршруты объявляются в единственном месте с использованием функций, объявленных в других местах (вместо использования декораторов, в которые могут быть обёрнуты функции, обрабатывающие конкретные ресурсы).
Это больше похоже на Django, чем на Flask и Starlette.
Он разделяет в коде вещи, которые довольно тесно связаны.
Маршруты объявляются в одном месте, используя функции, объявленные в других местах (вместо декораторов, которые можно разместить прямо над функцией, обрабатывающей эндпоинт). Это ближе к тому, как это делает Django, чем Flask (и Starlette). Это разделяет в коде вещи, которые довольно тесно связаны.
/// check | Идея для **FastAPI**
/// check | Вдохновило **FastAPI** на
Определить дополнительные проверки типов данных, используя значения атрибутов модели "по умолчанию".
Это улучшает помощь редактора и раньше это не было доступно в Pydantic.
Определять дополнительные проверки типов данных, используя значение «по умолчанию» атрибутов модели. Это улучшает поддержку в редакторе кода, и раньше этого не было в Pydantic.
Фактически это подтолкнуло на обновление Pydantic для поддержки одинакового стиля проверок (теперь этот функционал уже доступен в Pydantic).
Фактически это вдохновило на обновление частей Pydantic, чтобы поддерживать такой же стиль объявления валидации (вся эта функциональность теперь уже есть в Pydantic).
///
### <a href="https://github.com/hugapi/hug" class="external-link" target="_blank">Hug</a>
### <a href="https://github.com/hugapi/hug" class="external-link" target="_blank">Hug</a> { #hug }
Hug был одним из первых фреймворков, реализовавших объявление параметров API с использованием подсказок типов Python.
Эта отличная идея была использована и другими инструментами.
Hug был одним из первых фреймворков, реализовавших объявление типов параметров API с использованием аннотаций типов Python. Это была отличная идея, которая вдохновила и другие инструменты.
При объявлении параметров вместо стандартных типов Python использовались собственные типы, но всё же это был огромный шаг вперед.
Он использовал собственные типы в объявлениях вместо стандартных типов Python, но это всё равно был огромный шаг вперёд.
Это также был один из первых фреймворков, генерировавших полную API-схему в формате JSON.
Он также был одним из первых фреймворков, генерировавших собственную схему, описывающую весь API в JSON.
Данная схема не придерживалась стандартов вроде OpenAPI и JSON Schema.
Поэтому было бы непросто совместить её с другими инструментами, такими как Swagger UI.
Но опять же, это была очень инновационная идея.
Он не был основан на стандартах вроде OpenAPI и JSON Schema. Поэтому интегрировать его с другими инструментами, такими как Swagger UI, было бы непросто. Но, опять же, это была очень инновационная идея.
Ещё у него есть интересная и необычная функция: используя один и тот же фреймворк можно создавать и API, и <abbr title="Интерфейс командной строки">CLI</abbr>.
У него есть интересная и необычная особенность: с помощью одного и того же фреймворка можно создавать и API, и CLI.
Поскольку он основан на WSGI, старом стандарте для синхронных веб-фреймворков, он не может работать с веб-сокетами и другими модными штуками, но всё равно обладает высокой производительностью.
Так как он основан на предыдущем стандарте для синхронных веб-фреймворков Python (WSGI), он не может работать с WebSocket и прочим, хотя также демонстрирует высокую производительность.
/// info | Информация
Hug создан Timothy Crosley, автором <a href="https://github.com/timothycrosley/isort" class="external-link" target="_blank">`isort`</a>, отличного инструмента для автоматической сортировки импортов в Python-файлах.
Hug был создан Тимоти Кросли, тем же автором <a href="https://github.com/timothycrosley/isort" class="external-link" target="_blank">`isort`</a>, отличного инструмента для автоматической сортировки импортов в файлах Python.
///
/// check | Идеи для **FastAPI**
/// check | Идеи, вдохновившие **FastAPI**
Hug повлиял на создание некоторых частей APIStar и был одним из инструментов, которые я счел наиболее многообещающими, наряду с APIStar.
Hug вдохновил части APIStar и был одним из наиболее многообещающих инструментов, которые я нашёл, наряду с APIStar.
Hug натолкнул на мысли использовать в **FastAPI** подсказки типов Python для автоматического создания схемы, определяющей API и его параметры.
Hug помог вдохновить **FastAPI** использовать аннотации типов Python для объявления параметров и автоматически генерировать схему, определяющую API.
Hug вдохновил **FastAPI** объявить параметр `ответа` в функциях для установки заголовков и куки.
Hug вдохновил **FastAPI** объявлять параметр `response` в функциях для установки HTTP-заголовков и cookie.
///
### <a href="https://github.com/encode/apistar" class="external-link" target="_blank">APIStar</a> (<= 0.5)
### <a href="https://github.com/encode/apistar" class="external-link" target="_blank">APIStar</a> (<= 0.5) { #apistar-0-5 }
Непосредственно перед тем, как принять решение о создании **FastAPI**, я обнаружил **APIStar**.
В нем было почти все, что я искал и у него был отличный дизайн.
Прямо перед решением строить **FastAPI** я нашёл сервер **APIStar**. В нём было почти всё, что я искал, и отличный дизайн.
Это была одна из первых реализаций фреймворка, использующего подсказки типов для объявления параметров и запросов, которые я когда-либо видел (до NestJS и Molten).
Я нашёл его примерно в то же время, что и Hug, но APIStar использовал стандарт OpenAPI.
Это была одна из первых реализаций фреймворка, использующего аннотации типов Python для объявления параметров и запросов (до NestJS и Molten), которые я видел. Я обнаружил его примерно в то же время, что и Hug. Но APIStar использовал стандарт OpenAPI.
В нём были автоматические проверка и сериализация данных и генерация схемы OpenAPI основанные на подсказках типов в нескольких местах.
В нём были автоматические валидация данных, сериализация данных и генерация схемы OpenAPI на основе тех же аннотаций типов в нескольких местах.
При определении схемы тела сообщения не использовались подсказки типов, как в Pydantic, это больше похоже на Marshmallow, поэтому помощь редактора была недостаточно хорошей, но всё же APIStar был лучшим доступным вариантом.
Определение схемы тела запроса не использовало те же аннотации типов Python, как в Pydantic, — это было ближе к Marshmallow, поэтому поддержка редактора была бы хуже, но всё равно APIStar оставался лучшим доступным вариантом.
На тот момент у него были лучшие показатели производительности (проигрывающие только Starlette).
На тот момент у него были лучшие показатели в бенчмарках (его превосходил только Starlette).
Изначально у него не было автоматической документации API для веб-интерфейса, но я знал, что могу добавить к нему Swagger UI.
Сначала у него не было веб‑UI для автоматической документации API, но я знал, что могу добавить к нему Swagger UI.
В APIStar была система внедрения зависимостей, которая тоже требовала предварительную регистрацию компонентов, как и ранее описанные инструменты.
Но, тем не менее, это была отличная штука.
У него была система внедрения зависимостей. Она требовала предварительной регистрации компонентов, как и другие инструменты, обсуждавшиеся выше. Но всё же это была отличная возможность.
Я не смог использовать его в полноценном проекте, так как были проблемы со встраиванием <abbr title="Авторизация и соответствующие допуски к операциям пути (эндпоинтам)">функций безопасности</abbr> в схему OpenAPI, из-за которых невозможно было встроить все функции, применяемые в генераторах проектов на основе Flask-apispec.
Я добавил в свой список задач создание пул-реквеста, добавляющего эту функциональность.
Мне так и не удалось использовать его в полном проекте, поскольку не было интеграции с системой безопасности, поэтому я не мог заменить все возможности, которые имел с full-stack генераторами на основе Flask-apispec. В моём бэклоге было создать пулл-реквест (запрос на изменение), добавляющий эту функциональность.
В дальнейшем фокус проекта сместился.
Затем фокус проекта сместился.
Это больше не был API-фреймворк, так как автор сосредоточился на Starlette.
Это перестал быть веб-фреймворк для API, так как автору нужно было сосредоточиться на Starlette.
Ныне APIStar - это набор инструментов для проверки спецификаций OpenAPI.
Сейчас APIStar — это набор инструментов для валидации спецификаций OpenAPI, а не веб-фреймворк.
/// info | Информация
APIStar был создан Tom Christie. Тот самый парень, который создал:
APIStar был создан Томом Кристи. Тем самым человеком, который создал:
* Django REST Framework
* Starlette (на котором основан **FastAPI**)
* Uvicorn (используемый в Starlette и **FastAPI**)
* Uvicorn (используется Starlette и **FastAPI**)
///
/// check | Идеи для **FastAPI**
/// check | Вдохновило **FastAPI** на
Воплощение.
Существование.
Мне казалось блестящей идеей объявлять множество функций (проверка данных, сериализация, документация) с помощью одних и тех же типов Python, которые при этом обеспечивают ещё и помощь редактора кода.
Идея объявлять сразу несколько вещей (валидацию данных, сериализацию и документацию) с помощью одних и тех же типов Python, которые одновременно обеспечивают отличную поддержку в редакторе кода, показалась мне блестящей.
После долгих поисков среди похожих друг на друга фреймворков и сравнения их различий, APIStar стал самым лучшим выбором.
После долгих поисков похожего фреймворка и тестирования множества альтернатив APIStar был лучшим доступным вариантом.
Но APIStar перестал быть фреймворком для создания веб-сервера, зато появился Starlette, новая и лучшая основа для построения подобных систем.
Это была последняя капля, сподвигнувшая на создание **FastAPI**.
Затем APIStar перестал существовать как сервер, а был создан Starlette — новая и лучшая основа для такой системы. Это стало окончательным вдохновением для создания **FastAPI**.
Я считаю **FastAPI** "духовным преемником" APIStar, улучившим его возможности благодаря урокам, извлечённым из всех упомянутых выше инструментов.
Я считаю **FastAPI** «духовным преемником» APIStar, который улучшает и расширяет возможности, систему типов и другие части, опираясь на уроки от всех этих предыдущих инструментов.
///
## Что используется в **FastAPI**
## Что используется в **FastAPI** { #used-by-fastapi }
### <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> { #pydantic }
### <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>
Pydantic — это библиотека для определения валидации данных, сериализации и документации (с использованием JSON Schema) на основе аннотаций типов Python.
Pydantic - это библиотека для валидации данных, сериализации и документирования (используя JSON Schema), основываясь на подсказках типов Python, что делает его чрезвычайно интуитивным.
Благодаря этому он чрезвычайно интуитивен.
Его можно сравнить с Marshmallow, хотя в бенчмарках Pydantic быстрее, чем Marshmallow.
И он основан на тех же подсказках типов, которые отлично поддерживаются редакторами кода.
Его можно сравнить с Marshmallow. Хотя в бенчмарках он быстрее Marshmallow. И поскольку он основан на тех же аннотациях типов Python, поддержка в редакторе кода отличная.
/// check | **FastAPI** использует Pydantic
/// check | **FastAPI** использует его для
Для проверки данных, сериализации данных и автоматической документации моделей (на основе JSON Schema).
Обработки всей валидации данных, сериализации данных и автоматической документации моделей (на основе JSON Schema).
Затем **FastAPI** берёт эти схемы JSON и помещает их в схему OpenAPI, не касаясь других вещей, которые он делает.
Затем **FastAPI** берёт эти данные JSON Schema и помещает их в OpenAPI, помимо всех прочих функций.
///
### <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a>
### <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> { #starlette }
Starlette - это легковесный <abbr title="Новый стандарт построения асинхронных веб-сервисов Python">ASGI</abbr> фреймворк/набор инструментов, который идеален для построения высокопроизводительных асинхронных сервисов.
Starlette — это лёгкий <abbr title="Новый стандарт построения асинхронных веб-сервисов Python">ASGI</abbr> фреймворк/набор инструментов, идеально подходящий для создания высокопроизводительных asyncio‑сервисов.
Starlette очень простой и интуитивный.
Он разработан таким образом, чтобы быть легко расширяемым и иметь модульные компоненты.
Он очень простой и интуитивный. Спроектирован так, чтобы его было легко расширять, и чтобы компоненты были модульными.
В нём есть:
* Впечатляющая производительность.
* Поддержка веб-сокетов.
* Фоновые задачи.
* Обработка событий при старте и финише приложения.
* Тестовый клиент на основе HTTPX.
* Поддержка CORS, сжатие GZip, статические файлы, потоковая передача данных.
* Поддержка сессий и куки.
* Поддержка WebSocket.
* Фоновые задачи, выполняемые в том же процессе.
* События запуска и завершения.
* Тестовый клиент на базе HTTPX.
* CORS, GZip, статические файлы, потоковые ответы.
* Поддержка сессий и cookie.
* 100% покрытие тестами.
* 100% аннотированный код.
* Несколько жёстких зависимостей.
* 100% кодовой базы с аннотациями типов.
* Мало жёстких зависимостей.
В настоящее время Starlette — самый быстрый из протестированных Python-фреймворков. Его превосходит только Uvicorn, который не фреймворк, а сервер.
В настоящее время Starlette показывает самую высокую скорость среди Python-фреймворков в тестовых замерах.
Быстрее только Uvicorn, который является сервером, а не фреймворком.
Starlette предоставляет весь базовый функционал веб-микрофреймворка.
Starlette обеспечивает весь функционал микрофреймворка, но не предоставляет автоматическую валидацию данных, сериализацию и документацию.
Но он не предоставляет автоматическую валидацию данных, сериализацию или документацию.
**FastAPI** добавляет эти функции используя подсказки типов Python и Pydantic.
Ещё **FastAPI** добавляет систему внедрения зависимостей, утилиты безопасности, генерацию схемы OpenAPI и т.д.
Это одна из главных вещей, которые **FastAPI** добавляет поверх, всё на основе аннотаций типов Python (с использованием Pydantic). Плюс система внедрения зависимостей, утилиты безопасности, генерация схемы OpenAPI и т. д.
/// note | Технические детали
ASGI - это новый "стандарт" разработанный участниками команды Django.
Он пока что не является "стандартом в Python" (то есть принятым PEP), но процесс принятия запущен.
ASGI — это новый «стандарт», разрабатываемый участниками core-команды Django. Он всё ещё не является «стандартом Python» (PEP), хотя процесс идёт.
Тем не менее он уже используется в качестве "стандарта" несколькими инструментами.
Это значительно улучшает совместимость, поскольку Вы можете переключиться с Uvicorn на любой другой ASGI-сервер (например, Daphne или Hypercorn) или Вы можете добавить ASGI-совместимые инструменты, такие как `python-socketio`.
Тем не менее его уже используют как «стандарт» несколько инструментов. Это сильно улучшает совместимость: вы можете заменить Uvicorn на любой другой ASGI-сервер (например, Daphne или Hypercorn) или добавить совместимые с ASGI инструменты, такие как `python-socketio`.
///
/// check | **FastAPI** использует Starlette
/// check | **FastAPI** использует его для
В качестве ядра веб-сервиса для обработки запросов, добавив некоторые функции сверху.
Обработки всех основных веб-частей. Добавляя возможности поверх.
Класс `FastAPI` наследуется напрямую от класса `Starlette`.
Класс `FastAPI` напрямую наследуется от класса `Starlette`.
Таким образом, всё что Вы могли делать со Starlette, Вы можете делать с **FastAPI**, по сути это прокачанный Starlette.
Так что всё, что вы можете сделать со Starlette, вы можете сделать напрямую с **FastAPI**, по сути это «Starlette на стероидах».
///
### <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a>
### <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a> { #uvicorn }
Uvicorn - это молниеносный ASGI-сервер, построенный на uvloop и httptools.
Uvicorn молниеносный ASGI-сервер, построенный на uvloop и httptools.
Uvicorn является сервером, а не фреймворком.
Например, он не предоставляет инструментов для маршрутизации запросов по ресурсам.
Для этого нужна надстройка, такая как Starlette (или **FastAPI**).
Это не веб-фреймворк, а сервер. Например, он не предоставляет инструменты для маршрутизации по путям. Это предоставляет сверху фреймворк, такой как Starlette (или **FastAPI**).
Он рекомендуется в качестве сервера для Starlette и **FastAPI**.
Это рекомендуемый сервер для Starlette и **FastAPI**.
/// check | **FastAPI** рекомендует его
/// check | **FastAPI** рекомендует его как
Как основной сервер для запуска приложения **FastAPI**.
Основной веб-сервер для запуска приложений **FastAPI**.
Вы можете объединить его с Gunicorn, чтобы иметь асинхронный многопроцессный сервер.
Также вы можете использовать опцию командной строки `--workers`, чтобы получить асинхронный многопроцессный сервер.
Узнать больше деталей можно в разделе [Развёртывание](deployment/index.md){.internal-link target=_blank}.
Подробнее см. раздел [Развёртывание](deployment/index.md){.internal-link target=_blank}.
///
## Тестовые замеры и скорость
## Бенчмарки и скорость { #benchmarks-and-speed }
Чтобы понять, сравнить и увидеть разницу между Uvicorn, Starlette и FastAPI, ознакомьтесь с разделом [Тестовые замеры](benchmarks.md){.internal-link target=_blank}.
Чтобы понять, сравнить и увидеть разницу между Uvicorn, Starlette и FastAPI, см. раздел о [Бенчмарках](benchmarks.md){.internal-link target=_blank}.

481
docs/ru/docs/async.md

@ -1,18 +1,18 @@
# Конкурентность и async / await
# Конкурентность и async / await { #concurrency-and-async-await }
Здесь приведена подробная информация об использовании синтаксиса `async def` при написании *функций обработки пути*, а также рассмотрены основы асинхронного программирования, конкурентности и параллелизма.
Подробности о синтаксисе `async def` для *функций-обработчиков пути* и немного фона об асинхронном коде, конкурентности и параллелизме.
## Нет времени?<a name="in-a-hurry"></a>
## Нет времени? { #in-a-hurry }
<abbr title="too long; didn't read (основная мысль)"><strong>TL;DR:</strong></abbr>
<abbr title="too long; didn't read – слишком длинно; не читал"><strong>TL;DR:</strong></abbr>
Допустим, вы используете сторонюю библиотеку, которая требует вызова с ключевым словом `await`:
Если вы используете сторонние библиотеки, которые нужно вызывать с `await`, например:
```Python
results = await some_library()
```
В этом случае *функции обработки пути* необходимо объявлять с использованием синтаксиса `async def`:
Тогда объявляйте *функции-обработчики пути* с `async def`, например:
```Python hl_lines="2"
@app.get('/')
@ -21,18 +21,15 @@ async def read_results():
return results
```
/// note
/// note | Примечание
`await` можно использовать только внутри функций, объявленных с использованием `async def`.
`await` можно использовать только внутри функций, объявленных с `async def`.
///
---
Если вы обращаетесь к сторонней библиотеке, которая с чем-то взаимодействует
(с базой данных, API, файловой системой и т. д.), и не имеет поддержки синтаксиса `await`
(что относится сейчас к большинству библиотек для работы с базами данных), то
объявляйте *функции обработки пути* обычным образом с помощью `def`, например:
Если вы используете стороннюю библиотеку, которая взаимодействует с чем-то (база данных, API, файловая система и т.д.) и не поддерживает использование `await` (сейчас это относится к большинству библиотек для БД), тогда объявляйте *функции-обработчики пути* как обычно, просто с `def`, например:
```Python hl_lines="2"
@app.get('/')
@ -43,310 +40,283 @@ def results():
---
Если вашему приложению (странным образом) не нужно ни с чем взаимодействовать и, соответственно,
ожидать ответа, используйте `async def`.
Если вашему приложению (по какой-то причине) не нужно ни с чем взаимодействовать и ждать ответа, используйте `async def`, даже если внутри не нужен `await`.
---
Если вы не уверены, используйте обычный синтаксис `def`.
Если вы просто не уверены, используйте обычный `def`.
---
**Примечание**: при необходимости можно смешивать `def` и `async def` в *функциях обработки пути*
и использовать в каждом случае наиболее подходящий синтаксис. А FastAPI сделает с этим всё, что нужно.
**Примечание**: вы можете смешивать `def` и `async def` в *функциях-обработчиках пути* столько, сколько нужно, и объявлять каждую так, как лучше для вашего случая. FastAPI сделает с ними всё как надо.
В любом из описанных случаев FastAPI работает асинхронно и очень быстро.
В любом из случаев выше FastAPI всё равно работает асинхронно и очень быстро.
Однако придерживаясь указанных советов, можно получить дополнительную оптимизацию производительности.
Но следуя этим шагам, он сможет выполнить некоторые оптимизации производительности.
## Технические подробности
## Технические подробности { #technical-details }
Современные версии Python поддерживают разработку так называемого **"асинхронного кода"** посредством написания **"сопрограмм"** с использованием синтаксиса **`async` и `await`**.
Современные версии Python поддерживают **«асинхронный код»** с помощью **«сопрограмм»** (coroutines) и синтаксиса **`async` и `await`**.
Ниже разберём эту фразу по частям:
Разберём эту фразу по частям в разделах ниже:
* **Асинхронный код**
* **`async` и `await`**
* **Сопрограммы**
## Асинхронный код
## Асинхронный код { #asynchronous-code }
Асинхронный код означает, что в языке 💬 есть возможность сообщить машине / программе 🤖,
что в определённой точке кода ей 🤖 нужно будет ожидать завершения выполнения *чего-то ещё* в другом месте. Допустим это *что-то ещё* называется "медленный файл" 📝.
Асинхронный код значит, что в языке 💬 есть способ сказать компьютеру/программе 🤖, что в некоторый момент кода ему 🤖 придётся подождать, пока *что-то ещё* где-то в другом месте завершится. Назовём это *что-то ещё* «медленный файл» 📝.
И пока мы ждём завершения работы с "медленным файлом" 📝, компьютер может переключиться для выполнения других задач.
И пока мы ждём завершения работы с «медленныи файлом» 📝, компьютер может заняться другой работой.
Но при каждой возможности компьютер / программа 🤖 будет возвращаться обратно. Например, если он 🤖 опять окажется в режиме ожидания, или когда закончит всю работу. В этом случае компьютер 🤖 проверяет, не завершена ли какая-нибудь из текущих задач.
Затем компьютер/программа 🤖 будет возвращаться каждый раз, когда появится возможность (пока снова где-то идёт ожидание), или когда 🤖 завершит всю текущую работу. И он 🤖 проверит, не завершилась ли какая-либо из задач, которых он ждал, и сделает то, что нужно.
Потом он 🤖 берёт первую выполненную задачу (допустим, наш "медленный файл" 📝) и продолжает работу, производя с ней необходимые действия.
Далее он 🤖 возьмёт первую завершившуюся задачу (скажем, наш «медленный файл» 📝) и продолжит делать с ней то, что требуется.
Вышеупомянутое "что-то ещё", завершения которого приходится ожидать, обычно относится к достаточно "медленным" операциям <abbr title="Ввода-вывода">I/O</abbr> (по сравнению со скоростью работы процессора и оперативной памяти), например:
Это «ожидание чего-то ещё» обычно относится к операциям <abbr title="Input and Output – Ввод/вывод">I/O</abbr>, которые относительно «медленные» (по сравнению со скоростью процессора и оперативной памяти), например ожидание:
* отправка данных от клиента по сети
* получение клиентом данных, отправленных вашей программой по сети
* чтение системой содержимого файла с диска и передача этих данных программе
* запись на диск данных, которые программа передала системе
* обращение к удалённому API
* ожидание завершения операции с базой данных
* получение результатов запроса к базе данных
* и т. д.
* отправки данных клиентом по сети
* получения клиентом данных, отправленных вашей программой по сети
* чтения системой содержимого файла на диске и передачи этих данных вашей программе
* записи на диск содержимого, которое ваша программа передала системе
* операции удалённого API
* завершения операции базы данных
* возврата результатов запроса к базе данных
* и т.д.
Поскольку в основном время тратится на ожидание выполнения операций <abbr title="Ввода-вывода">I/O</abbr>,
их обычно называют операциями, <abbr title="I/O bound">ограниченными скоростью ввода-вывода</abbr>.
Поскольку основное время выполнения уходит на ожидание операций <abbr title="Input and Output – Ввод/вывод">I/O</abbr>, их называют операциями, «ограниченными вводом-выводом» (I/O bound).
Код называют "асинхронным", потому что компьютеру / программе не требуется "синхронизироваться" с медленной задачей и,
будучи в простое, ожидать момента её завершения, с тем чтобы забрать результат и продолжить работу.
Это называется «асинхронным», потому что компьютеру/программе не нужно «синхронизироваться» с медленной задачей, простаивая и выжидая точный момент её завершения, чтобы забрать результат и продолжить работу.
Вместо этого в "асинхронной" системе завершённая задача может немного подождать (буквально несколько микросекунд),
пока компьютер / программа занимается другими важными вещами, с тем чтобы потом вернуться,
забрать результаты выполнения и начать их обрабатывать.
Вместо этого, в «асинхронной» системе, уже завершившаяся задача может немного подождать (несколько микросекунд) в очереди, пока компьютер/программа завершит то, чем занимался, и затем вернётся, чтобы забрать результаты и продолжить работу с ними.
"Синхронное" исполнение (в противовес "асинхронному") также называют <abbr title="sequential">"последовательным"</abbr>,
потому что компьютер / программа последовательно выполняет все требуемые шаги перед тем, как перейти к следующей задаче,
даже если в процессе приходится ждать.
Для «синхронного» (в противоположность «асинхронному») исполнения часто используют термин «последовательный», потому что компьютер/программа выполняет все шаги по порядку, прежде чем переключиться на другую задачу, даже если эти шаги включают ожидание.
### Конкурентность и бургеры
### Конкурентность и бургеры { #concurrency-and-burgers }
Тот **асинхронный** код, о котором идёт речь выше, иногда называют **"конкурентностью"**. Она отличается от **"параллелизма"**.
Та идея **асинхронного** кода, описанная выше, иногда также называется **«конкурентностью»**. Она отличается от **«параллелизма»**.
Да, **конкурентность** и **параллелизм** подразумевают, что разные вещи происходят примерно в одно время.
И **конкурентность**, и **параллелизм** относятся к «разным вещам, происходящим примерно одновременно».
Но внутреннее устройство **конкурентности** и **параллелизма** довольно разное.
Но различия между *конкурентностью* и *параллелизмом* довольно существенные.
Чтобы это понять, представьте такую картину:
Чтобы их увидеть, представьте следующую историю про бургеры:
### Конкурентные бургеры
### Конкурентные бургеры { #concurrent-burgers }
<!-- The gender neutral cook emoji "🧑‍🍳" does not render well in browsers. In the meantime, I'm using a mix of male "👨‍🍳" and female "👩‍🍳" cooks. -->
Вы идёте со своей возлюбленной за фастфудом, вы стоите в очереди, пока кассир принимает заказы у людей перед вами. 😍
Вы идёте со своей возлюбленной 😍 в фастфуд 🍔 и становитесь в очередь, в это время кассир 💁 принимает заказы у посетителей перед вами.
<img src="/img/async/concurrent-burgers/concurrent-burgers-01.png" class="illustration">
Когда наконец подходит очередь, вы заказываете парочку самых вкусных и навороченных бургеров 🍔, один для своей возлюбленной 😍, а другой себе.
Наконец ваша очередь: вы заказываете 2 очень «навороченных» бургера — для вашей возлюбленной и для себя. 🍔🍔
Отдаёте деньги 💸.
<img src="/img/async/concurrent-burgers/concurrent-burgers-02.png" class="illustration">
Кассир 💁 что-то говорит поварам на кухне 👨‍🍳, теперь они знают, какие бургеры нужно будет приготовить 🍔
(но пока они заняты бургерами предыдущих клиентов).
Кассир говорит что-то повару на кухне, чтобы они знали, что нужно приготовить ваши бургеры (хотя сейчас они готовят бургеры для предыдущих клиентов).
Кассир 💁 отдаёт вам чек с номером заказа.
<img src="/img/async/concurrent-burgers/concurrent-burgers-03.png" class="illustration">
В ожидании еды вы идёте со своей возлюбленной 😍 выбрать столик, садитесь и довольно продолжительное время общаетесь 😍
(поскольку ваши бургеры самые навороченные, готовятся они не так быстро ✨🍔✨).
Вы платите. 💸
Сидя за столиком с возлюбленной 😍 в ожидании бургеров 🍔, вы отлично проводите время,
восхищаясь её великолепием, красотой и умом ✨😍✨.
Кассир выдаёт вам номер вашей очереди.
Всё ещё ожидая заказ и болтая со своей возлюбленной 😍, время от времени вы проверяете,
какой номер горит над прилавком, и не подошла ли уже ваша очередь.
<img src="/img/async/concurrent-burgers/concurrent-burgers-04.png" class="illustration">
И вот наконец настаёт этот момент, и вы идёте к стойке, чтобы забрать бургеры 🍔 и вернуться за столик.
Пока вы ждёте, вы вместе со своей возлюбленной идёте и выбираете столик, садитесь и долго болтаете (ваши бургеры очень «навороченные», поэтому им нужно время на приготовление).
Вы со своей возлюбленной 😍 едите бургеры 🍔 и отлично проводите время ✨.
Сидя за столиком со своей возлюбленной в ожидании бургеров, вы можете провести это время, восхищаясь тем, какая она классная, милая и умная ✨😍✨.
<img src="/img/async/concurrent-burgers/concurrent-burgers-05.png" class="illustration">
Пока вы ждёте и разговариваете, время от времени вы поглядываете на номер на табло, чтобы понять, не подошла ли уже ваша очередь.
И вот в какой-то момент ваша очередь наступает. Вы подходите к стойке, забираете свои бургеры и возвращаетесь к столику.
<img src="/img/async/concurrent-burgers/concurrent-burgers-06.png" class="illustration">
Вы со своей возлюбленной едите бургеры и отлично проводите время. ✨
<img src="/img/async/concurrent-burgers/concurrent-burgers-07.png" class="illustration">
/// info | Информация
Прекрасные иллюстрации от <a href="https://www.instagram.com/ketrinadrawsalot" class="external-link" target="_blank">Ketrina Thompson</a>. 🎨
///
---
А теперь представьте, что в этой небольшой истории вы компьютер / программа 🤖.
Представьте, что в этой истории вы — компьютер/программа 🤖.
В очереди вы просто глазеете по сторонам 😴, ждёте и ничего особо "продуктивного" не делаете.
Но очередь движется довольно быстро, поскольку кассир 💁 только принимает заказы (а не занимается приготовлением еды), так что ничего страшного.
Пока вы стоите в очереди, вы просто бездействуете 😴, ждёте своей очереди и не делаете ничего особо «продуктивного». Но очередь движется быстро, потому что кассир только принимает заказы (а не готовит их), так что это нормально.
Когда подходит очередь вы наконец предпринимаете "продуктивные" действия 🤓: просматриваете меню, выбираете в нём что-то, узнаёте, что хочет ваша возлюбленная 😍, собираетесь оплатить 💸, смотрите, какую достали карту, проверяете, чтобы с вас списали верную сумму, и что в заказе всё верно и т. д.
Когда приходит ваша очередь, вы выполняете действительно «продуктивную» работу: просматриваете меню, решаете, чего хотите, учитываете выбор своей возлюбленной, платите, проверяете, что дали правильную купюру/карту, что сумма списана корректно, что в заказе верные позиции и т.д.
И хотя вы всё ещё не получили бургеры 🍔, ваша работа с кассиром 💁 ставится "на паузу" ⏸,
поскольку теперь нужно ждать 🕙, когда заказ приготовят.
Но затем, хотя у вас ещё нет бургеров, ваша «работа» с кассиром поставлена «на паузу» ⏸, потому что нужно подождать 🕙, пока бургеры будут готовы.
Но отойдя с номерком от прилавка, вы садитесь за столик и можете переключить 🔀 внимание
на свою возлюбленную 😍 и "работать" ⏯ 🤓 уже над этим. И вот вы снова очень
"продуктивны" 🤓, мило болтаете вдвоём и всё такое 😍.
Но, отойдя от стойки и сев за столик с номерком, вы можете переключить 🔀 внимание на свою возлюбленную и «поработать» ⏯ 🤓 над этим. Снова очень «продуктивно» — флирт с вашей возлюбленной 😍.
В какой-то момент кассир 💁 поместит на табло ваш номер, подразумевая, что бургеры готовы 🍔, но вы не станете подскакивать как умалишённый, лишь только увидев на экране свою очередь. Вы уверены, что ваши бургеры 🍔 никто не утащит, ведь у вас свой номерок, а у других свой.
Потом кассир 💁 «говорит»: «Я закончил делать бургеры», — выводя ваш номер на табло, но вы не подпрыгиваете как сумасшедший в ту же секунду, как только номер сменился на ваш. Вы знаете, что ваши бургеры никто не украдёт, потому что у вас есть номер вашей очереди, а у других — их.
Поэтому вы подождёте, пока возлюбленная 😍 закончит рассказывать историю (закончите текущую работу ⏯ / задачу в обработке 🤓),
и мило улыбнувшись, скажете, что идёте забирать заказ ⏸.
Поэтому вы дожидаетесь, пока ваша возлюбленная закончит историю (завершится текущая работа ⏯ / выполняемая задача 🤓), мягко улыбаетесь и говорите, что идёте за бургерами ⏸.
И вот вы подходите к стойке 🔀, к первоначальной задаче, которая уже завершена ⏯, берёте бургеры 🍔, говорите спасибо и относите заказ за столик. На этом заканчивается этап / задача взаимодействия с кассой ⏹.
В свою очередь порождается задача "поедание бургеров" 🔀 ⏯, но предыдущая ("получение бургеров") завершена ⏹.
Затем вы идёте к стойке 🔀, к исходной задаче, которая теперь завершена ⏯, забираете бургеры, благодарите и несёте их к столику. На этом шаг/задача взаимодействия со стойкой завершён ⏹. Это, в свою очередь, создаёт новую задачу — «есть бургеры» 🔀 ⏯, но предыдущая «получить бургеры» — завершена ⏹.
### Параллельные бургеры
### Параллельные бургеры { #parallel-burgers }
Теперь представим, что вместо бургерной "Конкурентные бургеры" вы решили сходить в "Параллельные бургеры".
Теперь представим, что это не «Конкурентные бургеры», а «Параллельные бургеры».
И вот вы идёте со своей возлюбленной 😍 отведать параллельного фастфуда 🍔.
Вы идёте со своей возлюбленной за параллельным фастфудом.
Вы становитесь в очередь пока несколько (пусть будет 8) кассиров, которые по совместительству ещё и повары 👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳, принимают заказы у посетителей перед вами.
Вы стоите в очереди, пока несколько (скажем, 8) кассиров, которые одновременно являются поварами, принимают заказы у людей перед вами.
При этом клиенты не отходят от стойки и ждут 🕙 получения еды, поскольку каждый
из 8 кассиров идёт на кухню готовить бургеры 🍔, а только потом принимает следующий заказ.
Все перед вами ждут, пока их бургеры будут готовы, не отходя от стойки, потому что каждый из 8 кассиров сразу идёт готовить бургер перед тем, как принять следующий заказ.
Наконец настаёт ваша очередь, и вы просите два самых навороченных бургера 🍔, один для дамы сердца 😍, а другой себе.
<img src="/img/async/parallel-burgers/parallel-burgers-01.png" class="illustration">
Ни о чём не жалея, расплачиваетесь 💸.
Наконец ваша очередь: вы заказываете 2 очень «навороченных» бургера — для вашей возлюбленной и для себя.
И кассир уходит на кухню 👨‍🍳.
Вы платите 💸.
Вам приходится ждать перед стойкой 🕙, чтобы никто по случайности не забрал ваши бургеры 🍔, ведь никаких номерков у вас нет.
<img src="/img/async/parallel-burgers/parallel-burgers-02.png" class="illustration">
Поскольку вы с возлюбленной 😍 хотите получить заказ вовремя 🕙, и следите за тем, чтобы никто не вклинился в очередь,
у вас не получается уделять должного внимание своей даме сердца 😞.
Кассир уходит на кухню.
Это "синхронная" работа, вы "синхронизированы" с кассиром/поваром 👨‍🍳. Приходится ждать 🕙 у стойки,
когда кассир/повар 👨‍🍳 закончит делать бургеры 🍔 и вручит вам заказ, иначе его случайно может забрать кто-то другой.
Вы ждёте, стоя у стойки 🕙, чтобы никто не забрал ваши бургеры раньше вас, так как никаких номерков нет.
Наконец кассир/повар 👨‍🍳 возвращается с бургерами 🍔 после невыносимо долгого ожидания 🕙 за стойкой.
<img src="/img/async/parallel-burgers/parallel-burgers-03.png" class="illustration">
Вы скорее забираете заказ 🍔 и идёте с возлюбленной 😍 за столик.
Так как вы со своей возлюбленной заняты тем, чтобы никто не встал перед вами и не забрал ваши бургеры, как только они появятся, вы не можете уделить внимание своей возлюбленной. 😞
Там вы просто едите эти бургеры, и на этом всё 🍔 ⏹.
Это «синхронная» работа, вы «синхронизированы» с кассиром/поваром 👨‍🍳. Вам нужно ждать 🕙 и находиться там в точный момент, когда кассир/повар 👨‍🍳 закончит бургеры и вручит их вам, иначе их может забрать кто-то другой.
Вам не особо удалось пообщаться, потому что большую часть времени 🕙 пришлось провести у кассы 😞.
<img src="/img/async/parallel-burgers/parallel-burgers-04.png" class="illustration">
---
Затем ваш кассир/повар 👨‍🍳 наконец возвращается с вашими бургерами, после долгого ожидания 🕙 у стойки.
В описанном сценарии вы компьютер / программа 🤖 с двумя исполнителями (вы и ваша возлюбленная 😍),
на протяжении долгого времени 🕙 вы оба уделяете всё внимание ⏯ задаче "ждать на кассе".
<img src="/img/async/parallel-burgers/parallel-burgers-05.png" class="illustration">
В этом ресторане быстрого питания 8 исполнителей (кассиров/поваров) 👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳.
Хотя в бургерной конкурентного типа было всего два (один кассир и один повар) 💁 👨‍🍳.
Вы берёте бургеры и идёте со своей возлюбленной к столику.
Несмотря на обилие работников, опыт в итоге получился не из лучших 😞.
Вы просто их съедаете — и всё. ⏹
<img src="/img/async/parallel-burgers/parallel-burgers-06.png" class="illustration">
Разговоров и флирта было немного, потому что большую часть времени вы ждали 🕙 у стойки. 😞
/// info | Информация
Прекрасные иллюстрации от <a href="https://www.instagram.com/ketrinadrawsalot" class="external-link" target="_blank">Ketrina Thompson</a>. 🎨
///
---
Так бы выглядел аналог истории про бургерную 🍔 в "параллельном" мире.
В этом сценарии «параллельных бургеров» вы — компьютер/программа 🤖 с двумя процессорами (вы и ваша возлюбленная), оба ждут 🕙 и уделяют внимание ⏯ тому, чтобы «ждать у стойки» 🕙 долгое время.
В ресторане 8 процессоров (кассиров/поваров). Тогда как в «конкурентных бургерах» могло быть только 2 (один кассир и один повар).
Вот более реалистичный пример. Представьте себе банк.
И всё же финальный опыт — не самый лучший. 😞
До недавних пор в большинстве банков было несколько кассиров 👨‍💼👨‍💼👨‍💼👨‍💼 и длинные очереди 🕙🕙🕙🕙🕙🕙🕙🕙.
---
Каждый кассир обслуживал одного клиента, потом следующего 👨‍💼⏯.
Это была параллельная версия истории про бургеры. 🍔
Нужно было долгое время 🕙 стоять перед окошком вместе со всеми, иначе пропустишь свою очередь.
Для более «жизненного» примера представьте банк.
Сомневаюсь, что у вас бы возникло желание прийти с возлюбленной 😍 в банк 🏦 оплачивать налоги.
До недавнего времени в большинстве банков было несколько кассиров 👨‍💼👨‍💼👨‍💼👨‍💼 и длинная очередь 🕙🕙🕙🕙🕙🕙🕙🕙.
### Выводы о бургерах
Все кассиры делают всю работу с одним клиентом за другим 👨‍💼⏯.
В нашей истории про поход в фастфуд за бургерами приходится много ждать 🕙,
поэтому имеет смысл организовать конкурентную систему ⏸🔀⏯.
И вам приходится долго 🕙 стоять в очереди, иначе вы потеряете свою очередь.
И то же самое с большинством веб-приложений.
Вы вряд ли захотите взять свою возлюбленную 😍 с собой, чтобы заняться делами в банке 🏦.
Пользователей очень много, но ваш сервер всё равно вынужден ждать 🕙 запросы по их слабому интернет-соединению.
### Вывод про бургеры { #burger-conclusion }
Потом снова ждать 🕙, пока вернётся ответ.
В этом сценарии «фастфуда с вашей возлюбленной», так как много ожидания 🕙, гораздо логичнее иметь конкурентную систему ⏸🔀⏯.
<!--https://forum.wordreference.com/threads/%D0%9D%D0%BE-%D0%B5%D1%81%D0%BB%D0%B8.3258695/-->
Это ожидание 🕙 измеряется микросекундами, но если всё сложить, то набегает довольно много времени.
Так обстоит дело и с большинством веб-приложений.
Вот почему есть смысл использовать асинхронное ⏸🔀⏯ программирование при построении веб-API.
Очень много пользователей, но ваш сервер ждёт 🕙, пока их не самое хорошее соединение отправит их запросы.
Большинство популярных фреймворков (включая Flask и Django) создавались
до появления в Python новых возможностей асинхронного программирования. Поэтому
их можно разворачивать с поддержкой параллельного исполнения или асинхронного
программирования старого типа, которое не настолько эффективно.
А затем снова ждёт 🕙, пока отправятся ответы.
При том, что основная спецификация асинхронного взаимодействия Python с веб-сервером
(<a href="https://asgi.readthedocs.io" class="external-link" target="_blank">ASGI</a>)
была разработана командой Django для внедрения поддержки веб-сокетов.
Это «ожидание» 🕙 измеряется микросекундами, но если всё сложить, то в сумме получается много ожидания.
Именно асинхронность сделала NodeJS таким популярным (несмотря на то, что он не параллельный),
и в этом преимущество Go как языка программирования.
Вот почему асинхронный ⏸🔀⏯ код очень уместен для веб-API.
И тот же уровень производительности даёт **FastAPI**.
Именно такая асинхронность сделала NodeJS популярным (хотя NodeJS — не параллельный), и это сильная сторона Go как языка программирования.
Поскольку можно использовать преимущества параллелизма и асинхронности вместе,
вы получаете производительность лучше, чем у большинства протестированных NodeJS фреймворков
и на уровне с Go, который является компилируемым языком близким к C <a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">(всё благодаря Starlette)</a>.
Того же уровня производительности вы получаете с **FastAPI**.
### Получается, конкурентность лучше параллелизма?
А так как можно одновременно использовать параллелизм и асинхронность, вы получаете производительность выше, чем у большинства протестированных фреймворков на NodeJS и на уровне Go, который — компилируемый язык, ближе к C <a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">(всё благодаря Starlette)</a>.
Нет! Мораль истории совсем не в этом.
### Конкурентность лучше параллелизма? { #is-concurrency-better-than-parallelism }
Конкурентность отличается от параллелизма. Она лучше в **конкретных** случаях, где много времени приходится на ожидание.
Вот почему она зачастую лучше параллелизма при разработке веб-приложений. Но это не значит, что конкурентность лучше в любых сценариях.
Нет! Мораль истории не в этом.
Давайте посмотрим с другой стороны, представьте такую картину:
Конкурентность отличается от параллелизма. И она лучше в **конкретных** сценариях, где много ожидания. Поэтому при разработке веб-приложений она обычно намного лучше параллелизма. Но не во всём.
> Вам нужно убраться в большом грязном доме.
Чтобы уравновесить это, представьте такую короткую историю:
> Вам нужно убрать большой грязный дом.
*Да, это вся история*.
---
Тут не нужно нигде ждать 🕙, просто есть куча работы в разных частях дома.
Здесь нигде нет ожидания 🕙, просто очень много работы в разных местах дома.
Можно организовать очередь как в примере с бургерами, сначала гостиная, потом кухня,
но это ни на что не повлияет, поскольку вы нигде не ждёте 🕙, а просто трёте да моете.
Можно организовать «очереди» как в примере с бургерами — сначала гостиная, потом кухня, — но так как вы ничего не ждёте 🕙, а просто убираете и убираете, очереди ни на что не повлияют.
И понадобится одинаковое количество времени с очередью (конкурентностью) и без неё,
и работы будет сделано тоже одинаковое количество.
На завершение уйдёт одинаковое время — с очередями (конкурентностью) и без них — и объём выполненной работы будет одинаковым.
Однако в случае, если бы вы могли привести 8 бывших кассиров/поваров, а ныне уборщиков 👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳,
и каждый из них (вместе с вами) взялся бы за свой участок дома,
с такой помощью вы бы закончили намного быстрее, делая всю работу **параллельно**.
Но в этом случае, если бы вы могли привести 8 бывших кассиров/поваров, а теперь — уборщиков, и каждый из них (плюс вы) взял бы свою зону дома для уборки, вы могли бы сделать всю работу **параллельно**, с дополнительной помощью, и завершить гораздо быстрее.
В описанном сценарии каждый уборщик (включая вас) был бы исполнителем, занятым на своём участке работы.
В этом сценарии каждый уборщик (включая вас) был бы процессором, выполняющим свою часть работы.
И поскольку большую часть времени выполнения занимает реальная работа (а не ожидание),
а работу в компьютере делает <abbr title="Центральный процессор (CPU)">ЦП</abbr>,
такие задачи называют <abbr title="CPU bound">ограниченными производительностью процессора</abbr>.
И так как основное время выполнения уходит на реальную работу (а не ожидание), а работу в компьютере выполняет <abbr title="Central Processing Unit – Центральный процессор">CPU</abbr>, такие задачи называют «ограниченными процессором» (CPU bound).
---
Ограничение по процессору проявляется в операциях, где требуется выполнять сложные математические вычисления.
Типичные примеры CPU-bound операций — те, которые требуют сложной математической обработки.
Например:
* Обработка **звука** или **изображений**.
* **Компьютерное зрение**: изображение состоит из миллионов пикселей, в каждом пикселе 3 составляющих цвета,
обработка обычно требует проведения расчётов по всем пикселям сразу.
* **Машинное обучение**: здесь обычно требуется умножение "матриц" и "векторов".
Представьте гигантскую таблицу с числами в Экселе, и все их надо одновременно перемножить.
* **Глубокое обучение**: это область *машинного обучения*, поэтому сюда подходит то же описание.
Просто у вас будет не одна таблица в Экселе, а множество. В ряде случаев используется
специальный процессор для создания и / или использования построенных таким образом моделей.
* Обработка **аудио** или **изображений**.
* **Компьютерное зрение**: изображение состоит из миллионов пикселей, каждый пиксель имеет 3 значения/цвета; обычно требуется вычислить что-то для всех этих пикселей одновременно.
* **Машинное обучение**: обычно требует множества умножений «матриц» и «векторов». Представьте огромную таблицу с числами и умножение всех этих чисел «одновременно».
* **Глубокое обучение**: это подполе Машинного обучения, так что всё вышесказанное применимо. Просто это не одна таблица чисел, а их огромный набор, и во многих случаях вы используете специальный процессор, чтобы строить и/или использовать такие модели.
### Конкурентность + параллелизм: Веб + машинное обучение
### Конкурентность + параллелизм: Веб + Машинное обучение { #concurrency-parallelism-web-machine-learning }
**FastAPI** предоставляет возможности конкуретного программирования,
которое очень распространено в веб-разработке (именно этим славится NodeJS).
С **FastAPI** вы можете использовать преимущества конкурентности, что очень распространено в веб-разработке (это та же основная «фишка» NodeJS).
Кроме того вы сможете использовать все преимущества параллелизма и
<abbr title="multiprocessing">многопроцессорности</abbr> (когда несколько процессов работают параллельно),
если рабочая нагрузка предполагает **ограничение по процессору**,
как, например, в системах машинного обучения. <!--http://new.gramota.ru/spravka/punctum?layout=item&id=58_329-->
Но вы также можете использовать выгоды параллелизма и многопроцессности (когда несколько процессов работают параллельно) для рабочих нагрузок, **ограниченных процессором** (CPU bound), как в системах Машинного обучения.
Необходимо также отметить, что Python является главным языком в области
<abbr title="наука о данных (data science)">**дата-сайенс**</abbr>,
машинного обучения и, особенно, глубокого обучения. Всё это делает FastAPI
отличным вариантом (среди многих других) для разработки веб-API и приложений
в области дата-сайенс / машинного обучения.
Плюс к этому простой факт, что Python — основной язык для **Data Science**, Машинного обучения и особенно Глубокого обучения, делает FastAPI очень хорошим выбором для веб-API и приложений в области Data Science / Машинного обучения (среди многих других).
Как добиться такого параллелизма в эксплуатации описано в разделе [Развёртывание](deployment/index.md){.internal-link target=_blank}.
Как добиться такого параллелизма в продакшн, см. раздел [Развёртывание](deployment/index.md){.internal-link target=_blank}.
## `async` и `await`
## `async` и `await` { #async-and-await }
В современных версиях Python разработка асинхронного кода реализована очень интуитивно.
Он выглядит как обычный "последовательный" код и самостоятельно выполняет "ожидание", когда это необходимо.
В современных версиях Python есть очень интуитивный способ определять асинхронный код. Это делает его похожим на обычный «последовательный» код, а «ожидание» выполняется за вас в нужные моменты.
Если некая операция требует ожидания перед тем, как вернуть результат, и
поддерживает современные возможности Python, код можно написать следующим образом:
Когда есть операция, которой нужно подождать перед тем, как вернуть результат, и она поддерживает эти новые возможности Python, вы можете написать так:
```Python
burgers = await get_burgers(2)
```
Главное здесь слово `await`. Оно сообщает интерпретатору, что необходимо дождаться ⏸
пока `get_burgers(2)` закончит свои дела 🕙, и только после этого сохранить результат в `burgers`.
Зная это, Python может пока переключиться на выполнение других задач 🔀 ⏯
(например получение следующего запроса).<!--http://new.gramota.ru/spravka/buro/search-answer?s=296614-->
Ключ здесь — `await`. Он говорит Python, что нужно подождать ⏸, пока `get_burgers(2)` закончит своё дело 🕙, прежде чем сохранять результат в `burgers`. Благодаря этому Python будет знать, что за это время можно заняться чем-то ещё 🔀 ⏯ (например, принять другой запрос).
Чтобы ключевое слово `await` сработало, оно должно находиться внутри функции,
которая поддерживает асинхронность. Для этого вам просто нужно объявить её как `async def`:
Чтобы `await` работал, он должен находиться внутри функции, которая поддерживает такую асинхронность. Для этого просто объявите её с `async def`:
```Python hl_lines="1"
async def get_burgers(number: int):
# Готовим бургеры по специальному асинхронному рецепту
# Сделать что-то асинхронное, чтобы приготовить бургеры
return burgers
```
@ -355,26 +325,22 @@ async def get_burgers(number: int):
```Python hl_lines="2"
# Это не асинхронный код
def get_sequential_burgers(number: int):
# Готовим бургеры последовательно по шагам
# Сделать что-то последовательное, чтобы приготовить бургеры
return burgers
```
Объявление `async def` указывает интерпретатору, что внутри этой функции
следует ожидать выражений `await`, и что можно поставить выполнение такой функции на "паузу" ⏸ и
переключиться на другие задачи 🔀, с тем чтобы вернуться сюда позже.
С `async def` Python знает, что внутри этой функции нужно учитывать выражения `await` и что выполнение такой функции можно «приостанавливать» ⏸ и идти делать что-то ещё 🔀, чтобы потом вернуться.
Если вы хотите вызвать функцию с `async def`, вам нужно <abbr title="await">"ожидать"</abbr> её.
Поэтому такое не сработает:
Когда вы хотите вызвать функцию, объявленную с `async def`, нужно её «ожидать». Поэтому вот так не сработает:
```Python
# Это не заработает, поскольку get_burgers объявлена с использованием async def
# Это не сработает, потому что get_burgers определена с: async def
burgers = get_burgers(2)
```
---
Если сторонняя библиотека требует вызывать её с ключевым словом `await`,
необходимо писать *функции обработки пути* с использованием `async def`, например:
Итак, если вы используете библиотеку, которую можно вызывать с `await`, вам нужно создать *функцию-обработчик пути*, которая её использует, с `async def`, например:
```Python hl_lines="2-3"
@app.get('/burgers')
@ -383,129 +349,96 @@ async def read_burgers():
return burgers
```
### Технические подробности
### Более технические подробности { #more-technical-details }
Вы могли заметить, что `await` можно использовать только внутри функций, определённых с `async def`.
Как вы могли заметить, `await` может применяться только в функциях, объявленных с использованием `async def`.
Но при этом функции, определённые с `async def`, нужно «ожидать». Значит, функции с `async def` тоже можно вызывать только из функций, определённых с `async def`.
<!--http://new.gramota.ru/spravka/punctum?layout=item&id=58_128-->
Но выполнение такой функции необходимо "ожидать" с помощью `await`.
Это означает, что её можно вызвать только из другой функции, которая тоже объявлена с `async def`.
Так что же с «яйцом и курицей» — как вызвать первую `async` функцию?
Но как же тогда появилась первая <abbr title="или яйцо?🤔">курица</abbr>? В смысле... как нам вызвать первую асинхронную функцию?
Если вы работаете с **FastAPI**, вам не о чем беспокоиться, потому что этой «первой» функцией будет ваша *функция-обработчик пути*, а FastAPI знает, как сделать всё правильно.
При работе с **FastAPI** просто не думайте об этом, потому что "первой" функцией является ваша *функция обработки пути*,
и дальше с этим разберётся FastAPI.
Но если вы хотите использовать `async` / `await` без FastAPI, вы тоже можете это сделать.
Кроме того, если хотите, вы можете использовать синтаксис `async` / `await` и без FastAPI.
### Пишите свой асинхронный код { #write-your-own-async-code }
### Пишите свой асинхронный код
Starlette (и **FastAPI**) основаны на <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, что делает их совместимыми и со стандартной библиотекой Python <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a>, и с <a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a>.
Starlette (и **FastAPI**) основаны на <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, что делает их совместимыми как со стандартной библиотекой <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a> в Python, так и с <a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a>.<!--http://new.gramota.ru/spravka/buro/search-answer?s=285295-->
В частности, вы можете напрямую использовать <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> для продвинутых сценариев конкурентности, где в вашем коде нужны более сложные паттерны.
В частности, вы можете напрямую использовать <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> в тех проектах, где требуется более сложная логика работы с конкурентностью.
И даже если вы не используете FastAPI, вы можете писать свои асинхронные приложения с <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, чтобы они были максимально совместимыми и получали его преимущества (например, *структурную конкурентность*).
Даже если вы не используете FastAPI, вы можете писать асинхронные приложения с помощью <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, чтобы они были максимально совместимыми и получали его преимущества (например *структурную конкурентность*).
Я создал ещё одну библиотеку поверх AnyIO, тонкий слой, чтобы немного улучшить аннотации типов и получить более качественное **автозавершение**, **ошибки прямо в редакторе** и т.д. Там также есть дружелюбное введение и руководство, чтобы помочь вам **понять** и писать **свой собственный асинхронный код**: <a href="https://asyncer.tiangolo.com/" class="external-link" target="_blank">Asyncer</a>. Она особенно полезна, если вам нужно **комбинировать асинхронный код с обычным** (блокирующим/синхронным) кодом.
### Другие виды асинхронного программирования
### Другие формы асинхронного кода { #other-forms-of-asynchronous-code }
Стиль написания кода с `async` и `await` появился в языке Python относительно недавно.
Такой стиль использования `async` и `await` относительно новый в языке.
Но он сильно облегчает работу с асинхронным кодом.
Но он сильно упрощает работу с асинхронным кодом.
Ровно такой же синтаксис (ну или почти такой же) недавно был включён в современные версии JavaScript (в браузере и NodeJS).
Такой же (или почти такой же) синтаксис недавно появился в современных версиях JavaScript (в браузере и NodeJS).
До этого поддержка асинхронного кода была реализована намного сложнее, и его было труднее воспринимать.
До этого работа с асинхронным кодом была заметно сложнее и труднее для понимания.
В предыдущих версиях Python для этого использовались потоки или <a href="https://www.gevent.org/" class="external-link" target="_blank">Gevent</a>. Но такой код намного сложнее понимать, отлаживать и мысленно представлять.
В предыдущих версиях Python можно было использовать потоки или <a href="https://www.gevent.org/" class="external-link" target="_blank">Gevent</a>. Но такой код гораздо сложнее понимать, отлаживать и держать в голове.
Что касается JavaScript (в браузере и NodeJS), раньше там использовали для этой цели
<abbr title="callback">"обратные вызовы"</abbr>. Что выливалось в
"ад обратных вызовов".
В прежних версиях NodeJS/браузерного JavaScript вы бы использовали «callbacks» (обратные вызовы), что приводит к «callback hell» (ад обратных вызовов).
## Сопрограммы
## Сопрограммы { #coroutines }
<abbr title="coroutine">**Корути́на**</abbr> (или же сопрограмма) — это крутое словечко для именования той сущности,
которую возвращает функция `async def`. Python знает, что её можно запустить, как и обычную функцию,
но кроме того сопрограмму можно поставить на паузу ⏸ в том месте, где встретится слово `await`.
**Сопрограмма** (coroutine) — это просто «навороченное» слово для того, что возвращает функция `async def`. Python знает, что это похоже на функцию: её можно запустить, она когда-нибудь завершится, но её выполнение может приостанавливаться ⏸ внутри, когда встречается `await`.
Всю функциональность асинхронного программирования с использованием `async` и `await`
часто обобщают словом "корутины". Они аналогичны <abbr title="Goroutines">"горутинам"</abbr>, ключевой особенности
языка Go.
Часто всю функциональность использования асинхронного кода с `async` и `await` кратко называют «сопрограммами». Это сопоставимо с ключевой особенностью Go — «goroutines».
## Заключение
## Заключение { #conclusion }
В самом начале была такая фраза:
Вернёмся к той же фразе:
> Современные версии Python поддерживают разработку так называемого
**"асинхронного кода"** посредством написания **"сопрограмм"** с использованием
синтаксиса **`async` и `await`**.
> Современные версии Python поддерживают **«асинхронный код»** с помощью **«сопрограмм»** (coroutines) и синтаксиса **`async` и `await`**.
Теперь всё должно звучать понятнее. ✨
Теперь это должно звучать понятнее. ✨
На этом основана работа FastAPI (посредством Starlette), и именно это
обеспечивает его высокую производительность.
Именно это «движет» FastAPI (через Starlette) и обеспечивает столь впечатляющую производительность.
## Очень технические подробности
## Очень технические подробности { #very-technical-details }
/// warning
/// warning | Предупреждение
Этот раздел читать не обязательно.
Скорее всего, этот раздел можно пропустить.
Здесь приводятся подробности внутреннего устройства **FastAPI**.
Здесь — очень технические подробности о том, как **FastAPI** работает «под капотом».
Но если вы обладаете техническими знаниями (корутины, потоки, блокировка и т. д.)
и вам интересно, как FastAPI обрабатывает `async def` в отличие от обычных `def`,
читайте дальше.
Если у вас есть достаточно технических знаний (сопрограммы, потоки, блокировки и т.д.) и вам интересно, как FastAPI обрабатывает `async def` по сравнению с обычным `def`, — вперёд.
///
### Функции обработки пути
### Функции-обработчики пути { #path-operation-functions }
Когда вы объявляете *функцию обработки пути* обычным образом с ключевым словом `def`
вместо `async def`, FastAPI ожидает её выполнения, запустив функцию во внешнем
<abbr title="threadpool">пуле потоков</abbr>, а не напрямую (это бы заблокировало сервер).
Когда вы объявляете *функцию-обработчик пути* обычным `def` вместо `async def`, она запускается во внешнем пуле потоков, который затем «ожидается», вместо прямого вызова (прямой вызов заблокировал бы сервер).
Если ранее вы использовали другой асинхронный фреймворк, который работает иначе,
и привыкли объявлять простые вычислительные *функции* через `def` ради
незначительного прироста скорости (порядка 100 наносекунд), обратите внимание,
что с **FastAPI** вы получите противоположный эффект. В таком случае больше подходит
`async def`, если только *функция обработки пути* не использует код, приводящий
к блокировке <abbr title="Ввод/вывод: чтение и запись на диск, сетевые соединения.">I/O</abbr>.
<!--Уточнить: Не использовать async def, если код приводит к блокировке IO?-->
Если вы пришли из другого async-фреймворка, который работает иначе, и привыкли объявлять тривиальные *функции-обработчики пути*, выполняющие только вычисления, через простой `def` ради крошечной выгоды в производительности (около 100 наносекунд), обратите внимание: в **FastAPI** эффект будет противоположным. В таких случаях лучше использовать `async def`, если только ваши *функции-обработчики пути* не используют код, выполняющий блокирующий <abbr title="Input/Output – Ввод/вывод: чтение или запись на диск, сетевые соединения.">I/O</abbr>.
<!--http://new.gramota.ru/spravka/punctum?layout=item&id=58_285-->
Но в любом случае велика вероятность, что **FastAPI** [окажется быстрее](index.md#_11){.internal-link target=_blank}
другого фреймворка (или хотя бы на уровне с ним).
Тем не менее, в обоих случаях велика вероятность, что **FastAPI** [всё равно будет быстрее](index.md#performance){.internal-link target=_blank} (или как минимум сопоставим) с вашим предыдущим фреймворком.
### Зависимости
### Зависимости { #dependencies }
То же относится к зависимостям. Если это обычная функция `def`, а не `async def`,
она запускается во внешнем пуле потоков.
То же относится к [зависимостям](tutorial/dependencies/index.md){.internal-link target=_blank}. Если зависимость — это обычная функция `def`, а не `async def`, она запускается во внешнем пуле потоков.
### Подзависимости
### Подзависимости { #sub-dependencies }
Вы можете объявить множество ссылающихся друг на друга зависимостей и подзависимостей
(в виде параметров при определении функции). Какие-то будут созданы с помощью `async def`,
другие обычным образом через `def`, и такая схема вполне работоспособна. Функции,
объявленные с помощью `def` будут запускаться на внешнем потоке (из пула),
а не с помощью `await`.
У вас может быть несколько зависимостей и [подзависимостей](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank}, которые требуют друг друга (в виде параметров определений функций): часть из них может быть объявлена с `async def`, а часть — обычным `def`. Всё будет работать, а те, что объявлены обычным `def`, будут вызываться во внешнем потоке (из пула), а не «ожидаться».
### Другие служебные функции
### Другие служебные функции { #other-utility-functions }
Любые другие служебные функции, которые вы вызываете напрямую, можно объявлять
с использованием `def` или `async def`. FastAPI не будет влиять на то, как вы
их запускаете.
Любые другие служебные функции, которые вы вызываете напрямую, можно объявлять обычным `def` или `async def`, и FastAPI не будет влиять на то, как вы их вызываете.
Этим они отличаются от функций, которые FastAPI вызывает самостоятельно:
*функции обработки пути* и зависимости.
В отличие от функций, которые FastAPI вызывает за вас: *функции-обработчики пути* и зависимости.
Если служебная функция объявлена с помощью `def`, она будет вызвана напрямую
(как вы и написали в коде), а не в отдельном потоке. Если же она объявлена с
помощью `async def`, её вызов должен осуществляться с ожиданием через `await`.
Если служебная функция — обычная функция с `def`, она будет вызвана напрямую (как вы и пишете в коде), не в пуле потоков; если функция объявлена с `async def`, тогда при её вызове в вашем коде вы должны использовать `await`.
---
<!--http://new.gramota.ru/spravka/buro/search-answer?s=299749-->
Ещё раз повторим, что все эти технические подробности полезны, только если вы специально их искали.
Снова: это очень технические подробности, полезные, вероятно, только если вы целенаправленно их ищете.
В противном случае просто ознакомьтесь с основными принципами в разделе выше: <a href="#_1">Нет времени?</a>.
Иначе вам достаточно руководствоваться рекомендациями из раздела выше: <a href="#in-a-hurry">Нет времени?</a>.

45
docs/ru/docs/benchmarks.md

@ -1,37 +1,34 @@
# Замеры производительности
# Бенчмарки (тесты производительности) { #benchmarks }
Независимые тесты производительности приложений от TechEmpower показывают, что **FastAPI** под управлением Uvicorn <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">один из самых быстрых Python-фреймворков</a> и уступает только Starlette и Uvicorn (которые используются в FastAPI). (*)
Независимые бенчмарки TechEmpower показывают, что приложения **FastAPI** под управлением Uvicorn <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">одни из самых быстрых Python‑фреймворков</a>, уступающие только Starlette и самому Uvicorn (используются внутри FastAPI).
Но при просмотре и сравнении замеров производительности следует иметь в виду нижеописанное.
Но при просмотре бенчмарков и сравнений следует иметь в виду следующее.
## Замеры производительности и скорости
## Бенчмарки и скорость { #benchmarks-and-speed }
В подобных тестах часто можно увидеть, что инструменты разного типа сравнивают друг с другом, как аналогичные.
При проверке бенчмарков часто можно увидеть, что инструменты разных типов сравнивают как эквивалентные.
В частности, сравнивают вместе Uvicorn, Starlette и FastAPI (среди многих других инструментов).
В частности, часто сравнивают вместе Uvicorn, Starlette и FastAPI (среди многих других инструментов).
Чем проще проблема, которую решает инструмент, тем выше его производительность. И большинство тестов не проверяют дополнительные функции, предоставляемые инструментом.
Чем проще задача, которую решает инструмент, тем выше его производительность. И большинство бенчмарков не тестируют дополнительные возможности, предоставляемые инструментом.
Иерархия инструментов имеет следующий вид:
Иерархия выглядит так:
* **Uvicorn**: ASGI-сервер
* **Starlette** (использует Uvicorn): веб-микрофреймворк
* **FastAPI** (использует Starlette): API-микрофреймворк с дополнительными функциями для создания API, с валидацией данных и т.д.
* **Starlette**: (использует Uvicorn) веб-микрофреймворк
* **FastAPI**: (использует Starlette) API-микрофреймворк с рядом дополнительных возможностей для создания API, включая валидацию данных и т. п.
* **Uvicorn**:
* Будет иметь наилучшую производительность, так как не имеет большого количества дополнительного кода, кроме самого сервера.
* Вы не будете писать приложение на Uvicorn напрямую. Это означало бы, что Ваш код должен включать как минимум весь
код, предоставляемый Starlette (или **FastAPI**). И если Вы так сделаете, то в конечном итоге Ваше приложение будет иметь те же накладные расходы, что и при использовании фреймворка, минимизирующего код Вашего приложения и Ваши ошибки.
* Uvicorn подлежит сравнению с Daphne, Hypercorn, uWSGI и другими веб-серверами.
* Будет иметь наилучшую производительность, так как помимо самого сервера у него немного дополнительного кода.
* Вы не будете писать приложение непосредственно на Uvicorn. Это означало бы, что Ваш код должен включать как минимум весь код, предоставляемый Starlette (или **FastAPI**). И если Вы так сделаете, то в конечном итоге Ваше приложение будет иметь те же накладные расходы, что и при использовании фреймворка, минимизирующего код Вашего приложения и Ваши ошибки.
* Если Вы сравниваете Uvicorn, сравнивайте его с Daphne, Hypercorn, uWSGI и т. д. — серверами приложений.
* **Starlette**:
* Будет уступать Uvicorn по производительности. Фактически Starlette управляется Uvicorn и из-за выполнения большего количества кода он не может быть быстрее, чем Uvicorn.
* Зато он предоставляет Вам инструменты для создания простых веб-приложений с обработкой маршрутов URL и т.д.
* Starlette следует сравнивать с Sanic, Flask, Django и другими веб-фреймворками (или микрофреймворками).
* Будет на следующем месте по производительности после Uvicorn. Фактически Starlette запускается под управлением Uvicorn, поэтому он может быть только «медленнее» Uvicorn из‑за выполнения большего объёма кода.
* Зато он предоставляет Вам инструменты для создания простых веб‑приложений с маршрутизацией по путям и т. п.
* Если Вы сравниваете Starlette, сравнивайте его с Sanic, Flask, Django и т. д. — веб‑фреймворками (или микрофреймворками).
* **FastAPI**:
* Так же как Starlette использует Uvicorn и не может быть быстрее него, **FastAPI** использует Starlette, то есть он не может быть быстрее Starlette.
* FastAPI предоставляет больше возможностей поверх Starlette, которые наверняка Вам понадобятся при создании API, такие как проверка данных и сериализация. В довесок Вы ещё и получаете автоматическую документацию (автоматическая документация даже не увеличивает накладные расходы при работе приложения, так как она создается при запуске).
* Если Вы не используете FastAPI, а используете Starlette напрямую (или другой инструмент вроде Sanic, Flask, Responder и т.д.), Вам пришлось бы самостоятельно реализовать валидацию и сериализацию данных. То есть, в итоге, Ваше приложение имело бы такие же накладные расходы, как если бы оно было создано с использованием FastAPI. И во многих случаях валидация и сериализация данных представляют собой самый большой объём кода, написанного в приложениях.
* Таким образом, используя FastAPI Вы потратите меньше времени на разработку, уменьшите количество ошибок, строк кода и, вероятно, получите ту же производительность (или лучше), как и если бы не использовали его (поскольку Вам пришлось бы реализовать все его возможности в своем коде).
* FastAPI должно сравнивать с фреймворками веб-приложений (или наборами инструментов), которые обеспечивают валидацию и сериализацию данных, а также предоставляют автоматическую документацию, такими как Flask-apispec, NestJS, Molten и им подобные.
* Точно так же, как Starlette использует Uvicorn и не может быть быстрее него, **FastAPI** использует Starlette, поэтому не может быть быстрее его.
* FastAPI предоставляет больше возможностей поверх Starlette — те, которые почти всегда нужны при создании API, такие как валидация и сериализация данных. В довесок Вы ещё и получаете автоматическую документацию (автоматическая документация даже не увеличивает накладные расходы при работе приложения, так как она создаётся при запуске).
* Если бы Вы не использовали FastAPI, а использовали Starlette напрямую (или другой инструмент вроде Sanic, Flask, Responder и т. д.), Вам пришлось бы самостоятельно реализовать валидацию и сериализацию данных. То есть, в итоге, Ваше приложение имело бы такие же накладные расходы, как если бы оно было создано с использованием FastAPI. И во многих случаях валидация и сериализация данных представляют собой самый большой объём кода, написанного в приложениях.
* Таким образом, используя FastAPI, Вы экономите время разработки, уменьшаете количество ошибок, строк кода и, вероятно, получите ту же производительность (или лучше), как и если бы не использовали его (поскольку Вам пришлось бы реализовать все его возможности в своём коде).
* Если Вы сравниваете FastAPI, сравнивайте его с фреймворком веб‑приложений (или набором инструментов), который обеспечивает валидацию данных, сериализацию и документацию, такими как Flask-apispec, NestJS, Molten и им подобные. Фреймворки с интегрированной автоматической валидацией данных, сериализацией и документацией.

322
docs/ru/docs/deployment/concepts.md

@ -1,323 +1,321 @@
# Концепции развёртывания
# Концепции развёртывания { #deployments-concepts }
Существует несколько концепций, применяемых для развёртывания приложений **FastAPI**, равно как и для любых других типов веб-приложений, среди которых вы можете выбрать **наиболее подходящий** способ.
При развёртывании приложения **FastAPI** (и вообще любого веб‑API) есть несколько концепций, о которых стоит думать — с их помощью можно выбрать **наиболее подходящий** способ **развёртывания вашего приложения**.
Самые важные из них:
Некоторые из важных концепций:
* Использование более безопасного протокола HTTPS
* Настройки запуска приложения
* Перезагрузка приложения
* Запуск нескольких экземпляров приложения
* Управление памятью
* Использование перечисленных функций перед запуском приложения.
* Безопасность — HTTPS
* Запуск при старте
* Перезапуски
* Репликация (количество запущенных процессов)
* Память
* Предварительные шаги перед запуском
Рассмотрим ниже влияние каждого из них на процесс **развёртывания**.
Посмотрим, как они влияют на **развёртывания**.
Наша конечная цель - **обслуживать клиентов вашего API безопасно** и **бесперебойно**, с максимально эффективным использованием **вычислительных ресурсов** (например, удалённых серверов/виртуальных машин). 🚀
В конечном итоге цель — **обслуживать клиентов вашего API** безопасно, **избегать перебоев** и максимально эффективно использовать **вычислительные ресурсы** (например, удалённые серверы/виртуальные машины). 🚀
Здесь я немного расскажу Вам об этих **концепциях** и надеюсь, что у вас сложится **интуитивное понимание**, какой способ выбрать при развертывании вашего API в различных окружениях, возможно, даже **ещё не существующих**.
Здесь я немного расскажу о этих **концепциях**, чтобы у вас появилась **интуиция**, как развёртывать ваш API в разных окружениях, возможно даже в **будущих**, которых ещё не существует.
Ознакомившись с этими концепциями, вы сможете **оценить и выбрать** лучший способ развёртывании **Вашего API**.
Учитывая эти концепции, вы сможете **оценить и спроектировать** лучший способ развёртывания **своих API**.
В последующих главах я предоставлю Вам **конкретные рецепты** развёртывания приложения FastAPI.
В следующих главах я дам более **конкретные рецепты** по развёртыванию приложений FastAPI.
А сейчас давайте остановимся на важных **идеях этих концепций**. Эти идеи можно также применить и к другим типам веб-приложений. 💡
А пока давайте разберём важные **идеи**. Эти концепции применимы и к другим типам веб‑API. 💡
## Использование более безопасного протокола HTTPS
## Безопасность — HTTPS { #security-https }
В [предыдущей главе об HTTPS](https.md){.internal-link target=_blank} мы рассмотрели, как HTTPS обеспечивает шифрование для вашего API.
В [предыдущей главе про HTTPS](https.md){.internal-link target=_blank} мы разобрались, как HTTPS обеспечивает шифрование для вашего API.
Также мы заметили, что обычно для работы с HTTPS вашему приложению нужен **дополнительный** компонент - **прокси-сервер завершения работы TLS**.
Также мы увидели, что HTTPS обычно обеспечивает компонент, **внешний** по отношению к серверу вашего приложения — **TLS Termination Proxy**.
И если прокси-сервер не умеет сам **обновлять сертификаты HTTPS**, то нужен ещё один компонент для этого действия.
И должен быть компонент, отвечающий за **обновление HTTPS‑сертификатов** — это может быть тот же самый компонент или отдельный.
### Примеры инструментов для работы с HTTPS
### Примеры инструментов для HTTPS { #example-tools-for-https }
Вот некоторые инструменты, которые вы можете применять как прокси-серверы:
Некоторые инструменты, которые можно использовать как TLS Termination Proxy:
* Traefik
* С автоматическим обновлением сертификатов
* Автоматически обновляет сертификаты
* Caddy
* С автоматическим обновлением сертификатов
* Автоматически обновляет сертификаты
* Nginx
* С дополнительным компонентом типа Certbot для обновления сертификатов
* С внешним компонентом (например, Certbot) для обновления сертификатов
* HAProxy
* С дополнительным компонентом типа Certbot для обновления сертификатов
* Kubernetes с Ingress Controller похожим на Nginx
* С дополнительным компонентом типа cert-manager для обновления сертификатов
* Использование услуг облачного провайдера (читайте ниже 👇)
* С внешним компонентом (например, Certbot) для обновления сертификатов
* Kubernetes с Ingress Controller (например, Nginx)
* С внешним компонентом (например, cert-manager) для обновления сертификатов
* Обрабатывается внутри облачного провайдера как часть его услуг (см. ниже 👇)
В последнем варианте вы можете воспользоваться услугами **облачного сервиса**, который сделает большую часть работы, включая настройку HTTPS. Это может наложить дополнительные ограничения или потребовать дополнительную плату и т.п. Зато Вам не понадобится самостоятельно заниматься настройками прокси-сервера.
Другой вариант — использовать **облачный сервис**, который возьмёт на себя больше задач, включая настройку HTTPS. Там могут быть ограничения или дополнительная стоимость и т.п., но в таком случае вам не придётся самим настраивать TLS Termination Proxy.
В дальнейшем я покажу Вам некоторые конкретные примеры их применения.
В следующих главах я покажу конкретные примеры.
---
Следующие концепции рассматривают применение программы, запускающей Ваш API (такой как Uvicorn).
Далее рассмотрим концепции, связанные с программой, которая запускает ваш реальный API (например, Uvicorn).
## Программа и процесс
## Программа и процесс { #program-and-process }
Мы часто будем встречать слова **процесс** и **программа**, потому следует уяснить отличия между ними.
Мы часто будем говорить о работающем "**процессе**", поэтому полезно чётко понимать, что это значит и чем отличается от "**программы**".
### Что такое программа
### Что такое программа { #what-is-a-program }
Термином **программа** обычно описывают множество вещей:
Словом **программа** обычно называют разные вещи:
* **Код**, который вы написали, в нашем случае **Python-файлы**.
* **Файл**, который может быть **исполнен** операционной системой, например `python`, `python.exe` или `uvicorn`.
* Конкретная программа, **запущенная** операционной системой и использующая центральный процессор и память. В таком случае это также называется **процесс**.
* **Код**, который вы пишете, то есть **Python‑файлы**.
* **Файл**, который может быть **запущен** операционной системой, например: `python`, `python.exe` или `uvicorn`.
* Конкретную программу в момент, когда она **работает** в операционной системе, используя CPU и память. Это также называют **процессом**.
### Что такое процесс
### Что такое процесс { #what-is-a-process }
Термин **процесс** имеет более узкое толкование, подразумевая что-то, запущенное операционной системой (как в последнем пункте из вышестоящего абзаца):
Слово **процесс** обычно используют более конкретно — только для того, что реально выполняется в операционной системе (как в последнем пункте выше):
* Конкретная программа, **запущенная** операционной системой.
* Это не имеет отношения к какому-либо файлу или коду, но нечто **определённое**, управляемое и **выполняемое** операционной системой.
* Любая программа, любой код, **могут делать что-то** только когда они **выполняются**. То есть, когда являются **работающим процессом**.
* Процесс может быть **прерван** (или "убит") Вами или вашей операционной системой. В результате чего он перестанет исполняться и **не будет продолжать делать что-либо**.
* Каждое приложение, которое вы запустили на своём компьютере, каждая программа, каждое "окно" запускает какой-то процесс. И обычно на включенном компьютере **одновременно** запущено множество процессов.
* И **одна программа** может запустить **несколько параллельных процессов**.
* Конкретная программа в момент, когда она **запущена** в операционной системе.
* Речь не о файле и не о коде, а **конкретно** о том, что **исполняется** и управляется операционной системой.
* Любая программа, любой код **могут что‑то делать** только когда **исполняются**, то есть когда есть **работающий процесс**.
* Процесс можно **завершить** (или «убить») вами или операционной системой. В этот момент он перестаёт выполняться и **больше ничего делать не может**.
* У каждого запущенного приложения на вашем компьютере есть свой процесс; у каждой программы, у каждого окна и т.д. Обычно одновременно **работает много процессов**, пока компьютер включён.
* Могут **одновременно** работать **несколько процессов** одной и той же **программы**.
Если вы заглянете в "диспетчер задач" или "системный монитор" (или аналогичные инструменты) вашей операционной системы, то увидите множество работающих процессов.
Если вы посмотрите «диспетчер задач» или «системный монитор» (или аналогичные инструменты) в вашей операционной системе, то увидите множество работающих процессов.
Вполне вероятно, что вы увидите несколько процессов с одним и тем же названием браузерной программы (Firefox, Chrome, Edge и т. Д.). Обычно браузеры запускают один процесс на вкладку и вдобавок некоторые дополнительные процессы.
Например, вы, скорее всего, увидите несколько процессов одного и того же браузера (Firefox, Chrome, Edge и т.д.). Обычно браузеры запускают один процесс на вкладку плюс дополнительные процессы.
<img class="shadow" src="/img/deployment/concepts/image01.png">
---
Теперь, когда нам известна разница между **процессом** и **программой**, давайте продолжим обсуждение развёртывания.
Теперь, когда мы понимаем разницу между **процессом** и **программой**, продолжим разговор о развёртываниях.
## Настройки запуска приложения
## Запуск при старте { #running-on-startup }
В большинстве случаев когда вы создаёте веб-приложение, то желаете, чтоб оно **работало постоянно** и непрерывно, предоставляя клиентам доступ в любое время. Хотя иногда у вас могут быть причины, чтоб оно запускалось только при определённых условиях.
В большинстве случаев, создавая веб‑API, вы хотите, чтобы он **работал постоянно**, без перерывов, чтобы клиенты всегда могли к нему обратиться. Разве что у вас есть особые причины запускать его только при определённых условиях, но обычно вы хотите, чтобы он был постоянно запущен и **доступен**.
### Удалённый сервер
### На удалённом сервере { #in-a-remote-server }
Когда вы настраиваете удалённый сервер (облачный сервер, виртуальную машину и т.п.), самое простое, что можно сделать, запустить Uvicorn (или его аналог) вручную, как вы делаете при локальной разработке.
Когда вы настраиваете удалённый сервер (облачный сервер, виртуальную машину и т.п.), самый простой вариант — вручную использовать `fastapi run` (он использует Uvicorn) или что‑то похожее, как вы делаете при локальной разработке.
Это рабочий способ и он полезен **во время разработки**.
Это будет работать и полезно **во время разработки**.
Но если вы потеряете соединение с сервером, то не сможете отслеживать - работает ли всё ещё **запущенный Вами процесс**.
Но если соединение с сервером прервётся, **запущенный процесс**, скорее всего, завершится.
И если сервер перезагрузится (например, после обновления или каких-то действий облачного провайдера), вы скорее всего **этого не заметите**, чтобы снова запустить процесс вручную. Вследствие этого Ваш API останется мёртвым. 😱
А если сервер перезагрузится (например, после обновлений или миграций у облачного провайдера), вы, вероятно, **даже не заметите этого**. Из‑за этого вы не узнаете, что нужно вручную перезапустить процесс — и ваш API просто будет «мёртв». 😱
### Автоматический запуск программ
### Автоматический запуск при старте { #run-automatically-on-startup }
Вероятно вы захотите, чтоб Ваша серверная программа (такая, как Uvicorn) стартовала автоматически при включении сервера, без **человеческого вмешательства** и всегда могла управлять Вашим API (так как Uvicorn запускает приложение FastAPI).
Как правило, вы захотите, чтобы серверная программа (например, Uvicorn) запускалась автоматически при старте сервера и без **участия человека**, чтобы всегда был процесс, запущенный с вашим API (например, Uvicorn, запускающий ваше приложение FastAPI).
### Отдельная программа
### Отдельная программа { #separate-program }
Для этого у обычно используют отдельную программу, которая следит за тем, чтобы Ваши приложения запускались при включении сервера. Такой подход гарантирует, что другие компоненты или приложения также будут запущены, например, база данных
Чтобы этого добиться, обычно используют **отдельную программу**, которая гарантирует запуск вашего приложения при старте. Во многих случаях она также запускает и другие компоненты/приложения, например базу данных.
### Примеры инструментов, управляющих запуском программ
### Примеры инструментов для запуска при старте { #example-tools-to-run-at-startup }
Вот несколько примеров, которые могут справиться с такой задачей:
Примеры инструментов, которые могут с этим справиться:
* Docker
* Kubernetes
* Docker Compose
* Docker в режиме Swarm
* Docker в режиме Swarm (Swarm Mode)
* Systemd
* Supervisor
* Использование услуг облачного провайдера
* Обработка внутри облачного провайдера как часть его услуг
* Прочие...
Я покажу Вам некоторые примеры их использования в следующих главах.
Более конкретные примеры будут в следующих главах.
## Перезапуск
## Перезапуски { #restarts }
Вы, вероятно, также захотите, чтоб ваше приложение **перезапускалось**, если в нём произошёл сбой.
Подобно тому как вы обеспечиваете запуск приложения при старте, вы, вероятно, захотите обеспечить его **перезапуск** после сбоев.
### Мы ошибаемся
### Мы ошибаемся { #we-make-mistakes }
Все люди совершают **ошибки**. Программное обеспечение почти *всегда* содержит **баги** спрятавшиеся в разных местах. 🐛
Мы, люди, постоянно совершаем **ошибки**. В программном обеспечении почти всегда есть **баги**, скрытые в разных местах. 🐛
И мы, будучи разработчиками, продолжаем улучшать код, когда обнаруживаем в нём баги или добавляем новый функционал (возможно, добавляя при этом баги 😅).
И мы, как разработчики, продолжаем улучшать код — находим баги и добавляем новые возможности (иногда добавляя новые баги 😅).
### Небольшие ошибки обрабатываются автоматически
### Небольшие ошибки обрабатываются автоматически { #small-errors-automatically-handled }
Когда вы создаёте свои API на основе FastAPI и допускаете в коде ошибку, то FastAPI обычно остановит её распространение внутри одного запроса, при обработке которого она возникла. 🛡
Создавая веб‑API с FastAPI, если в нашем коде возникает ошибка, FastAPI обычно «локализует» её в пределах одного запроса, который эту ошибку вызвал. 🛡
Клиент получит ошибку **500 Internal Server Error** в ответ на свой запрос, но приложение не сломается и будет продолжать работать с последующими запросами.
Клиент получит **500 Internal Server Error** для этого запроса, но приложение продолжит работать для последующих запросов, а не «упадёт» целиком.
### Большие ошибки - Падение приложений
### Большие ошибки — падения { #bigger-errors-crashes }
Тем не менее, может случиться так, что ошибка вызовет **сбой всего приложения** или даже сбой в Uvicorn, а то и в самом Python. 💥
Тем не менее возможны случаи, когда код **роняет всё приложение**, приводя к сбою Uvicorn и Python. 💥
Но мы всё ещё хотим, чтобы приложение **продолжало работать** несмотря на эту единственную ошибку, обрабатывая, как минимум, запросы к *операциям пути* не имеющим ошибок.
И вы, скорее всего, не захотите, чтобы приложение оставалось «мёртвым» из‑за ошибки в одном месте — вы захотите, чтобы оно **продолжало работать** хотя бы для *операций пути*, которые не сломаны.
### Перезапуск после падения
### Перезапуск после падения { #restart-after-crash }
Для случаев, когда ошибки приводят к сбою в запущенном **процессе**, Вам понадобится добавить компонент, который **перезапустит** процесс хотя бы пару раз...
В случаях действительно серьёзных ошибок, которые роняют работающий **процесс**, вам понадобится внешний компонент, отвечающий за **перезапуск** процесса, как минимум пару раз...
/// tip | Заметка
/// tip | Совет
... Если приложение падает сразу же после запуска, вероятно бесполезно его бесконечно перезапускать. Но полагаю, вы заметите такое поведение во время разработки или, по крайней мере, сразу после развёртывания.
...Хотя если приложение **падает сразу же**, вероятно, нет смысла перезапускать его бесконечно. Но такие случаи вы, скорее всего, заметите во время разработки или как минимум сразу после развёртывания.
Так что давайте сосредоточимся на конкретных случаях, когда приложение может полностью выйти из строя, но всё ещё есть смысл его запустить заново.
Давайте сосредоточимся на основных сценариях, когда в каких‑то конкретных ситуациях **в будущем** приложение может падать целиком, и при этом имеет смысл его перезапускать.
///
Возможно вы захотите, чтоб был некий **внешний компонент**, ответственный за перезапуск вашего приложения даже если уже не работает Uvicorn или Python. То есть ничего из того, что написано в вашем коде внутри приложения, не может быть выполнено в принципе.
Скорее всего, вы захотите, чтобы перезапуском вашего приложения занимался **внешний компонент**, потому что к тому моменту Uvicorn и Python уже упали, и внутри того же кода вашего приложения сделать уже ничего нельзя.
### Примеры инструментов для автоматического перезапуска
### Примеры инструментов для автоматического перезапуска { #example-tools-to-restart-automatically }
В большинстве случаев инструменты **запускающие программы при старте сервера** умеют **перезапускать** эти программы.
В большинстве случаев тот же инструмент, который **запускает программу при старте**, умеет обрабатывать и автоматические **перезапуски**.
В качестве примера можно взять те же:
Например, это может быть:
* Docker
* Kubernetes
* Docker Compose
* Docker в режиме Swarm
* Docker в режиме Swarm (Swarm Mode)
* Systemd
* Supervisor
* Использование услуг облачного провайдера
* Обработка внутри облачного провайдера как часть его услуг
* Прочие...
## Запуск нескольких экземпляров приложения (Репликация) - Процессы и память
## Репликация — процессы и память { #replication-processes-and-memory }
Приложение FastAPI, управляемое серверной программой (такой как Uvicorn), запускается как **один процесс** и может обслуживать множество клиентов одновременно.
В приложении FastAPI, используя серверную программу (например, команду `fastapi`, которая запускает Uvicorn), запуск в **одном процессе** уже позволяет обслуживать нескольких клиентов одновременно.
Но часто Вам может понадобиться несколько одновременно работающих одинаковых процессов.
Но во многих случаях вы захотите одновременно запустить несколько процессов‑воркеров.
### Множество процессов - Воркеры (Workers)
### Несколько процессов — Воркеры { #multiple-processes-workers }
Если количество Ваших клиентов больше, чем может обслужить один процесс (допустим, что виртуальная машина не слишком мощная), но при этом Вам доступно **несколько ядер процессора**, то вы можете запустить **несколько процессов** одного и того же приложения параллельно и распределить запросы между этими процессами.
Если клиентов больше, чем способен обслужить один процесс (например, если виртуальная машина не слишком мощная), и на сервере есть **несколько ядер CPU**, вы можете запустить **несколько процессов** одного и того же приложения параллельно и распределять запросы между ними.
**Несколько запущенных процессов** одной и той же API-программы часто называют **воркерами**.
Когда вы запускаете **несколько процессов** одной и той же программы API, их обычно называют **воркерами**.
### Процессы и порты́
### Процессы‑воркеры и порты { #worker-processes-and-ports }
Помните ли Вы, как на странице [Об HTTPS](https.md){.internal-link target=_blank} мы обсуждали, что на сервере только один процесс может слушать одну комбинацию IP-адреса и порта?
Помните из раздела [Об HTTPS](https.md){.internal-link target=_blank}, что на сервере только один процесс может слушать конкретную комбинацию порта и IP‑адреса?
С тех пор ничего не изменилось.
Это по‑прежнему так.
Соответственно, чтобы иметь возможность работать с **несколькими процессами** одновременно, должен быть **один процесс, прослушивающий порт** и затем каким-либо образом передающий данные каждому рабочему процессу.
Поэтому, чтобы одновременно работало **несколько процессов**, должен быть **один процесс, слушающий порт**, который затем каким‑то образом передаёт коммуникацию каждому воркер‑процессу.
### У каждого процесса своя память
### Память на процесс { #memory-per-process }
Работающая программа загружает в память данные, необходимые для её работы, например, переменные содержащие модели машинного обучения или большие файлы. Каждая переменная **потребляет некоторое количество оперативной памяти (RAM)** сервера.
Когда программа загружает что‑то в память (например, модель машинного обучения в переменную или содержимое большого файла в переменную), всё это **потребляет часть памяти (RAM)** сервера.
Обычно процессы **не делятся памятью друг с другом**. Сие означает, что каждый работающий процесс имеет свои данные, переменные и свой кусок памяти. И если для выполнения вашего кода процессу нужно много памяти, то **каждый такой же процесс** запущенный дополнительно, потребует такого же количества памяти.
И разные процессы обычно **не делят память**. Это значит, что у каждого процесса свои переменные и своя память. Если ваш код потребляет много памяти, то **каждый процесс** будет потреблять сопоставимый объём памяти.
### Память сервера
### Память сервера { #server-memory }
Допустим, что Ваш код загружает модель машинного обучения **размером 1 ГБ**. Когда вы запустите своё API как один процесс, он займёт в оперативной памяти не менее 1 ГБ. А если вы запустите **4 таких же процесса** (4 воркера), то каждый из них займёт 1 ГБ оперативной памяти. В результате вашему API потребуется **4 ГБ оперативной памяти (RAM)**.
Например, если ваш код загружает модель Машинного обучения размером **1 ГБ**, то при запуске одного процесса с вашим API он будет использовать как минимум 1 ГБ RAM. А если вы запустите **4 процесса** (4 воркера), каждый процесс будет использовать 1 ГБ RAM. Всего ваш API будет потреблять **4 ГБ RAM**.
И если Ваш удалённый сервер или виртуальная машина располагает только 3 ГБ памяти, то попытка загрузить в неё 4 ГБ данных вызовет проблемы. 🚨
И если у вашего удалённого сервера или виртуальной машины только 3 ГБ RAM, попытка загрузить более 4 ГБ вызовет проблемы. 🚨
### Множество процессов - Пример
### Несколько процессов — пример { #multiple-processes-an-example }
В этом примере **менеджер процессов** запустит и будет управлять двумя **воркерами**.
В этом примере есть **процесс‑менеджер**, который запускает и контролирует два **процесса‑воркера**.
Менеджер процессов будет слушать определённый **сокет** (IP:порт) и передавать данные работающим процессам.
Процесс‑менеджер, вероятно, будет тем, кто слушает **порт** на IP. И он будет передавать всю коммуникацию воркер‑процессам.
Каждый из этих процессов будет запускать ваше приложение для обработки полученного **запроса** и возвращения вычисленного **ответа** и они будут использовать оперативную память.
Эти воркеры будут запускать ваше приложение, выполнять основные вычисления для получения **запроса** и возврата **ответа**, и загружать всё, что вы кладёте в переменные, в RAM.
<img src="/img/deployment/concepts/process-ram.drawio.svg">
Безусловно, на этом же сервере будут работать и **другие процессы**, которые не относятся к вашему приложению.
Конечно, на той же машине помимо вашего приложения, скорее всего, будут работать и **другие процессы**.
Интересная деталь заключается в том, что процент **использования центрального процессора (CPU)** каждым процессом может сильно меняться с течением времени, но объём занимаемой **оперативной памяти (RAM)** остаётся относительно **стабильным**.
Интересная деталь: процент **использования CPU** каждым процессом со временем может сильно **меняться**, но **память (RAM)** обычно остаётся более‑менее **стабильной**.
Если у вас есть API, который каждый раз выполняет сопоставимый объем вычислений, и у вас много клиентов, то **загрузка процессора**, вероятно, акже будет стабильной* (вместо того, чтобы постоянно быстро увеличиваться и уменьшаться).
Если у вас API, который каждый раз выполняет сопоставимый объём вычислений, и у вас много клиентов, то **загрузка процессора**, вероятно, оже будет стабильной* (вместо того, чтобы быстро и постоянно «скакать»).
### Примеры стратегий и инструментов для запуска нескольких экземпляров приложения
### Примеры инструментов и стратегий репликации { #examples-of-replication-tools-and-strategies }
Существует несколько подходов для достижения целей репликации и я расскажу Вам больше о конкретных стратегиях в следующих главах, например, когда речь пойдет о Docker и контейнерах.
Есть несколько подходов для достижения этого, и я расскажу больше о конкретных стратегиях в следующих главах, например, говоря о Docker и контейнерах.
Основное ограничение при этом - только **один** компонент может работать с определённым **портом публичного IP**. И должен быть способ **передачи** данных между этим компонентом и копиями **процессов/воркеров**.
Главное ограничение: должен быть **один** компонент, который обрабатывает **порт** на **публичном IP**. И у него должен быть способ **передавать** коммуникацию реплицированным **процессам/воркерам**.
Вот некоторые возможные комбинации и стратегии:
Некоторые возможные комбинации и стратегии:
* **Gunicorn** управляющий **воркерами Uvicorn**
* Gunicorn будет выступать как **менеджер процессов**, прослушивая **IP:port**. Необходимое количество запущенных экземпляров приложения будет осуществляться посредством запуска **множества работающих процессов Uvicorn**.
* **Uvicorn** управляющий **воркерами Uvicorn**
* Один процесс Uvicorn будет выступать как **менеджер процессов**, прослушивая **IP:port**. Он будет запускать **множество работающих процессов Uvicorn**.
* **Kubernetes** и аналогичные **контейнерные системы**
* Какой-то компонент в **Kubernetes** будет слушать **IP:port**. Необходимое количество запущенных экземпляров приложения будет осуществляться посредством запуска **нескольких контейнеров**, в каждом из которых работает **один процесс Uvicorn**.
* **Облачные сервисы**, которые позаботятся обо всём за Вас
* Возможно, что облачный сервис умеет **управлять запуском дополнительных экземпляров приложения**. Вероятно, он потребует, чтоб вы указали - какой **процесс** или **образ** следует клонировать. Скорее всего, вы укажете **один процесс Uvicorn** и облачный сервис будет запускать его копии при необходимости.
* **Uvicorn** с `--workers`
* Один **процесс‑менеджер** Uvicorn будет слушать **IP** и **порт** и запускать **несколько процессов‑воркеров Uvicorn**.
* **Kubernetes** и другие распределённые **контейнерные системы**
* Некий компонент на уровне **Kubernetes** будет слушать **IP** и **порт**. Репликация достигается с помощью **нескольких контейнеров**, в каждом из которых работает **один процесс Uvicorn**.
* **Облачные сервисы**, которые берут это на себя
* Облачный сервис, скорее всего, **возьмёт репликацию на себя**. Он, возможно, позволит указать **процесс для запуска** или **образ контейнера**. В любом случае это, скорее всего, будет **один процесс Uvicorn**, а сервис займётся его репликацией.
/// tip | Заметка
/// tip | Совет
Если вы не знаете, что такое **контейнеры**, Docker или Kubernetes, не переживайте.
Не беспокойтесь, если некоторые пункты про **контейнеры**, Docker или Kubernetes пока кажутся неочевидными.
Я поведаю Вам о контейнерах, образах, Docker, Kubernetes и т.п. в главе: [FastAPI внутри контейнеров - Docker](docker.md){.internal-link target=_blank}.
Я расскажу больше про образы контейнеров, Docker, Kubernetes и т.п. в следующей главе: [FastAPI внутри контейнеров — Docker](docker.md){.internal-link target=_blank}.
///
## Шаги, предшествующие запуску
## Предварительные шаги перед запуском { #previous-steps-before-starting }
Часто бывает, что Вам необходимо произвести какие-то подготовительные шаги **перед запуском** своего приложения.
Во многих случаях вы захотите выполнить некоторые шаги **перед запуском** приложения.
Например, запустить **миграции базы данных**.
Но в большинстве случаев такие действия достаточно произвести **однократно**.
Но чаще всего эти шаги нужно выполнять только **один раз**.
Поэтому Вам нужен будет **один процесс**, выполняющий эти **подготовительные шаги** до запуска приложения.
Поэтому вы захотите иметь **один процесс**, который выполнит эти **предварительные шаги**, прежде чем запускать приложение.
Также Вам нужно будет убедиться, что этот процесс выполнил подготовительные шаги *даже* если впоследствии вы запустите **несколько процессов** (несколько воркеров) самого приложения. Если бы эти шаги выполнялись в каждом **клонированном процессе**, они бы **дублировали** работу, пытаясь выполнить её **параллельно**. И если бы эта работа была бы чем-то деликатным, вроде миграции базы данных, то это может вызвать конфликты между ними.
И вам нужно будет убедиться, что это делает один процесс **даже** если потом вы запускаете **несколько процессов** (несколько воркеров) самого приложения. Если эти шаги выполнят **несколько процессов**, они **дублируют** работу, запустив её **параллельно**, и, если речь о чём‑то деликатном (например, миграции БД), это может вызвать конфликты.
Безусловно, возможны случаи, когда нет проблем при выполнении предварительной подготовки параллельно или несколько раз. Тогда Вам повезло, работать с ними намного проще.
Конечно, бывают случаи, когда нет проблем, если предварительные шаги выполняются несколько раз — тогда всё проще.
/// tip | Заметка
/// tip | Совет
Имейте в виду, что в некоторых случаях запуск вашего приложения **может не требовать каких-либо предварительных шагов вовсе**.
Также учтите, что в зависимости от вашей схемы развёртывания в некоторых случаях **предварительные шаги могут вовсе не требоваться**.
Что ж, тогда Вам не нужно беспокоиться об этом. 🤷
Тогда об этом можно не беспокоиться. 🤷
///
### Примеры стратегий запуска предварительных шагов
### Примеры стратегий для предварительных шагов { #examples-of-previous-steps-strategies }
Существует **сильная зависимость** от того, как вы **развёртываете свою систему**, запускаете программы, обрабатываете перезапуски и т.д.
Это будет **сильно зависеть** от того, как вы **развёртываете систему**, как запускаете программы, обрабатываете перезапуски и т.д.
Вот некоторые возможные идеи:
Некоторые возможные идеи:
* При использовании Kubernetes нужно предусмотреть "инициализирующий контейнер", запускаемый до контейнера с приложением.
* Bash-скрипт, выполняющий предварительные шаги, а затем запускающий приложение.
* При этом Вам всё ещё нужно найти способ - как запускать/перезапускать *такой* bash-скрипт, обнаруживать ошибки и т.п.
* «Init Container» в Kubernetes, который запускается перед контейнером с приложением
* Bash‑скрипт, который выполняет предварительные шаги, а затем запускает приложение
* При этом всё равно нужен способ запускать/перезапускать *этот* bash‑скрипт, обнаруживать ошибки и т.п.
/// tip | Заметка
/// tip | Совет
Я приведу Вам больше конкретных примеров работы с контейнерами в главе: [FastAPI внутри контейнеров - Docker](docker.md){.internal-link target=_blank}.
Я приведу более конкретные примеры с контейнерами в следующей главе: [FastAPI внутри контейнеров — Docker](docker.md){.internal-link target=_blank}.
///
## Утилизация ресурсов
## Использование ресурсов { #resource-utilization }
Ваш сервер располагает ресурсами, которые Ваши программы могут потреблять или **утилизировать**, а именно - время работы центрального процессора и объём оперативной памяти.
Ваш сервер(а) — это **ресурс**, который ваши программы могут потреблять или **использовать**: время вычислений на CPU и доступную оперативную память (RAM).
Как много системных ресурсов вы предполагаете потребить/утилизировать? Если не задумываться, то можно ответить - "немного", но на самом деле Вы, вероятно, захотите использовать **максимально возможное количество**.
Какую долю системных ресурсов вы хотите потреблять/использовать? Можно подумать «немного», но на практике вы, скорее всего, захотите потреблять **максимум без падений**.
Если вы платите за содержание трёх серверов, но используете лишь малую часть системных ресурсов каждого из них, то вы **выбрасываете деньги на ветер**, а также **впустую тратите электроэнергию** и т.п.
Если вы платите за 3 сервера, но используете лишь малую часть их RAM и CPU, вы, вероятно, **тратите деньги впустую** 💸 и **электроэнергию серверов** 🌎 и т.п.
В таком случае было бы лучше обойтись двумя серверами, но более полно утилизировать их ресурсы (центральный процессор, оперативную память, жёсткий диск, сети передачи данных и т.д).
В таком случае лучше иметь 2 сервера и использовать более высокий процент их ресурсов (CPU, память, диск, сетевую полосу и т.д.).
С другой стороны, если вы располагаете только двумя серверами и используете **на 100% их процессоры и память**, но какой-либо процесс запросит дополнительную память, то операционная система сервера будет использовать жёсткий диск для расширения оперативной памяти (а диск работает в тысячи раз медленнее), а то вовсе **упадёт**. Или если какому-то процессу понадобится произвести вычисления, то ему придётся подождать, пока процессор освободится.
С другой стороны, если у вас 2 сервера и вы используете **100% их CPU и RAM**, в какой‑то момент один процесс попросит больше памяти, и сервер начнёт использовать диск как «память» (что в тысячи раз медленнее) или даже **упадёт**. Или процессу понадобятся вычисления, но ему придётся ждать освобождения CPU.
В такой ситуации лучше подключить **ещё один сервер** и перераспределить процессы между серверами, чтоб всем **хватало памяти и процессорного времени**.
В таком случае лучше добавить **ещё один сервер** и запустить часть процессов на нём, чтобы у всех было **достаточно RAM и времени CPU**.
Также есть вероятность, что по какой-то причине возник **всплеск** запросов к вашему API. Возможно, это был вирус, боты или другие сервисы начали пользоваться им. И для таких происшествий вы можете захотеть иметь дополнительные ресурсы.
Также возможен **всплеск** использования вашего API: он мог «взорваться» по популярности, или какие‑то сервисы/боты начали его активно использовать. На такие случаи стоит иметь запас ресурсов.
При настройке логики развёртываний, вы можете указать **целевое значение** утилизации ресурсов, допустим, **от 50% до 90%**. Обычно эти метрики и используют.
Можно задать **целевое значение**, например **между 50% и 90%** использования ресурсов. Скорее всего, именно эти вещи вы будете измерять и на их основе настраивать развёртывание.
Вы можете использовать простые инструменты, такие как `htop`, для отслеживания загрузки центрального процессора и оперативной памяти сервера, в том числе каждым процессом. Или более сложные системы мониторинга нескольких серверов.
Можно использовать простые инструменты вроде `htop`, чтобы смотреть загрузку CPU и RAM на сервере или по процессам. Или более сложные распределённые системы мониторинга.
## Резюме
## Резюме { #recap }
Вы прочитали некоторые из основных концепций, которые необходимо иметь в виду при принятии решения о развертывании приложений:
Здесь вы прочитали о некоторых основных концепциях, которые, вероятно, стоит учитывать при выборе способа развёртывания приложения:
* Использование более безопасного протокола HTTPS
* Настройки запуска приложения
* Перезагрузка приложения
* Запуск нескольких экземпляров приложения
* Управление памятью
* Использование перечисленных функций перед запуском приложения.
* Безопасность — HTTPS
* Запуск при старте
* Перезапуски
* Репликация (количество запущенных процессов)
* Память
* Предварительные шаги перед запуском
Осознание этих идей и того, как их применять, должно дать Вам интуитивное понимание, необходимое для принятия решений при настройке развертываний. 🤓
Понимание этих идей и того, как их применять, даст вам интуицию, необходимую для принятия решений при настройке и доработке ваших развёртываний. 🤓
В следующих разделах я приведу более конкретные примеры возможных стратегий, которым вы можете следовать. 🚀
В следующих разделах я приведу более конкретные примеры возможных стратегий. 🚀

639
docs/ru/docs/deployment/docker.md

@ -1,17 +1,17 @@
# FastAPI и Docker-контейнеры
# FastAPI в контейнерах — Docker { #fastapi-in-containers-docker }
При развёртывании приложений FastAPI, часто начинают с создания **образа контейнера на основе Linux**. Обычно для этого используют <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a>. Затем можно развернуть такой контейнер на сервере одним из нескольких способов.
При развёртывании приложений FastAPI распространённый подход — собирать **образ контейнера на Linux**. Обычно это делают с помощью <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a>. Затем такой образ контейнера можно развернуть несколькими способами.
Использование контейнеров на основе Linux имеет ряд преимуществ, включая **безопасность**, **воспроизводимость**, **простоту** и прочие.
Использование Linux-контейнеров даёт ряд преимуществ: **безопасность**, **воспроизводимость**, **простоту** и другие.
/// tip | Подсказка
Торопитесь или уже знакомы с этой технологией? Перепрыгните на раздел [Создать Docker-образ для FastAPI 👇](#docker-fastapi)
Нет времени и вы уже знакомы с этим? Перейдите к [`Dockerfile` ниже 👇](#build-a-docker-image-for-fastapi).
///
<details>
<summary>Развернуть Dockerfile 👀</summary>
<summary>Предпросмотр Dockerfile 👀</summary>
```Dockerfile
FROM python:3.9
@ -24,130 +24,125 @@ 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"]
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
# Если используете прокси-сервер, такой как Nginx или Traefik, добавьте --proxy-headers
# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--proxy-headers"]
# Если запускаете за прокси, например Nginx или Traefik, добавьте --proxy-headers
# CMD ["fastapi", "run", "app/main.py", "--port", "80", "--proxy-headers"]
```
</details>
## Что такое "контейнер"
## Что такое контейнер { #what-is-a-container }
Контейнеризация - это **легковесный** способ упаковать приложение, включая все его зависимости и необходимые файлы, чтобы изолировать его от других контейнеров (других приложений и компонентов) работающих на этой же системе.
Контейнеры (в основном Linux-контейнеры) — это очень **легковесный** способ упаковать приложения вместе со всеми их зависимостями и необходимыми файлами, изолировав их от других контейнеров (других приложений или компонентов) в той же системе.
Контейнеры, основанные на Linux, запускаются используя ядро Linux хоста (машины, виртуальной машины, облачного сервера и т.п.). Это значит, что они очень легковесные (по сравнению с полноценными виртуальными машинами, полностью эмулирующими работу операционной системы).
Linux-контейнеры запускаются, используя то же ядро Linux хоста (машины, виртуальной машины, облачного сервера и т.п.). Это означает, что они очень легковесные (по сравнению с полноценными виртуальными машинами, эмулирующими целую операционную систему).
Благодаря этому, контейнеры потребляют **малое количество ресурсов**, сравнимое с процессом запущенным напрямую (виртуальная машина потребует гораздо больше ресурсов).
Таким образом, контейнеры потребляют **малое количество ресурсов**, сопоставимое с запуском процессов напрямую (виртуальная машина потребовала бы намного больше ресурсов).
Контейнеры также имеют собственные запущенные **изолированные** процессы (но часто только один процесс), файловую систему и сеть, что упрощает развёртывание, разработку, управление доступом и т.п.
У контейнеров также есть собственные **изолированные** выполняемые процессы (обычно всего один процесс), файловая система и сеть, что упрощает развёртывание, безопасность, разработку и т.д.
## Что такое "образ контейнера"
## Что такое образ контейнера { #what-is-a-container-image }
Для запуска **контейнера** нужен **образ контейнера**.
**Контейнер** запускается из **образа контейнера**.
Образ контейнера - это **замороженная** версия всех файлов, переменных окружения, программ и команд по умолчанию, необходимых для работы приложения. **Замороженный** - означает, что **образ** не запущен и не выполняется, это всего лишь упакованные вместе файлы и метаданные.
Образ контейнера — это **статическая** версия всех файлов, переменных окружения и команды/программы по умолчанию, которые должны присутствовать в контейнере. Здесь **статическая** означает, что **образ** не запущен, он не выполняется — это только упакованные файлы и метаданные.
В отличие от **образа контейнера**, хранящего неизменное содержимое, под термином **контейнер** подразумевают запущенный образ, то есть объёкт, который **исполняется**.
В противоположность «**образу контейнера**» (хранящему статическое содержимое), «**контейнер**» обычно означает запущенный экземпляр, то, что **выполняется**.
Когда **контейнер** запущен (на основании **образа**), он может создавать и изменять файлы, переменные окружения и т.д. Эти изменения будут существовать только внутри контейнера, но не будут сохраняться в образе контейнера (не будут сохранены на диск).
Когда **контейнер** запущен (на основе **образа контейнера**), он может создавать или изменять файлы, переменные окружения и т.д.. Эти изменения существуют только внутри контейнера и не сохраняются в исходном образе контейнера (не записываются на диск).
Образ контейнера можно сравнить с файлом, содержащем **программу**, например, как файл `main.py`.
Образ контейнера можно сравнить с **файлами программы**, например `python` и каким-то файлом `main.py`.
И **контейнер** (в отличие от **образа**) - это на самом деле выполняемый экземпляр образа, примерно как **процесс**. По факту, контейнер запущен только когда запущены его процессы (чаще, всего один процесс) и остановлен, когда запущенных процессов нет.
А сам **контейнер** (в отличие от **образа контейнера**) — это фактически запущенный экземпляр образа, сопоставимый с **процессом**. По сути, контейнер работает только тогда, когда в нём есть **запущенный процесс** (и обычно это один процесс). Контейнер останавливается, когда в нём не остаётся запущенных процессов.
## Образы контейнеров
## Образы контейнеров { #container-images }
Docker является одним из основных инструментов для создания **образов** и **контейнеров** и управления ими.
Docker — один из основных инструментов для создания и управления **образами контейнеров** и **контейнерами**.
Существует общедоступный <a href="https://hub.docker.com/" class="external-link" target="_blank">Docker Hub</a> с подготовленными **официальными образами** многих инструментов, окружений, баз данных и приложений.
Существует публичный <a href="https://hub.docker.com/" class="external-link" target="_blank">Docker Hub</a> с готовыми **официальными образами** для многих инструментов, окружений, баз данных и приложений.
К примеру, есть официальный <a href="https://hub.docker.com/_/python" class="external-link" target="_blank">образ Python</a>.
Например, есть официальный <a href="https://hub.docker.com/_/python" class="external-link" target="_blank">образ Python</a>.
Также там представлены и другие полезные образы, такие как базы данных:
А также множество образов для разных вещей, например баз данных:
* <a href="https://hub.docker.com/_/postgres" class="external-link" target="_blank">PostgreSQL</a>
* <a href="https://hub.docker.com/_/mysql" class="external-link" target="_blank">MySQL</a>
* <a href="https://hub.docker.com/_/mongo" class="external-link" target="_blank">MongoDB</a>
* <a href="https://hub.docker.com/_/redis" class="external-link" target="_blank">Redis</a>
* <a href="https://hub.docker.com/_/redis" class="external-link" target="_blank">Redis</a>, и т.д.
и т.п.
Используя готовые образы, очень легко **комбинировать** разные инструменты и использовать их. Например, чтобы попробовать новую базу данных. В большинстве случаев можно воспользоваться **официальными образами** и просто настроить их через переменные окружения.
Использование подготовленных образов значительно упрощает **комбинирование** и использование разных инструментов. Например, вы можете попытаться использовать новую базу данных. В большинстве случаев можно использовать **официальный образ** и всего лишь указать переменные окружения.
Таким образом, во многих случаях вы можете изучить контейнеры и Docker и переиспользовать эти знания с множеством различных инструментов и компонентов.
Таким образом, вы можете изучить, что такое контейнеризация и Docker, и использовать полученные знания с разными инструментами и компонентами.
Например, вы можете запустить **несколько контейнеров**: с базой данных, Python-приложением, веб-сервером с фронтендом на React и связать их через внутреннюю сеть.
Так, вы можете запустить одновременно **множество контейнеров** с базой данных, Python-приложением, веб-сервером, React-приложением и соединить их вместе через внутреннюю сеть.
Все системы управления контейнерами (такие как Docker или Kubernetes) имеют интегрированные возможности для такого сетевого взаимодействия.
Все системы управления контейнерами (такие, как Docker или Kubernetes) имеют встроенные возможности для организации такого сетевого взаимодействия.
## Контейнеры и процессы { #containers-and-processes }
## Контейнеры и процессы
**Образ контейнера** обычно включает в свои метаданные программу или команду по умолчанию, которую следует запускать при старте **контейнера**, а также параметры, передаваемые этой программе. Это очень похоже на запуск команды в терминале.
Обычно **образ контейнера** содержит метаданные предустановленной программы или команду, которую следует выполнить при запуске **контейнера**. Также он может содержать параметры, передаваемые предустановленной программе. Похоже на то, как если бы вы запускали такую программу через терминал.
Когда **контейнер** стартует, он выполняет указанную команду/программу (хотя вы можете переопределить это и запустить другую команду/программу).
Когда **контейнер** запущен, он будет выполнять прописанные в нём команды и программы. Но вы можете изменить его так, чтоб он выполнял другие команды и программы.
Контейнер работает до тех пор, пока работает его **главный процесс** (команда или программа).
Контейнер будет работать до тех пор, пока выполняется его **главный процесс** (команда или программа).
Обычно в контейнере есть **один процесс**, но главный процесс может запускать подпроцессы, и тогда в том же контейнере будет **несколько процессов**.
В контейнере обычно выполняется **только один процесс**, но от его имени можно запустить другие процессы, тогда в этом же в контейнере будет выполняться **множество процессов**.
Нельзя иметь работающий контейнер без **хотя бы одного запущенного процесса**. Если главный процесс останавливается, контейнер останавливается.
Контейнер не считается запущенным, если в нём **не выполняется хотя бы один процесс**. Если главный процесс остановлен, значит и контейнер остановлен.
## Создать Docker-образ для FastAPI { #build-a-docker-image-for-fastapi }
## Создать Docker-образ для FastAPI
Итак, давайте что-нибудь соберём! 🚀
Что ж, давайте ужё создадим что-нибудь! 🚀
Я покажу, как собрать **Docker-образ** для FastAPI **с нуля** на основе **официального образа Python**.
Я покажу Вам, как собирать **Docker-образ** для FastAPI **с нуля**, основываясь на **официальном образе Python**.
Именно так стоит делать в **большинстве случаев**, например:
Такой подход сгодится для **большинства случаев**, например:
* При использовании **Kubernetes** или похожих инструментов
* При запуске на **Raspberry Pi**
* При использовании облачного сервиса, который запускает для вас образ контейнера и т.п.
* Использование с **Kubernetes** или аналогичным инструментом
* Запуск в **Raspberry Pi**
* Использование в облачных сервисах, запускающих образы контейнеров для вас и т.п.
### Зависимости пакетов { #package-requirements }
### Установить зависимости
Обычно **зависимости** вашего приложения описаны в каком-то файле.
Обычно вашему приложению необходимы **дополнительные библиотеки**, список которых находится в отдельном файле.
Конкретный формат зависит в основном от инструмента, которым вы **устанавливаете** эти зависимости.
На название и содержание такого файла влияет выбранный Вами инструмент **установки** этих библиотек (зависимостей).
Чаще всего используется файл `requirements.txt` с именами пакетов и их версиями по одному на строку.
Чаще всего это простой файл `requirements.txt` с построчным перечислением библиотек и их версий.
Разумеется, вы будете придерживаться тех же идей, что описаны здесь: [О версиях FastAPI](versions.md){.internal-link target=_blank}, чтобы задать диапазоны версий.
При этом Вы, для выбора версий, будете использовать те же идеи, что упомянуты на странице [О версиях FastAPI](versions.md){.internal-link target=_blank}.
Ваш файл `requirements.txt` может выглядеть как-то так:
Например, ваш `requirements.txt` может выглядеть так:
```
fastapi>=0.68.0,<0.69.0
pydantic>=1.8.0,<2.0.0
uvicorn>=0.15.0,<0.16.0
fastapi[standard]>=0.113.0,<0.114.0
pydantic>=2.7.0,<3.0.0
```
Устанавливать зависимости проще всего с помощью `pip`:
И обычно вы установите эти зависимости командой `pip`, например:
<div class="termy">
```console
$ pip install -r requirements.txt
---> 100%
Successfully installed fastapi pydantic uvicorn
Successfully installed fastapi pydantic
```
</div>
/// info | Информация
Существуют и другие инструменты управления зависимостями.
В этом же разделе, но позже, я покажу вам пример использования Poetry. 👇
Существуют и другие форматы и инструменты для описания и установки зависимостей.
///
### Создать приложение **FastAPI**
### Создать код **FastAPI** { #create-the-fastapi-code }
* Создайте директорию `app` и перейдите в неё.
* Создайте пустой файл `__init__.py`.
* Создайте файл `main.py` и заполните его:
* Создайте файл `main.py` со следующим содержимым:
```Python
from typing import Union
@ -167,77 +162,109 @@ def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
```
### Dockerfile
### Dockerfile { #dockerfile }
В этой же директории создайте файл `Dockerfile` и заполните его:
Теперь в той же директории проекта создайте файл `Dockerfile`:
```{ .dockerfile .annotate }
# (1)
# (1)!
FROM python:3.9
# (2)
# (2)!
WORKDIR /code
# (3)
# (3)!
COPY ./requirements.txt /code/requirements.txt
# (4)
# (4)!
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (5)
# (5)!
COPY ./app /code/app
# (6)
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
# (6)!
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
1. Начните с официального образа Python, который будет основой для образа приложения.
1. Начинаем с официального базового образа Python.
2. Укажите, что в дальнейшем команды запускаемые в контейнере, будут выполняться в директории `/code`.
2. Устанавливаем текущую рабочую директорию в `/code`.
Инструкция создаст эту директорию внутри контейнера и мы поместим в неё файл `requirements.txt` и директорию `app`.
Здесь мы разместим файл `requirements.txt` и директорию `app`.
3. Скопируете файл с зависимостями из текущей директории в `/code`.
3. Копируем файл с зависимостями в директорию `/code`.
Сначала копируйте **только** файл с зависимостями.
Сначала копируйте **только** файл с зависимостями, не остальной код.
Этот файл **изменяется довольно редко**, Docker ищет изменения при постройке образа и если не находит, то использует **кэш**, в котором хранятся предыдущие версии сборки образа.
Так как этот файл **меняется нечасто**, Docker определит это и использует **кэш** на этом шаге, что позволит использовать кэш и на следующем шаге.
4. Установите библиотеки перечисленные в файле с зависимостями.
4. Устанавливаем зависимости из файла с требованиями.
Опция `--no-cache-dir` указывает `pip` не сохранять загружаемые библиотеки на локальной машине для использования их в случае повторной загрузки. В контейнере, в случае пересборки этого шага, они всё равно будут удалены.
Опция `--no-cache-dir` указывает `pip` не сохранять загруженные пакеты локально, т.к. это нужно только если `pip` будет запускаться снова для установки тех же пакетов, а при работе с контейнерами это обычно не требуется.
/// note | Заметка
Опция `--no-cache-dir` нужна только для `pip`, она никак не влияет на Docker или контейнеры.
`--no-cache-dir` относится только к `pip` и не имеет отношения к Docker или контейнерам.
///
Опция `--upgrade` указывает `pip` обновить библиотеки, емли они уже установлены.
Опция `--upgrade` указывает `pip` обновлять пакеты, если они уже установлены.
Как и в предыдущем шаге с копированием файла, этот шаг также будет использовать **кэш Docker** в случае отсутствия изменений.
Поскольку предыдущий шаг с копированием файла может быть обработан **кэшем Docker**, этот шаг также **использует кэш Docker**, когда это возможно.
Использование кэша, особенно на этом шаге, позволит вам **сэкономить** кучу времени при повторной сборке образа, так как зависимости будут сохранены в кеше, а не **загружаться и устанавливаться каждый раз**.
Использование кэша на этом шаге **сэкономит** вам много **времени** при повторных сборках образа во время разработки, вместо того чтобы **загружать и устанавливать** все зависимости **каждый раз**.
5. Скопируйте директорию `./app` внутрь директории `/code` (в контейнере).
5. Копируем директорию `./app` внутрь директории `/code`.
Так как в этой директории расположен код, который **часто изменяется**, то использование **кэша** на этом шаге будет наименее эффективно, а значит лучше поместить этот шаг **ближе к концу** `Dockerfile`, дабы не терять выгоду от оптимизации предыдущих шагов.
Так как здесь весь код, который **меняется чаще всего**, кэш Docker **вряд ли** будет использоваться для этого шагa или **последующих шагов**.
6. Укажите **команду**, запускающую сервер `uvicorn`.
Поэтому важно разместить этот шаг **ближе к концу** `Dockerfile`, чтобы оптимизировать время сборки образа контейнера.
`CMD` принимает список строк, разделённых запятыми, но при выполнении объединит их через пробел, собрав из них одну команду, которую вы могли бы написать в терминале.
6. Указываем **команду** для запуска `fastapi run`, под капотом используется Uvicorn.
Эта команда будет выполнена в **текущей рабочей директории**, а именно в директории `/code`, которая указана в команде `WORKDIR /code`.
`CMD` принимает список строк, каждая из которых — это то, что вы бы ввели в командной строке, разделяя пробелами.
Так как команда выполняется внутри директории `/code`, в которую мы поместили папку `./app` с приложением, то **Uvicorn** сможет найти и **импортировать** объект `app` из файла `app.main`.
Эта команда будет выполнена из **текущей рабочей директории**, той самой `/code`, которую вы задали выше `WORKDIR /code`.
/// tip | Подсказка
Если ткнёте на кружок с плюсом, то увидите пояснения. 👆
Посмотрите, что делает каждая строка, кликнув по номеру рядом со строкой. 👆
///
/// warning | Предупреждение
Всегда используйте **exec-форму** инструкции `CMD`, как описано ниже.
///
На данном этапе структура проекта должны выглядеть так:
#### Используйте `CMD` — exec-форма { #use-cmd-exec-form }
Инструкцию Docker <a href="https://docs.docker.com/reference/dockerfile/#cmd" class="external-link" target="_blank">`CMD`</a> можно писать в двух формах:
**Exec**-форма:
```Dockerfile
# ✅ Делайте так
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
⛔️ **Shell**-форма:
```Dockerfile
# ⛔️ Не делайте так
CMD fastapi run app/main.py --port 80
```
Обязательно используйте **exec**-форму, чтобы FastAPI мог корректно завершаться и чтобы срабатывали [события lifespan](../advanced/events.md){.internal-link target=_blank}.
Подробнее об этом читайте в <a href="https://docs.docker.com/reference/dockerfile/#shell-and-exec-form" class="external-link" target="_blank">документации Docker о shell- и exec-формах</a>.
Это особенно заметно при использовании `docker compose`. См. раздел FAQ Docker Compose с техническими подробностями: <a href="https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop" class="external-link" target="_blank">Почему мои сервисы пересоздаются или останавливаются 10 секунд?</a>.
#### Структура директорий { #directory-structure }
Теперь у вас должна быть такая структура:
```
.
@ -248,53 +275,52 @@ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
└── requirements.txt
```
#### Использование прокси-сервера
#### За прокси-сервером TLS терминации { #behind-a-tls-termination-proxy }
Если вы запускаете контейнер за прокси-сервером завершения TLS (балансирующего нагрузку), таким как Nginx или Traefik, добавьте опцию `--proxy-headers`, которая укажет Uvicorn, что он работает позади прокси-сервера и может доверять заголовкам отправляемым им.
Если вы запускаете контейнер за прокси-сервером завершения TLS (балансировщиком нагрузки), таким как Nginx или Traefik, добавьте опцию `--proxy-headers`. Это сообщит Uvicorn (через FastAPI CLI), что приложение работает за HTTPS и можно доверять соответствующим заголовкам.
```Dockerfile
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"]
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
```
#### Кэш Docker
#### Кэш Docker { #docker-cache }
В нашем `Dockerfile` использована полезная хитрость, когда сначала копируется **только файл с зависимостями**, а не вся папка с кодом приложения.
В этом `Dockerfile` есть важная хитрость: мы сначала копируем **только файл с зависимостями**, а не весь код. Вот зачем.
```Dockerfile
COPY ./requirements.txt /code/requirements.txt
```
Docker и подобные ему инструменты **создают** образы контейнеров **пошагово**, добавляя **один слой над другим**, начиная с первой строки `Dockerfile` и добавляя файлы, создаваемые при выполнении каждой инструкции из `Dockerfile`.
Docker и подобные инструменты **строят** образы контейнеров **инкрементально**, добавляя **слой за слоем**, начиная с первой строки `Dockerfile` и добавляя любые файлы, создаваемые каждой инструкцией `Dockerfile`.
При создании образа используется **внутренний кэш** и если в файлах нет изменений с момента последней сборки образа, то будет **переиспользован** ранее созданный слой образа, а не повторное копирование файлов и создание слоя с нуля.
Заметьте, что так как слой следующего шага зависит от слоя предыдущего, то изменения внесённые в промежуточный слой, также повлияют на последующие.
Docker и подобные инструменты также используют **внутренний кэш** при сборке образа: если файл не изменился с момента предыдущей сборки, будет **переиспользован слой**, созданный в прошлый раз, вместо повторного копирования файла и создания нового слоя с нуля.
Избегание копирования файлов не обязательно улучшит ситуацию, но использование кэша на одном шаге, позволит **использовать кэш и на следующих шагах**. Например, можно использовать кэш при установке зависимостей:
Само по себе избегание копирования всех файлов не всегда даёт много, но благодаря использованию кэша на этом шаге Docker сможет **использовать кэш и на следующем шаге**. Например, на шаге установки зависимостей:
```Dockerfile
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
```
Файл со списком зависимостей **изменяется довольно редко**. Так что выполнив команду копирования только этого файла, Docker сможет **использовать кэш** на этом шаге.
Файл с зависимостями **меняется нечасто**. Поэтому, копируя только его, Docker сможет **использовать кэш** для этого шага.
А затем **использовать кэш и на следующем шаге**, загружающем и устанавливающем зависимости. И вот тут-то мы и **сэкономим много времени**. ✨ ...а не будем томиться в тягостном ожидании. 😪😆
А затем Docker сможет **использовать кэш и на следующем шаге**, где скачиваются и устанавливаются зависимости. Здесь мы как раз **экономим много времени**. ✨ ...и не скучаем в ожидании. 😪😆
Для загрузки и установки необходимых библиотек **может понадобиться несколько минут**, но использование **кэша** занимает несколько **секунд** максимум.
Скачивание и установка зависимостей **может занять минуты**, но использование **кэша****секунды**.
И так как во время разработки вы будете часто пересобирать контейнер для проверки работоспособности внесённых изменений, то сэкономленные минуты сложатся в часы, а то и дни.
Поскольку во время разработки вы будете пересобирать образ снова и снова, чтобы проверить изменения в коде, суммарно это сэкономит немало времени.
Так как папка с кодом приложения **изменяется чаще всего**, то мы расположили её в конце `Dockerfile`, ведь после внесённых в код изменений кэш не будет использован на этом и следующих шагах.
Затем, ближе к концу `Dockerfile`, мы копируем весь код. Так как он **меняется чаще всего**, мы ставим этот шаг в конец, потому что почти всегда всё, что после него, уже не сможет использовать кэш.
```Dockerfile
COPY ./app /code/app
```
### Создать Docker-образ
### Собрать Docker-образ { #build-the-docker-image }
Теперь, когда все файлы на своих местах, давайте создадим образ контейнера.
Теперь, когда все файлы на месте, соберём образ контейнера.
* Перейдите в директорию проекта (в ту, где расположены `Dockerfile` и папка `app` с приложением).
* Создай образ приложения FastAPI:
* Перейдите в директорию проекта (где ваш `Dockerfile` и директория `app`).
* Соберите образ FastAPI:
<div class="termy">
@ -308,15 +334,15 @@ $ docker build -t myimage .
/// tip | Подсказка
Обратите внимание, что в конце написана точка - `.`, это то же самое что и `./`, тем самым мы указываем Docker директорию, из которой нужно выполнять сборку образа контейнера.
Обратите внимание на точку `.` в конце — это то же самое, что `./`. Так мы указываем Docker, из какой директории собирать образ контейнера.
В данном случае это та же самая директория (`.`).
В данном случае это текущая директория (`.`).
///
### Запуск Docker-контейнера
### Запустить Docker-контейнер { #start-the-docker-container }
* Запустите контейнер, основанный на вашем образе:
* Запустите контейнер на основе вашего образа:
<div class="termy">
@ -326,35 +352,35 @@ $ docker run -d --name mycontainer -p 80:80 myimage
</div>
## Проверка
## Проверка { #check-it }
Вы можете проверить, что Ваш Docker-контейнер работает перейдя по ссылке: <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> или <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (или похожей, которую использует Ваш Docker-хост).
Проверьте работу по адресу вашего Docker-хоста, например: <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> или <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (или аналогичный URL вашего Docker-хоста).
Там вы увидите:
Вы увидите что-то вроде:
```JSON
{"item_id": 5, "q": "somequery"}
```
## Интерактивная документация API
## Интерактивная документация API { #interactive-api-docs }
Теперь перейдите по ссылке <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> или <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a> (или похожей, которую использует Ваш Docker-хост).
Теперь зайдите на <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> или <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a> (или аналогичный URL вашего Docker-хоста).
Здесь вы увидите автоматическую интерактивную документацию API (предоставляемую <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
Вы увидите автоматическую интерактивную документацию API (на базе <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
## Альтернативная документация API
## Альтернативная документация API { #alternative-api-docs }
Также вы можете перейти по ссылке <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> or <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a> (или похожей, которую использует Ваш Docker-хост).
Также можно открыть <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> или <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a> (или аналогичный URL вашего Docker-хоста).
Здесь вы увидите альтернативную автоматическую документацию API (предоставляемую <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
Вы увидите альтернативную автоматическую документацию (на базе <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## Создание Docker-образа на основе однофайлового приложения FastAPI
## Собрать Docker-образ для однофайлового FastAPI { #build-a-docker-image-with-a-single-file-fastapi }
Если ваше приложение FastAPI помещено в один файл, например, `main.py` и структура Ваших файлов похожа на эту:
Если ваше приложение FastAPI — один файл, например `main.py` без директории `./app`, структура файлов может быть такой:
```
.
@ -363,7 +389,7 @@ $ docker run -d --name mycontainer -p 80:80 myimage
└── requirements.txt
```
Вам нужно изменить в `Dockerfile` соответствующие пути копирования файлов:
Тогда в `Dockerfile` нужно изменить пути копирования:
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.9
@ -374,360 +400,221 @@ COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (1)
# (1)!
COPY ./main.py /code/
# (2)
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
# (2)!
CMD ["fastapi", "run", "main.py", "--port", "80"]
```
1. Скопируйте непосредственно файл `main.py` в директорию `/code` (не указывайте `./app`).
1. Копируем файл `main.py` напрямую в `/code` (без директории `./app`).
2. При запуске Uvicorn укажите ему, что объект `app` нужно импортировать из файла `main` (вместо импортирования из `app.main`).
2. Используем `fastapi run` для запуска приложения из одного файла `main.py`.
Настройте Uvicorn на использование `main` вместо `app.main` для импорта объекта `app`.
Когда вы передаёте файл в `fastapi run`, он автоматически определит, что это одиночный файл, а не часть пакета, и поймёт, как его импортировать и запустить ваше FastAPI-приложение. 😎
## Концепции развёртывания
## Концепции развертывания { #deployment-concepts }
Давайте вспомним о [Концепциях развёртывания](concepts.md){.internal-link target=_blank} и применим их к контейнерам.
Ещё раз рассмотрим [концепции развертывания](concepts.md){.internal-link target=_blank} применительно к контейнерам.
Контейнеры - это, в основном, инструмент упрощающий **сборку и развёртывание** приложения и они не обязывают к применению какой-то определённой **концепции развёртывания**, а значит мы можем выбирать нужную стратегию.
Контейнеры главным образом упрощают **сборку и развёртывание** приложения, но не навязывают конкретный подход к этим **концепциям развертывания**, и существует несколько стратегий.
**Хорошая новость** в том, что независимо от выбранной стратегии, мы всё равно можем покрыть все концепции развёртывания. 🎉
**Хорошая новость** в том, что при любой стратегии есть способ охватить все концепции развертывания. 🎉
Рассмотрим эти **концепции развёртывания** применительно к контейнерам:
Рассмотрим эти **концепции развертывания** в терминах контейнеров:
* Использование более безопасного протокола HTTPS
* Настройки запуска приложения
* Перезагрузка приложения
* Запуск нескольких экземпляров приложения
* Управление памятью
* Использование перечисленных функций перед запуском приложения
* HTTPS
* Запуск при старте
* Перезапуски
* Репликация (количество запущенных процессов)
* Память
* Предварительные шаги перед запуском
## Использование более безопасного протокола HTTPS
## HTTPS { #https }
Если мы определимся, что **образ контейнера** будет содержать только приложение FastAPI, то работу с HTTPS можно организовать **снаружи** контейнера при помощи другого инструмента.
Если мы рассматриваем только **образ контейнера** для приложения FastAPI (и далее запущенный **контейнер**), то HTTPS обычно обрабатывается **внешним** инструментом.
Это может быть другой контейнер, в котором есть, например, <a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a>, работающий с **HTTPS** и **самостоятельно** обновляющий **сертификаты**.
Это может быть другой контейнер, например с <a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a>, который берёт на себя **HTTPS** и **автоматическое** получение **сертификатов**.
/// tip | Подсказка
Traefik совместим с Docker, Kubernetes и им подобными инструментами. Он очень прост в установке и настройке использования HTTPS для Ваших контейнеров.
У Traefik есть интеграции с Docker, Kubernetes и другими, поэтому очень легко настроить и сконфигурировать HTTPS для ваших контейнеров.
///
В качестве альтернативы, работу с HTTPS можно доверить облачному провайдеру, если он предоставляет такую услугу.
В качестве альтернативы HTTPS может быть реализован как сервис облачного провайдера (при этом приложение всё равно работает в контейнере).
## Настройки запуска и перезагрузки приложения
## Запуск при старте и перезапуски { #running-on-startup-and-restarts }
Обычно **запуском контейнера с приложением** занимается какой-то отдельный инструмент.
Обычно есть другой инструмент, отвечающий за **запуск и работу** вашего контейнера.
Это может быть сам **Docker**, **Docker Compose**, **Kubernetes**, **облачный провайдер** и т.п.
Это может быть сам **Docker**, **Docker Compose**, **Kubernetes**, **облачный сервис** и т.п.
В большинстве случаев это простейшие настройки запуска и перезагрузки приложения (при падении). Например, команде запуска Docker-контейнера можно добавить опцию `--restart`.
В большинстве (или во всех) случаев есть простая опция, чтобы включить запуск контейнера при старте системы и перезапуски при сбоях. Например, в Docker это опция командной строки `--restart`.
Управление запуском и перезагрузкой приложений без использования контейнеров - весьма затруднительно. Но при **работе с контейнерами** - это всего лишь функционал доступный по умолчанию. ✨
Без контейнеров обеспечить запуск при старте и перезапуски может быть сложно. Но при **работе с контейнерами** в большинстве случаев этот функционал доступен по умолчанию. ✨
## Запуск нескольких экземпляров приложения - Указание количества процессов
## Репликация — количество процессов { #replication-number-of-processes }
Если у вас есть <abbr title="Несколько серверов настроенных для совместной работы.">кластер</abbr> машин под управлением **Kubernetes**, Docker Swarm Mode, Nomad или аналогичной сложной системой оркестрации контейнеров, скорее всего, вместо использования менеджера процессов (типа Gunicorn и его воркеры) в каждом контейнере, вы захотите **управлять количеством запущенных экземпляров приложения** на **уровне кластера**.
Если у вас есть <abbr title="Группа машин, настроенных так, чтобы быть соединенными и работать вместе определенным образом.">кластер</abbr> машин с **Kubernetes**, Docker Swarm Mode, Nomad или другой похожей системой для управления распределёнными контейнерами на нескольких машинах, скорее всего вы будете **управлять репликацией** на **уровне кластера**, а не использовать **менеджер процессов** (например, Uvicorn с воркерами) в каждом контейнере.
В любую из этих систем управления контейнерами обычно встроен способ управления **количеством запущенных контейнеров** для распределения **нагрузки** от входящих запросов на **уровне кластера**.
Одна из таких систем управления распределёнными контейнерами, как Kubernetes, обычно имеет встроенный способ управлять **репликацией контейнеров**, поддерживая **балансировку нагрузки** для входящих запросов — всё это на **уровне кластера**.
В такой ситуации Вы, вероятно, захотите создать **образ Docker**, как [описано выше](#dockerfile), с установленными зависимостями и запускающий **один процесс Uvicorn** вместо того, чтобы запускать Gunicorn управляющий несколькими воркерами Uvicorn.
В таких случаях вы, скорее всего, захотите собрать **Docker-образ с нуля**, как [описано выше](#dockerfile), установить зависимости и запускать **один процесс Uvicorn** вместо множества воркеров Uvicorn.
### Балансировщик нагрузки
### Балансировщик нагрузки { #load-balancer }
Обычно при использовании контейнеров один компонент **прослушивает главный порт**. Это может быть контейнер содержащий **прокси-сервер завершения работы TLS** для работы с **HTTPS** или что-то подобное.
При использовании контейнеров обычно есть компонент, **слушающий главный порт**. Это может быть другой контейнер — **прокси завершения TLS** для обработки **HTTPS** или похожий инструмент.
Поскольку этот компонент **принимает запросы** и равномерно **распределяет** их между компонентами, его также называют **балансировщиком нагрузки**.
Поскольку этот компонент принимает **нагрузку** запросов и распределяет её между воркерами **сбалансированно**, его часто называют **балансировщиком нагрузки**.
/// tip | Подсказка
**Прокси-сервер завершения работы TLS** одновременно может быть **балансировщиком нагрузки**.
Тот же компонент **прокси завершения TLS**, который обрабатывает HTTPS, скорее всего также будет **балансировщиком нагрузки**.
///
Система оркестрации, которую вы используете для запуска и управления контейнерами, имеет встроенный инструмент **сетевого взаимодействия** (например, для передачи HTTP-запросов) между контейнерами с Вашими приложениями и **балансировщиком нагрузки** (который также может быть **прокси-сервером**).
При работе с контейнерами система, которую вы используете для запуска и управления ими, уже имеет внутренние средства для передачи **сетевого взаимодействия** (например, HTTP-запросов) от **балансировщика нагрузки** (который также может быть **прокси завершения TLS**) к контейнеру(-ам) с вашим приложением.
### Один балансировщик - Множество контейнеров
### Один балансировщик — несколько контейнеров-воркеров { #one-load-balancer-multiple-worker-containers }
При работе с **Kubernetes** или аналогичными системами оркестрации использование их внутренней сети позволяет иметь один **балансировщик нагрузки**, который прослушивает **главный** порт и передаёт запросы **множеству запущенных контейнеров** с Вашими приложениями.
При работе с **Kubernetes** или похожими системами управления распределёнными контейнерами их внутренние механизмы сети позволяют одному **балансировщику нагрузки**, слушающему главный **порт**, передавать запросы в **несколько контейнеров**, где запущено ваше приложение.
В каждом из контейнеров обычно работает **только один процесс** (например, процесс Uvicorn управляющий Вашим приложением FastAPI). Контейнеры могут быть **идентичными**, запущенными на основе одного и того же образа, но у каждого будут свои отдельные процесс, память и т.п. Таким образом мы получаем преимущества **распараллеливания** работы по **разным ядрам** процессора или даже **разным машинам**.
Каждый такой контейнер с вашим приложением обычно имеет **только один процесс** (например, процесс Uvicorn с вашим приложением FastAPI). Все они — **одинаковые контейнеры**, запускающие одно и то же, но у каждого свой процесс, память и т.п. Так вы используете **параллелизм** по **разным ядрам** CPU или даже **разным машинам**.
Система управления контейнерами с **балансировщиком нагрузки** будет **распределять запросы** к контейнерам с приложениями **по очереди**. То есть каждый запрос будет обработан одним из множества **одинаковых контейнеров** с одним и тем же приложением.
Система распределённых контейнеров с **балансировщиком нагрузки** будет **распределять запросы** между контейнерами с вашим приложением **по очереди**. То есть каждый запрос может обрабатываться одним из нескольких **реплицированных контейнеров**.
**Балансировщик нагрузки** может обрабатывать запросы к *разным* приложениям, расположенным в вашем кластере (например, если у них разные домены или префиксы пути) и передавать запросы нужному контейнеру с требуемым приложением.
Обычно такой **балансировщик нагрузки** может также обрабатывать запросы к *другим* приложениям в вашем кластере (например, к другому домену или под другим префиксом пути URL) и направлять их к нужным контейнерам этого *другого* приложения.
### Один процесс на контейнер
### Один процесс на контейнер { #one-process-per-container }
В этом варианте **в одном контейнере будет запущен только один процесс (Uvicorn)**, а управление изменением количества запущенных копий приложения происходит на уровне кластера.
В таком сценарии, скорее всего, вы захотите иметь **один (Uvicorn) процесс на контейнер**, так как репликация уже управляется на уровне кластера.
Здесь **не нужен** менеджер процессов типа Gunicorn, управляющий процессами Uvicorn, или же Uvicorn, управляющий другими процессами Uvicorn. Достаточно **только одного процесса Uvicorn** на контейнер (но запуск нескольких процессов не запрещён).
Поэтому в контейнере **не нужно** поднимать несколько воркеров, например через опцию командной строки `--workers`. Нужен **один процесс Uvicorn** на контейнер (но, возможно, несколько контейнеров).
Использование менеджера процессов (Gunicorn или Uvicorn) внутри контейнера только добавляет **излишнее усложнение**, так как управление следует осуществлять системой оркестрации.
Наличие отдельного менеджера процессов внутри контейнера (как при нескольких воркерах) только добавит **лишнюю сложность**, которую, вероятно, уже берёт на себя ваша кластерная система.
### Множество процессов внутри контейнера для особых случаев
### Контейнеры с несколькими процессами и особые случаи { #containers-with-multiple-processes-and-special-cases }
Безусловно, бывают **особые случаи**, когда может понадобиться внутри контейнера запускать **менеджер процессов Gunicorn**, управляющий несколькими **процессами Uvicorn**.
Конечно, есть **особые случаи**, когда может понадобиться **контейнер** с несколькими **воркерами Uvicorn** внутри.
Для таких случаев вы можете использовать **официальный Docker-образ** (прим. пер: - *здесь и далее на этой странице, если вы встретите сочетание "официальный Docker-образ" без уточнений, то автор имеет в виду именно предоставляемый им образ*), где в качестве менеджера процессов используется **Gunicorn**, запускающий несколько **процессов Uvicorn** и некоторые настройки по умолчанию, автоматически устанавливающие количество запущенных процессов в зависимости от количества ядер вашего процессора. Я расскажу вам об этом подробнее тут: [Официальный Docker-образ со встроенными Gunicorn и Uvicorn](#docker-gunicorn-uvicorn).
В таких случаях вы можете использовать опцию командной строки `--workers`, чтобы указать нужное количество воркеров:
Некоторые примеры подобных случаев:
```{ .dockerfile .annotate }
FROM python:3.9
#### Простое приложение
WORKDIR /code
Вы можете использовать менеджер процессов внутри контейнера, если ваше приложение **настолько простое**, что у вас нет необходимости (по крайней мере, пока нет) в тщательных настройках количества процессов и вам достаточно имеющихся настроек по умолчанию (если используется официальный Docker-образ) для запуска приложения **только на одном сервере**, а не в кластере.
COPY ./requirements.txt /code/requirements.txt
#### Docker Compose
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
С помощью **Docker Compose** можно разворачивать несколько контейнеров на **одном сервере** (не кластере), но при это у вас не будет простого способа управления количеством запущенных контейнеров с одновременным сохранением общей сети и **балансировки нагрузки**.
COPY ./app /code/app
В этом случае можно использовать **менеджер процессов**, управляющий **несколькими процессами**, внутри **одного контейнера**.
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
```
#### Prometheus и прочие причины
1. Здесь мы используем опцию `--workers`, чтобы установить число воркеров равным 4.
У вас могут быть и **другие причины**, когда использование **множества процессов** внутри **одного контейнера** будет проще, нежели запуск **нескольких контейнеров** с **единственным процессом** в каждом из них.
Примеры, когда это может быть уместно:
Например (в зависимости от конфигурации), у вас могут быть инструменты подобные экспортёру Prometheus, которые должны иметь доступ к **каждому запросу** приходящему в контейнер.
#### Простое приложение { #a-simple-app }
Если у вас будет **несколько контейнеров**, то Prometheus, по умолчанию, **при сборе метрик** получит их **только с одного контейнера**, который обрабатывает конкретный запрос, вместо **сбора метрик** со всех работающих контейнеров.
Вам может понадобиться менеджер процессов в контейнере, если приложение **достаточно простое**, чтобы запускаться на **одном сервере**, а не в кластере.
В таком случае может быть проще иметь **один контейнер** со **множеством процессов**, с нужным инструментом (таким как экспортёр Prometheus) в этом же контейнере и собирающем метрики со всех внутренних процессов этого контейнера.
#### Docker Compose { #docker-compose }
Вы можете развёртывать на **одном сервере** (не кластере) с **Docker Compose**, и у вас не будет простого способа управлять репликацией контейнеров (в Docker Compose), сохраняя общую сеть и **балансировку нагрузки**.
Тогда вы можете захотеть **один контейнер** с **менеджером процессов**, который запускает **несколько воркеров** внутри.
---
Самое главное - **ни одно** из перечисленных правил не является **высеченным на камне** и вы не обязаны слепо их повторять. вы можете использовать эти идеи при **рассмотрении вашего конкретного случая** и самостоятельно решать, какая из концепции подходит лучше:
Главное — **ни одно** из этих правил не является **строго обязательным**. Используйте эти идеи, чтобы **оценить свой конкретный случай** и решить, какой подход лучше для вашей системы, учитывая:
* Использование более безопасного протокола HTTPS
* Настройки запуска приложения
* Перезагрузка приложения
* Запуск нескольких экземпляров приложения
* Управление памятью
* Использование перечисленных функций перед запуском приложения
* Безопасность — HTTPS
* Запуск при старте
* Перезапуски
* Репликацию (количество запущенных процессов)
* Память
* Предварительные шаги перед запуском
## Управление памятью
## Память { #memory }
При **запуске одного процесса на контейнер** вы получаете относительно понятный, стабильный и ограниченный объём памяти, потребляемый одним контейнером.
Если вы запускаете **один процесс на контейнер**, у каждого контейнера будет более-менее чётко определённый, стабильный и ограниченный объём потребляемой памяти (контейнеров может быть несколько при репликации).
Вы можете установить аналогичные ограничения по памяти при конфигурировании своей системы управления контейнерами (например, **Kubernetes**). Таким образом система сможет **изменять количество контейнеров** на **доступных ей машинах** приводя в соответствие количество памяти нужной контейнерам с количеством памяти доступной в кластере (наборе доступных машин).
Затем вы можете задать такие же лимиты и требования по памяти в конфигурации вашей системы управления контейнерами (например, в **Kubernetes**). Так система сможет **реплицировать контейнеры** на **доступных машинах**, учитывая объём необходимой памяти и доступной памяти в машинах кластера.
Если у вас **простенькое** приложение, вероятно у вас не будет **необходимости** устанавливать жёсткие ограничения на выделяемую ему память. Но если приложение **использует много памяти** (например, оно использует модели **машинного обучения**), вам следует проверить, как много памяти ему требуется и отрегулировать **количество контейнеров** запущенных на **каждой машине** (может быть даже добавить машин в кластер).
Если приложение **простое**, это, вероятно, **не будет проблемой**, и жёсткие лимиты памяти можно не указывать. Но если вы **используете много памяти** (например, с моделями **Машинного обучения**), проверьте, сколько памяти потребляется, и отрегулируйте **число контейнеров** на **каждой машине** (и, возможно, добавьте машины в кластер).
Если вы запускаете **несколько процессов в контейнере**, то должны быть уверены, что эти процессы не **займут памяти больше**, чем доступно для контейнера.
Если вы запускаете **несколько процессов в контейнере**, нужно убедиться, что их суммарное потребление **не превысит доступную память**.
## Подготовительные шаги при запуске контейнеров
## Предварительные шаги перед запуском и контейнеры { #previous-steps-before-starting-and-containers }
Есть два основных подхода, которые вы можете использовать при запуске контейнеров (Docker, Kubernetes и т.п.).
Если вы используете контейнеры (например, Docker, Kubernetes), есть два основных подхода.
### Множество контейнеров
### Несколько контейнеров { #multiple-containers }
Когда вы запускаете **множество контейнеров**, в каждом из которых работает **только один процесс** (например, в кластере **Kubernetes**), может возникнуть необходимость иметь **отдельный контейнер**, который осуществит **предварительные шаги перед запуском** остальных контейнеров (например, применяет миграции к базе данных).
Если у вас **несколько контейнеров**, и, вероятно, каждый запускает **один процесс** (например, в кластере **Kubernetes**), то вы, скорее всего, захотите иметь **отдельный контейнер**, выполняющий **предварительные шаги** в одном контейнере и одном процессе **до** запуска реплицированных контейнеров-воркеров.
/// info | Информация
При использовании Kubernetes, это может быть <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Инициализирующий контейнер</a>.
Если вы используете Kubernetes, это, вероятно, будет <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Init Container</a>.
///
При отсутствии такой необходимости (допустим, не нужно применять миграции к базе данных, а только проверить, что она готова принимать соединения), вы можете проводить такую проверку в каждом контейнере перед запуском его основного процесса и запускать все контейнеры **одновременно**.
### Только один контейнер
Если у вас несложное приложение для работы которого достаточно **одного контейнера**, но в котором работает **несколько процессов** (или один процесс), то прохождение предварительных шагов можно осуществить в этом же контейнере до запуска основного процесса. Официальный Docker-образ поддерживает такие действия.
Если в вашем случае нет проблемы с тем, чтобы выполнять эти предварительные шаги **многократно и параллельно** (например, вы не запускаете миграции БД, а только проверяете готовность БД), вы можете просто выполнить их в каждом контейнере прямо перед стартом основного процесса.
## Официальный Docker-образ с Gunicorn и Uvicorn
### Один контейнер { #single-container }
Я подготовил для вас Docker-образ, в который включён Gunicorn управляющий процессами (воркерами) Uvicorn, в соответствии с концепциями рассмотренными в предыдущей главе: [Рабочие процессы сервера (воркеры) - Gunicorn совместно с Uvicorn](server-workers.md){.internal-link target=_blank}.
Если у вас простая схема с **одним контейнером**, который затем запускает несколько **воркеров** (или один процесс), можно выполнить подготовительные шаги в этом же контейнере непосредственно перед запуском процесса с приложением.
Этот образ может быть полезен для ситуаций описанных тут: [Множество процессов внутри контейнера для особых случаев](#_11).
* <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
/// warning | Предупреждение
### Базовый Docker-образ { #base-docker-image }
Скорее всего у вас **нет необходимости** в использовании этого образа или подобного ему и лучше создать свой образ с нуля как описано тут: [Создать Docker-образ для FastAPI](#docker-fastapi).
Ранее существовал официальный Docker-образ FastAPI: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. Сейчас он помечен как устаревший. ⛔️
///
Скорее всего, вам **не стоит** использовать этот базовый образ (или какой-либо аналогичный).
В этом образе есть **автоматический** механизм подстройки для запуска **необходимого количества процессов** в соответствии с доступным количеством ядер процессора.
Если вы используете **Kubernetes** (или другое) и уже настраиваете **репликацию** на уровне кластера через несколько **контейнеров**, в этих случаях лучше **собрать образ с нуля**, как описано выше: [Создать Docker-образ для FastAPI](#build-a-docker-image-for-fastapi).
В нём установлены **разумные значения по умолчанию**, но можно изменять и обновлять конфигурацию с помощью **переменных окружения** или конфигурационных файлов.
А если вам нужны несколько воркеров, просто используйте опцию командной строки `--workers`.
Он также поддерживает прохождение <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker#pre_start_path" class="external-link" target="_blank">**Подготовительных шагов при запуске контейнеров**</a> при помощи скрипта.
/// note | Технические подробности
/// tip | Подсказка
Этот Docker-образ был создан в то время, когда Uvicorn не умел управлять и перезапускать «упавших» воркеров, и приходилось использовать Gunicorn вместе с Uvicorn, что добавляло заметную сложность, лишь бы Gunicorn управлял и перезапускал воркеров Uvicorn.
Для просмотра всех возможных настроек перейдите на страницу этого Docker-образа: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
Но теперь, когда Uvicorn (и команда `fastapi`) поддерживают `--workers`, нет причин использовать базовый Docker-образ вместо сборки своего (кода получается примерно столько же 😅).
///
### Количество процессов в официальном Docker-образе
**Количество процессов** в этом образе **вычисляется автоматически** и зависит от доступного количества **ядер** центрального процессора.
Это означает, что он будет пытаться **выжать** из процессора как можно больше **производительности**.
Но вы можете изменять и обновлять конфигурацию с помощью **переменных окружения** и т.п.
Поскольку количество процессов зависит от процессора, на котором работает контейнер, **объём потребляемой памяти** также будет зависеть от этого.
А значит, если вашему приложению требуется много оперативной памяти (например, оно использует модели машинного обучения) и Ваш сервер имеет центральный процессор с большим количеством ядер, но **не слишком большим объёмом оперативной памяти**, то может дойти до того, что контейнер попытается занять памяти больше, чем доступно, из-за чего будет падение производительности (или сервер вовсе упадёт). 🚨
### Написание `Dockerfile`
Итак, теперь мы можем написать `Dockerfile` основанный на этом официальном Docker-образе:
```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
```
### Большие приложения
Если вы успели ознакомиться с разделом [Приложения содержащие много файлов](../tutorial/bigger-applications.md){.internal-link target=_blank}, состоящие из множества файлов, Ваш Dockerfile может выглядеть так:
```Dockerfile hl_lines="7"
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9
COPY ./requirements.txt /app/requirements.txt
## Развёртывание образа контейнера { #deploy-the-container-image }
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app/app
```
### Как им пользоваться
Если вы используете **Kubernetes** (или что-то вроде того), скорее всего вам **не нужно** использовать официальный Docker-образ (или другой похожий) в качестве основы, так как управление **количеством запущенных контейнеров** должно быть настроено на уровне кластера. В таком случае лучше **создать образ с нуля**, как описано в разделе Создать [Docker-образ для FastAPI](#docker-fastapi).
Официальный образ может быть полезен в отдельных случаях, описанных выше в разделе [Множество процессов внутри контейнера для особых случаев](#_11). Например, если ваше приложение **достаточно простое**, не требует запуска в кластере и способно уместиться в один контейнер, то его настройки по умолчанию будут работать довольно хорошо. Или же вы развертываете его с помощью **Docker Compose**, работаете на одном сервере и т. д
## Развёртывание образа контейнера
После создания образа контейнера существует несколько способов его развёртывания.
После того как у вас есть образ контейнера (Docker), его можно развёртывать несколькими способами.
Например:
* С использованием **Docker Compose** при развёртывании на одном сервере
* С использованием **Kubernetes** в кластере
* С использованием режима Docker Swarm в кластере
* С использованием других инструментов, таких как Nomad
* С использованием облачного сервиса, который будет управлять разворачиванием вашего контейнера
## Docker-образ и Poetry
Если вы пользуетесь <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a> для управления зависимостями вашего проекта, то можете использовать многоэтапную сборку образа:
```{ .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. Это первый этап, которому мы дадим имя `requirements-stage`.
2. Установите директорию `/tmp` в качестве рабочей директории.
* С **Docker Compose** на одном сервере
* В кластере **Kubernetes**
* В кластере Docker Swarm Mode
* С другим инструментом, например Nomad
* С облачным сервисом, который принимает ваш образ контейнера и разворачивает его
В ней будет создан файл `requirements.txt`
3. На этом шаге установите Poetry.
4. Скопируйте файлы `pyproject.toml` и `poetry.lock` в директорию `/tmp`.
Поскольку название файла написано как `./poetry.lock*``*` в конце), то ничего не сломается, если такой файл не будет найден.
5. Создайте файл `requirements.txt`.
6. Это второй (и последний) этап сборки, который и создаст окончательный образ контейнера.
7. Установите директорию `/code` в качестве рабочей.
8. Скопируйте файл `requirements.txt` в директорию `/code`.
Этот файл находится в образе, созданном на предыдущем этапе, которому мы дали имя requirements-stage, потому при копировании нужно написать `--from-requirements-stage`.
9. Установите зависимости, указанные в файле `requirements.txt`.
10. Скопируйте папку `app` в папку `/code`.
11. Запустите `uvicorn`, указав ему использовать объект `app`, расположенный в `app.main`.
/// tip | Подсказка
Если ткнёте на кружок с плюсом, то увидите объяснения, что происходит в этой строке.
///
**Этапы сборки Docker-образа** являются частью `Dockerfile` и работают как **временные образы контейнеров**. Они нужны только для создания файлов, используемых в дальнейших этапах.
Первый этап был нужен только для **установки Poetry** и **создания файла `requirements.txt`**, в которым прописаны зависимости вашего проекта, взятые из файла `pyproject.toml`.
На **следующем этапе** `pip` будет использовать файл `requirements.txt`.
В итоговом образе будет содержаться **только последний этап сборки**, предыдущие этапы будут отброшены.
При использовании Poetry, имеет смысл использовать **многоэтапную сборку Docker-образа**, потому что на самом деле вам не нужен Poetry и его зависимости в окончательном образе контейнера, вам **нужен только** сгенерированный файл `requirements.txt` для установки зависимостей вашего проекта.
А на последнем этапе, придерживаясь описанных ранее правил, создаётся итоговый образ
### Использование прокси-сервера завершения TLS и Poetry
И снова повторюсь, если используете прокси-сервер (балансировщик нагрузки), такой как Nginx или Traefik, добавьте в команду запуска опцию `--proxy-headers`:
```Dockerfile
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"]
```
## Docker-образ с `uv` { #docker-image-with-uv }
## Резюме
Если вы используете <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a> для установки и управления проектом, следуйте их <a href="https://docs.astral.sh/uv/guides/integration/docker/" class="external-link" target="_blank">руководству по Docker для uv</a>.
При помощи систем контейнеризации (таких, как **Docker** и **Kubernetes**), становится довольно просто обрабатывать все **концепции развертывания**:
## Резюме { #recap }
* Использование более безопасного протокола HTTPS
* Настройки запуска приложения
* Перезагрузка приложения
* Запуск нескольких экземпляров приложения
* Управление памятью
* Использование перечисленных функций перед запуском приложения
Используя системы контейнеризации (например, **Docker** и **Kubernetes**), довольно просто закрыть все **концепции развертывания**:
В большинстве случаев Вам, вероятно, не нужно использовать какой-либо базовый образ, **лучше создать образ контейнера с нуля** на основе официального Docker-образа Python.
* HTTPS
* Запуск при старте
* Перезапуски
* Репликация (количество запущенных процессов)
* Память
* Предварительные шаги перед запуском
Позаботившись о **порядке написания** инструкций в `Dockerfile`, вы сможете использовать **кэш Docker'а**, **минимизировав время сборки**, максимально повысив свою производительность (и не заскучать). 😎
В большинстве случаев вы, вероятно, не захотите использовать какой-либо базовый образ, а вместо этого **соберёте образ контейнера с нуля** на основе официального Docker-образа Python.
В некоторых особых случаях вы можете использовать официальный образ Docker для FastAPI. 🤓
Заботясь о **порядке** инструкций в `Dockerfile`и используя **кэш Docker**, вы можете **минимизировать время сборки**, чтобы повысить продуктивность (и не скучать). 😎

248
docs/ru/docs/deployment/https.md

@ -1,207 +1,231 @@
# Об HTTPS
# Об HTTPS { #about-https }
Обычно представляется, что HTTPS это некая опция, которая либо "включена", либо нет.
Легко предположить, что HTTPS — это что-то, что просто «включено» или нет.
Но всё несколько сложнее.
Но на самом деле всё гораздо сложнее.
/// tip | Заметка
/// tip | Совет
Если вы торопитесь или вам не интересно, можете перейти на следующую страницу этого пошагового руководства по размещению приложений на серверах с использованием различных технологий.
Если вы торопитесь или вам это не важно, переходите к следующим разделам с пошаговыми инструкциями по настройке всего разными способами.
///
Чтобы **изучить основы HTTPS** для клиента, перейдите по ссылке <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>.
Здесь же представлены некоторые концепции, которые **разработчик** должен иметь в виду при размышлениях об HTTPS:
* Протокол HTTPS предполагает, что **серверу** нужно **располагать "сертификатами"** сгенерированными **третьей стороной**.
* На самом деле эти сертификаты **приобретены** у третьей стороны, а не "сгенерированы".
* У сертификатов есть **срок годности**.
* Срок годности **истекает**.
* По истечении срока годности их нужно **обновить**, то есть **снова получить** у третьей стороны.
* Шифрование соединения происходит **на уровне протокола TCP**.
* Протокол TCP находится на один уровень **ниже протокола HTTP**.
* Поэтому **проверка сертификатов и шифрование** происходит **до HTTP**.
* **TCP не знает о "доменах"**, но знает об IP-адресах.
* Информация о **запрашиваемом домене** извлекается из запроса **на уровне HTTP**.
* **Сертификаты HTTPS** "сертифицируют" **конкретный домен**, но проверка сертификатов и шифрование данных происходит на уровне протокола TCP, то есть **до того**, как станет известен домен-получатель данных.
* **По умолчанию** это означает, что у вас может быть **только один сертификат HTTPS на один IP-адрес**.
* Не важно, насколько большой у вас сервер и насколько маленькие приложения на нём могут быть.
* Однако, у этой проблемы есть **решение**.
* Существует **расширение** протокола **TLS** (который работает на уровне TCP, то есть до HTTP) называемое **<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Указание имени сервера">SNI</abbr></a>**.
* Расширение SNI позволяет одному серверу (с **одним IP-адресом**) иметь **несколько сертификатов HTTPS** и обслуживать **множество HTTPS-доменов/приложений**.
* Чтобы эта конструкция работала, **один** её компонент (программа) запущенный на сервере и слушающий **публичный IP-адрес**, должен иметь **все сертификаты HTTPS** для этого сервера.
* **После** установления защищённого соединения, протоколом передачи данных **остаётся HTTP**.
* Но данные теперь **зашифрованы**, несмотря на то, что они передаются по **протоколу HTTP**.
Обычной практикой является иметь **одну программу/HTTP-сервер** запущенную на сервере (машине, хосте и т.д.) и **ответственную за всю работу с HTTPS**:
* получение **зашифрованных HTTPS-запросов**
* отправка **расшифрованных HTTP запросов** в соответствующее HTTP-приложение, работающее на том же сервере (в нашем случае, это приложение **FastAPI**)
* получение **HTTP-ответа** от приложения
* **шифрование ответа** используя подходящий **сертификат HTTPS**
* отправка зашифрованного **HTTPS-ответа клиенту**.
Такой сервер часто называют **<a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">Прокси-сервер завершения работы TLS</a>** или просто "прокси-сервер".
Вот некоторые варианты, которые вы можете использовать в качестве такого прокси-сервера:
* Traefik (может обновлять сертификаты)
* Caddy (может обновлять сертификаты)
Чтобы **изучить основы HTTPS** с точки зрения пользователя, загляните на <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>.
Теперь, со стороны **разработчика**, вот несколько вещей, которые стоит держать в голове, размышляя об HTTPS:
* Для HTTPS **серверу** нужно **иметь «сертификаты»**, сгенерированные **третьей стороной**.
* Эти сертификаты на самом деле **приобретаются** у третьей стороны, а не «генерируются».
* У сертификатов есть **срок действия**.
* Они **истекают**.
* После этого их нужно **обновлять**, то есть **получать заново** у третьей стороны.
* Шифрование соединения происходит на **уровне TCP**.
* Это на один уровень **ниже HTTP**.
* Поэтому **сертификаты и шифрование** обрабатываются **до HTTP**.
* **TCP не знает о «доменах»**. Только об IP-адресах.
* Информация о **конкретном домене** передаётся в **данных HTTP**.
* **HTTPS-сертификаты** «сертифицируют» **определённый домен**, но протокол и шифрование происходят на уровне TCP, **до того как** становится известен домен, с которым идёт работа.
* **По умолчанию** это означает, что вы можете иметь **лишь один HTTPS-сертификат на один IP-адрес**.
* Неважно, насколько мощный у вас сервер или насколько маленькие приложения на нём работают.
* Однако у этого есть **решение**.
* Есть **расширение** протокола **TLS** (того самого, что занимается шифрованием на уровне TCP, до HTTP) под названием **<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication – Указание имени сервера">SNI</abbr></a>**.
* Это расширение SNI позволяет одному серверу (с **одним IP-адресом**) иметь **несколько HTTPS-сертификатов** и обслуживать **несколько HTTPS-доменов/приложений**.
* Чтобы это работало, **один** компонент (программа), запущенный на сервере и слушающий **публичный IP-адрес**, должен иметь **все HTTPS-сертификаты** на этом сервере.
* **После** установления защищённого соединения, протокол обмена данными — **всё ещё HTTP**.
* Содержимое **зашифровано**, несмотря на то, что оно отправляется по **протоколу HTTP**.
Обычно на сервере (машине, хосте и т.п.) запускают **одну программу/HTTP‑сервер**, которая **управляет всей частью, связанной с HTTPS**: принимает **зашифрованные HTTPS-запросы**, отправляет **расшифрованные HTTP-запросы** в само HTTP‑приложение, работающее на том же сервере (в нашем случае это приложение **FastAPI**), получает **HTTP-ответ** от приложения, **шифрует его** с использованием подходящего **HTTPS‑сертификата** и отправляет клиенту по **HTTPS**. Такой сервер часто называют **<a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">прокси‑сервером TLS-терминации</a>**.
Некоторые варианты, которые вы можете использовать как прокси‑сервер TLS-терминации:
* Traefik (умеет обновлять сертификаты)
* Caddy (умеет обновлять сертификаты)
* Nginx
* HAProxy
## Let's Encrypt (центр сертификации)
## Let's Encrypt { #lets-encrypt }
До появления Let's Encrypt **сертификаты HTTPS** приходилось покупать у третьих сторон.
До появления Let's Encrypt эти **HTTPS-сертификаты** продавались доверенными третьими сторонами.
Процесс получения такого сертификата был трудоёмким, требовал предоставления подтверждающих документов и сертификаты стоили дорого.
Процесс получения таких сертификатов был неудобным, требовал бумажной волокиты, а сами сертификаты были довольно дорогими.
Но затем консорциумом Linux Foundation был создан проект **<a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a>**.
Затем появился **<a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a>**.
Он автоматически предоставляет **бесплатные сертификаты HTTPS**. Эти сертификаты используют все стандартные криптографические способы шифрования. Они имеют небольшой срок годности (около 3 месяцев), благодаря чему они даже **более безопасны**.
Это проект Linux Foundation. Он предоставляет **HTTPS‑сертификаты бесплатно**, в автоматическом режиме. Эти сертификаты используют стандартные криптографические механизмы и имеют короткий срок действия (около 3 месяцев), поэтому **безопасность фактически выше** благодаря уменьшенному сроку жизни.
При запросе на получение сертификата, он автоматически генерируется и домен проверяется на безопасность. Это позволяет обновлять сертификаты автоматически.
Домены безопасно проверяются, а сертификаты выдаются автоматически. Это также позволяет автоматизировать процесс их продления.
Суть идеи в автоматическом получении и обновлении этих сертификатов, чтобы все могли пользоваться **безопасным HTTPS. Бесплатно. В любое время.**
Идея — автоматизировать получение и продление сертификатов, чтобы у вас был **безопасный HTTPS, бесплатно и навсегда**.
## HTTPS для разработчиков
## HTTPS для разработчиков { #https-for-developers }
Ниже, шаг за шагом, с заострением внимания на идеях, важных для разработчика, описано, как может выглядеть HTTPS API.
Ниже приведён пример того, как может выглядеть HTTPS‑API, шаг за шагом, с акцентом на идеях, важных для разработчиков.
### Имя домена
### Имя домена { #domain-name }
Чаще всего, всё начинается с **приобретения имени домена**. Затем нужно настроить DNS-сервер (вероятно у того же провайдера, который выдал вам домен).
Чаще всего всё начинается с **приобретения** **имени домена**. Затем вы настраиваете его на DNS‑сервере (возможно, у того же облачного провайдера).
Далее, возможно, вы получаете "облачный" сервер (виртуальную машину) или что-то типа этого, у которого есть <abbr title="Не изменяемый">постоянный</abbr> **публичный IP-адрес**.
Скорее всего, вы получите облачный сервер (виртуальную машину) или что-то подобное, и у него будет <abbr title="Не изменяется">постоянный</abbr> **публичный IP-адрес**.
На DNS-сервере (серверах) вам следует настроить соответствующую ресурсную запись ("`запись A`"), указав, что **Ваш домен** связан с публичным **IP-адресом вашего сервера**.
На DNS‑сервере(ах) вы настроите запись («`A record`» - запись типа A), указывающую, что **ваш домен** должен указывать на публичный **IP‑адрес вашего сервера**.
Обычно эту запись достаточно указать один раз, при первоначальной настройке всего сервера.
Обычно это делается один раз — при первоначальной настройке всего.
/// tip | Заметка
/// tip | Совет
Уровни протоколов, работающих с именами доменов, намного ниже HTTPS, но об этом следует упомянуть здесь, так как всё зависит от доменов и IP-адресов.
Часть про доменное имя относится к этапам задолго до HTTPS, но так как всё зависит от домена и IP‑адреса, здесь стоит это упомянуть.
///
### DNS
### DNS { #dns }
Теперь давайте сфокусируемся на работе с HTTPS.
Теперь сфокусируемся на собственно частях, связанных с HTTPS.
Всё начинается с того, что браузер спрашивает у **DNS-серверов**, какой **IP-адрес связан с доменом**, для примера возьмём домен `someapp.example.com`.
Сначала браузер спросит у **DNS‑серверов**, какой **IP соответствует домену**, в нашем примере `someapp.example.com`.
DNS-сервера присылают браузеру определённый **IP-адрес**, тот самый публичный IP-адрес вашего сервера, который вы указали в ресурсной "записи А" при настройке.
DNS‑серверы ответят браузеру, какой **конкретный IP‑адрес** использовать. Это будет публичный IP‑адрес вашего сервера, который вы указали в настройках DNS.
<img src="/img/deployment/https/https01.drawio.svg">
### Рукопожатие TLS
### Начало TLS-рукопожатия { #tls-handshake-start }
В дальнейшем браузер будет взаимодействовать с этим IP-адресом через **port 443** (общепринятый номер порта для HTTPS).
Далее браузер будет общаться с этим IP‑адресом на **порту 443** (порт HTTPS).
Первым шагом будет установление соединения между клиентом (браузером) и сервером и выбор криптографического ключа (для шифрования).
Первая часть взаимодействия — установить соединение между клиентом и сервером и договориться о криптографических ключах и т.п.
<img src="/img/deployment/https/https02.drawio.svg">
Эта часть клиент-серверного взаимодействия устанавливает TLS-соединение и называется **TLS-рукопожатием**.
Это взаимодействие клиента и сервера для установления TLS‑соединения называется **TLS‑рукопожатием**.
### TLS с расширением SNI
### TLS с расширением SNI { #tls-with-sni-extension }
На сервере **только один процесс** может прослушивать определённый **порт** определённого **IP-адреса**. На сервере могут быть и другие процессы, слушающие другие порты этого же IP-адреса, но никакой процесс не может быть привязан к уже занятой комбинации IP-адрес:порт. Эта комбинация называется "сокет".
На сервере **только один процесс** может слушать конкретный **порт** на конкретном **IP‑адресе**. Могут быть другие процессы, слушающие другие порты на том же IP‑адресе, но не более одного процесса на каждую комбинацию IP‑адреса и порта.
По умолчанию TLS (HTTPS) использует порт `443`. Потому этот же порт будем использовать и мы.
По умолчанию TLS (HTTPS) использует порт `443`. Значит, он нам и нужен.
И раз уж только один процесс может слушать этот порт, то это будет процесс **прокси-сервера завершения работы TLS**.
Так как только один процесс может слушать этот порт, делать это будет **прокси‑сервер TLS-терминации**.
Прокси-сервер завершения работы TLS будет иметь доступ к одному или нескольким **TLS-сертификатам** (сертификаты HTTPS).
У прокси‑сервера TLS-терминации будет доступ к одному или нескольким **TLS‑сертификатам** (HTTPS‑сертификатам).
Используя **расширение SNI** упомянутое выше, прокси-сервер из имеющихся сертификатов TLS (HTTPS) выберет тот, который соответствует имени домена, указанному в запросе от клиента.
Используя **расширение SNI**, упомянутое выше, прокси‑сервер TLS-терминации определит, какой из доступных TLS (HTTPS)‑сертификатов нужно использовать для этого соединения, выбрав тот, который соответствует домену, ожидаемому клиентом.
То есть будет выбран сертификат для домена `someapp.example.com`.
В нашем случае это будет сертификат для `someapp.example.com`.
<img src="/img/deployment/https/https03.drawio.svg">
Клиент уже **доверяет** тому, кто выдал этот TLS-сертификат (в нашем случае - Let's Encrypt, но мы ещё обсудим это), потому может **проверить**, действителен ли полученный от сервера сертификат.
Клиент уже **доверяет** организации, выдавшей этот TLS‑сертификат (в нашем случае — Let's Encrypt, но об этом позже), поэтому может **проверить**, что сертификат действителен.
Затем, используя этот сертификат, клиент и прокси-сервер **выбирают способ шифрования** данных для устанавливаемого **TCP-соединения**. На этом операция **TLS-рукопожатия** завершена.
Затем, используя сертификат, клиент и прокси‑сервер TLS-терминации **договариваются о способе шифрования** остальной **TCP‑коммуникации**. На этом **TLS‑рукопожатие** завершено.
В дальнейшем клиент и сервер будут взаимодействовать по **зашифрованному TCP-соединению**, как предлагается в протоколе TLS. И на основе этого TCP-соедениния будет создано **HTTP-соединение**.
После этого у клиента и сервера есть **зашифрованное TCP‑соединение** — это и предоставляет TLS. И они могут использовать это соединение, чтобы начать собственно **HTTP‑обмен**.
Таким образом, **HTTPS** это тот же **HTTP**, но внутри **безопасного TLS-соединения** вместо чистого (незашифрованного) TCP-соединения.
Собственно, **HTTPS** — это обычный **HTTP** внутри **защищённого TLS‑соединения**, вместо чистого (незашифрованного) TCP‑соединения.
/// tip | Заметка
/// tip | Совет
Обратите внимание, что шифрование происходит на **уровне TCP**, а не на более высоком уровне HTTP.
Обратите внимание, что шифрование обмена происходит на **уровне TCP**, а не на уровне HTTP.
///
### HTTPS-запрос
### HTTPS‑запрос { #https-request }
Теперь, когда между клиентом и сервером (в нашем случае, браузером и прокси-сервером) создано **зашифрованное TCP-соединение**, они могут начать **обмен данными по протоколу HTTP**.
Теперь, когда у клиента и сервера (конкретно у браузера и прокси‑сервера TLS-терминации) есть **зашифрованное TCP‑соединение**, они могут начать **HTTP‑обмен**.
Так клиент отправляет **HTTPS-запрос**. То есть обычный HTTP-запрос, но через зашифрованное TLS-содинение.
Клиент отправляет **HTTPS‑запрос**. Это обычный HTTP‑запрос через зашифрованное TLS‑соединение.
<img src="/img/deployment/https/https04.drawio.svg">
### Расшифровка запроса
### Расшифровка запроса { #decrypt-the-request }
Прокси-сервер, используя согласованный с клиентом ключ, расшифрует полученный **зашифрованный запрос** и передаст **обычный (незашифрованный) HTTP-запрос** процессу, запускающему приложение (например, процессу Uvicorn запускающему приложение FastAPI).
Прокси‑сервер TLS-терминации использует согласованное шифрование, чтобы **расшифровать запрос**, и передаёт **обычный (расшифрованный) HTTP‑запрос** процессу, запускающему приложение (например, процессу с Uvicorn, который запускает приложение FastAPI).
<img src="/img/deployment/https/https05.drawio.svg">
### HTTP-ответ
### HTTP‑ответ { #http-response }
Приложение обработает запрос и вернёт **обычный (незашифрованный) HTTP-ответ** прокси-серверу.
Приложение обработает запрос и отправит **обычный (незашифрованный) HTTP‑ответ** прокси‑серверу TLS-терминации.
<img src="/img/deployment/https/https06.drawio.svg">
### HTTPS-ответ
### HTTPS‑ответ { #https-response }
Пркоси-сервер **зашифрует ответ** используя ранее согласованный с клиентом способ шифрования (которые содержатся в сертификате для домена `someapp.example.com`) и отправит его браузеру.
Затем прокси‑сервер TLS-терминации **зашифрует ответ** с использованием ранее согласованного способа шифрования (который начали использовать для сертификата для `someapp.example.com`) и отправит его обратно в браузер.
Наконец, браузер проверит ответ, в том числе, что тот зашифрован с нужным ключом, **расшифрует его** и обработает.
Далее браузер проверит, что ответ корректен и зашифрован правильным криптографическим ключом и т.п., затем **расшифрует ответ** и обработает его.
<img src="/img/deployment/https/https07.drawio.svg">
Клиент (браузер) знает, что ответ пришёл от правильного сервера, так как использует методы шифрования, согласованные ими раннее через **HTTPS-сертификат**.
Клиент (браузер) узнает, что ответ пришёл от правильного сервера, потому что используется способ шифрования, о котором они договорились ранее с помощью **HTTPS‑сертификата**.
### Множество приложений
### Несколько приложений { #multiple-applications }
На одном и том же сервере (или серверах) можно разместить **множество приложений**, например, другие программы с API или базы данных.
На одном и том же сервере (или серверах) могут работать **несколько приложений**, например другие программы с API или база данных.
Напомню, что только один процесс (например, прокси-сервер) может прослушивать определённый порт определённого IP-адреса.
Но другие процессы и приложения тоже могут работать на этом же сервере (серверах), если они не пытаются использовать уже занятую **комбинацию IP-адреса и порта** (сокет).
Только один процесс может обрабатывать конкретную комбинацию IP и порта (в нашем примере — прокси‑сервер TLS-терминации), но остальные приложения/процессы тоже могут работать на сервере(ах), пока они не пытаются использовать ту же **комбинацию публичного IP и порта**.
<img src="/img/deployment/https/https08.drawio.svg">
Таким образом, сервер завершения TLS может обрабатывать HTTPS-запросы и использовать сертификаты для **множества доменов** или приложений и передавать запросы правильным адресатам (другим приложениям).
Таким образом, прокси‑сервер TLS-терминации может обрабатывать HTTPS и сертификаты для **нескольких доменов** (для нескольких приложений), а затем передавать запросы нужному приложению в каждом случае.
### Обновление сертификата
### Продление сертификата { #certificate-renewal }
В недалёком будущем любой сертификат станет **просроченным** (примерно через три месяца после получения).
Со временем каждый сертификат **истечёт** (примерно через 3 месяца после получения).
Когда это произойдёт, можно запустить другую программу, которая подключится к Let's Encrypt и обновит сертификат(ы). Существуют прокси-серверы, которые могут сделать это действие самостоятельно.
Затем будет другая программа (иногда это отдельная программа, иногда — тот же прокси‑сервер TLS-терминации), которая свяжется с Let's Encrypt и продлит сертификат(ы).
<img src="/img/deployment/https/https.drawio.svg">
**TLS-сертификаты** не привязаны к IP-адресу, но **связаны с именем домена**.
**TLS‑сертификаты** **связаны с именем домена**, а не с IP‑адресом.
Так что при обновлении сертификатов программа должна **подтвердить** центру сертификации (Let's Encrypt), что обновление запросил **"владелец", который контролирует этот домен**.
Поэтому, чтобы продлить сертификаты, программа продления должна **доказать** удостоверяющему центру (Let's Encrypt), что она действительно **«владеет» и контролирует этот домен**.
Есть несколько путей осуществления этого. Самые популярные из них:
Для этого, учитывая разные потребности приложений, есть несколько способов. Популярные из них:
* **Изменение записей DNS**.
* Для такого варианта Ваша программа обновления должна уметь работать с API DNS-провайдера, обслуживающего Ваши DNS-записи. Не у всех провайдеров есть такой API, так что этот способ не всегда применим.
* **Запуск в качестве программы-сервера** (как минимум, на время обновления сертификатов) на публичном IP-адресе домена.
* Как уже не раз упоминалось, только один процесс может прослушивать определённый порт определённого IP-адреса.
* Это одна из причин использования прокси-сервера ещё и в качестве программы обновления сертификатов.
* В случае, если обновлением сертификатов занимается другая программа, вам понадобится остановить прокси-сервер, запустить программу обновления сертификатов на сокете, предназначенном для прокси-сервера, настроить прокси-сервер на работу с новыми сертификатами и перезапустить его. Эта схема далека от идеальной, так как Ваши приложения будут недоступны на время отключения прокси-сервера.
* **Изменить некоторые DNS‑записи**.
* Для этого программа продления должна поддерживать API DNS‑провайдера, поэтому, в зависимости от используемого провайдера DNS, этот вариант может быть доступен или нет.
* **Запуститься как сервер** (как минимум на время получения сертификатов) на публичном IP‑адресе, связанном с доменом.
* Как сказано выше, только один процесс может слушать конкретный IP и порт.
* Это одна из причин, почему очень удобно, когда тот же прокси‑сервер TLS-терминации также занимается процессом продления сертификатов.
* В противном случае вам, возможно, придётся временно остановить прокси‑сервер TLS-терминации, запустить программу продления для получения сертификатов, затем настроить их в прокси‑сервере TLS-терминации и перезапустить его. Это не идеально, так как ваше приложение(я) будут недоступны, пока прокси‑сервер TLS-терминации остановлен.
Весь этот процесс обновления, одновременный с обслуживанием запросов, является одной из основных причин, по которой желательно иметь **отдельную систему для работы с HTTPS** в виде прокси-сервера завершения TLS, а не просто использовать сертификаты TLS непосредственно с сервером приложений (например, Uvicorn).
Весь этот процесс продления, совмещённый с обслуживанием приложения, — одна из главных причин иметь **отдельную систему для работы с HTTPS** в виде прокси‑сервера TLS-терминации, вместо использования TLS‑сертификатов напрямую в сервере приложения (например, Uvicorn).
## Резюме
## Пересылаемые HTTP-заголовки прокси { #proxy-forwarded-headers }
Наличие **HTTPS** очень важно и довольно **критично** в большинстве случаев. Однако, Вам, как разработчику, не нужно тратить много сил на это, достаточно **понимать эти концепции** и принципы их работы.
Когда вы используете прокси для обработки HTTPS, ваш **сервер приложения** (например, Uvicorn через FastAPI CLI) ничего не знает о процессе HTTPS, он общается обычным HTTP с **прокси‑сервером TLS-терминации**.
Но узнав базовые основы **HTTPS** вы можете легко совмещать разные инструменты, которые помогут вам в дальнейшей разработке.
Обычно этот **прокси** на лету добавляет некоторые HTTP‑заголовки перед тем, как переслать запрос на **сервер приложения**, чтобы тот знал, что запрос был **проксирован**.
В следующих главах я покажу вам несколько примеров, как настраивать **HTTPS** для приложений **FastAPI**. 🔒
/// note | Технические детали
Заголовки прокси:
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For" class="external-link" target="_blank">X-Forwarded-For</a>
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto" class="external-link" target="_blank">X-Forwarded-Proto</a>
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host" class="external-link" target="_blank">X-Forwarded-Host</a>
///
Тем не менее, так как **сервер приложения** не знает, что он находится за доверенным **прокси**, по умолчанию он не будет доверять этим заголовкам.
Но вы можете настроить **сервер приложения**, чтобы он доверял *пересылаемым* заголовкам, отправленным **прокси**. Если вы используете FastAPI CLI, вы можете использовать *опцию CLI* `--forwarded-allow-ips`, чтобы указать, с каких IP‑адресов следует доверять этим *пересылаемым* заголовкам.
Например, если **сервер приложения** получает запросы только от доверенного **прокси**, вы можете установить `--forwarded-allow-ips="*"`, чтобы доверять всем входящим IP, так как он всё равно будет получать запросы только с IP‑адреса, используемого **прокси**.
Таким образом, приложение сможет знать свой публичный URL, использует ли оно HTTPS, какой домен и т.п.
Это будет полезно, например, для корректной обработки редиректов.
/// tip | Совет
Подробнее об этом вы можете узнать в документации: [За прокси — Включить пересылаемые заголовки прокси](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers){.internal-link target=_blank}
///
## Резюме { #recap }
Наличие **HTTPS** очень важно и во многих случаях довольно **критично**. Большая часть усилий, которые вам, как разработчику, нужно приложить вокруг HTTPS, — это просто **понимание этих концепций** и того, как они работают.
Зная базовую информацию о **HTTPS для разработчиков**, вы сможете легко комбинировать и настраивать разные инструменты, чтобы управлять всем этим простым способом.
В некоторых из следующих глав я покажу вам несколько конкретных примеров настройки **HTTPS** для приложений **FastAPI**. 🔒

10
docs/ru/docs/deployment/index.md

@ -1,16 +1,16 @@
# Развёртывание
# Развёртывание { #deployment }
Развернуть приложение **FastAPI** довольно просто.
## Да что такое это ваше - "развёртывание"?!
## Что означает развёртывание { #what-does-deployment-mean }
Термин **развёртывание** (приложения) означает выполнение необходимых шагов, чтобы сделать приложение **доступным для пользователей**.
Обычно **веб-приложения** размещают на удалённом компьютере с серверной программой, которая обеспечивает хорошую производительность, стабильность и т. д., Чтобы ваши пользователи могли эффективно, беспрерывно и беспроблемно обращаться к приложению.
Для **веб-API** это обычно означает размещение его на **удалённой машине** с **серверной программой**, обеспечивающей хорошую производительность, стабильность и т.д., чтобы ваши **пользователи** могли **получать доступ** к приложению эффективно и без перебоев или проблем.
Это отличается от **разработки**, когда вы постоянно меняете код, делаете в нём намеренные ошибки и исправляете их, останавливаете и перезапускаете сервер разработки и т. д.
Это отличается от этапов **разработки**, когда вы постоянно меняете код, ломаете его и исправляете, останавливаете и перезапускаете сервер разработки и т.д.
## Стратегии развёртывания
## Стратегии развёртывания { #deployment-strategies }
В зависимости от вашего конкретного случая, есть несколько способов сделать это.

178
docs/ru/docs/deployment/manually.md

@ -1,163 +1,157 @@
# Запуск сервера вручную - Uvicorn
# Запуск сервера вручную { #run-a-server-manually }
Для запуска приложения **FastAPI** на удалённой серверной машине вам необходим программный сервер, поддерживающий протокол ASGI, такой как **Uvicorn**.
## Используйте команду `fastapi run` { #use-the-fastapi-run-command }
Существует три наиболее распространённые альтернативы:
Коротко: используйте `fastapi run`, чтобы запустить ваше приложение FastAPI:
* <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a>: высокопроизводительный ASGI сервер.
* <a href="https://hypercorn.readthedocs.io/" class="external-link" target="_blank">Hypercorn</a>: ASGI сервер, помимо прочего поддерживающий HTTP/2 и Trio.
* <a href="https://github.com/django/daphne" class="external-link" target="_blank">Daphne</a>: ASGI сервер, созданный для Django Channels.
## Сервер как машина и сервер как программа
В этих терминах есть некоторые различия и вам следует запомнить их. 💡
Слово "**сервер**" чаще всего используется в двух контекстах:
<div class="termy">
- удалённый или расположенный в "облаке" компьютер (физическая или виртуальная машина).
- программа, запущенная на таком компьютере (например, Uvicorn).
```console
$ <font color="#4E9A06">fastapi</font> run <u style="text-decoration-style:solid">main.py</u>
Просто запомните, если вам встретился термин "сервер", то обычно он подразумевает что-то из этих двух смыслов.
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting production server 🚀
Когда имеют в виду именно удалённый компьютер, часто говорят просто **сервер**, но ещё его называют **машина**, **ВМ** (виртуальная машина), **нода**. Все эти термины обозначают одно и то же - удалённый компьютер, обычно под управлением Linux, на котором вы запускаете программы.
Searching for package file structure from directories
with <font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
## Установка программного сервера
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
Вы можете установить сервер, совместимый с протоколом ASGI, так:
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with
the following code:
//// tab | Uvicorn
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
* <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a>, очень быстрый ASGI сервер, основанный на библиотеках uvloop и httptools.
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<div class="termy">
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
```console
$ pip install "uvicorn[standard]"
Logs:
---> 100%
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>2306215</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font> <b>(</b>Press CTRL+C
to quit<b>)</b>
```
</div>
/// tip | Подсказка
С опцией `standard`, Uvicorn будет устанавливаться и использоваться с некоторыми дополнительными рекомендованными зависимостями.
В них входит `uvloop`, высокопроизводительная замена `asyncio`, которая значительно ускоряет работу асинхронных программ.
///
////
В большинстве случаев этого достаточно. 😎
//// tab | Hypercorn
Этой командой, например, можно запускать приложение **FastAPI** в контейнере, на сервере и т.д.
* <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>, ASGI сервер, поддерживающий протокол HTTP/2.
## ASGI‑серверы { #asgi-servers }
<div class="termy">
Давайте немного углубимся в детали.
```console
$ pip install hypercorn
FastAPI использует стандарт для построения Python‑веб‑фреймворков и серверов под названием <abbr title="Asynchronous Server Gateway Interface – Асинхронный шлюзовый интерфейс сервера">ASGI</abbr>. FastAPI — ASGI-веб‑фреймворк.
---> 100%
```
Главное, что вам нужно, чтобы запустить приложение **FastAPI** (или любое другое ASGI‑приложение) на удалённой серверной машине, — это программа ASGI‑сервера, такая как **Uvicorn**; именно он используется по умолчанию в команде `fastapi`.
</div>
Есть несколько альтернатив, например:
...или какой-либо другой ASGI сервер.
* <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a>: высокопроизводительный ASGI‑сервер.
* <a href="https://hypercorn.readthedocs.io/" class="external-link" target="_blank">Hypercorn</a>: ASGI‑сервер, среди прочего совместимый с HTTP/2 и Trio.
* <a href="https://github.com/django/daphne" class="external-link" target="_blank">Daphne</a>: ASGI‑сервер, созданный для Django Channels.
* <a href="https://github.com/emmett-framework/granian" class="external-link" target="_blank">Granian</a>: HTTP‑сервер на Rust для Python‑приложений.
* <a href="https://unit.nginx.org/howto/fastapi/" class="external-link" target="_blank">NGINX Unit</a>: NGINX Unit — лёгкая и многофункциональная среда выполнения веб‑приложений.
////
## Сервер как машина и сервер как программа { #server-machine-and-server-program }
## Запуск серверной программы
Есть небольшой нюанс в терминологии, о котором стоит помнить. 💡
Затем запустите ваше приложение так же, как было указано в руководстве ранее, но без опции `--reload`:
Слово «сервер» обычно используют и для обозначения удалённого/облачного компьютера (физической или виртуальной машины), и для программы, работающей на этой машине (например, Uvicorn).
//// tab | Uvicorn
Имейте в виду, что слово «сервер» в целом может означать любое из этих двух.
<div class="termy">
Когда речь идёт об удалённой машине, её зачастую называют **сервер**, а также **машина**, **VM** (виртуальная машина), **нода**. Всё это — варианты названия удалённой машины, обычно под управлением Linux, на которой вы запускаете программы.
```console
$ uvicorn main:app --host 0.0.0.0 --port 80
## Установка серверной программы { #install-the-server-program }
<span style="color: green;">INFO</span>: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
При установке FastAPI он поставляется с продакшн‑сервером Uvicorn, и вы можете запустить его командой `fastapi run`.
</div>
Но вы также можете установить ASGI‑сервер вручную.
////
Создайте [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активируйте его и затем установите серверное приложение.
//// tab | Hypercorn
Например, чтобы установить Uvicorn:
<div class="termy">
```console
$ hypercorn main:app --bind 0.0.0.0:80
$ pip install "uvicorn[standard]"
Running on 0.0.0.0:8080 over http (CTRL + C to quit)
---> 100%
```
</div>
////
Аналогично устанавливаются и другие ASGI‑серверы.
/// warning | Предупреждение
/// tip | Совет
Не забудьте удалить опцию `--reload`, если ранее пользовались ею.
С добавлением `standard` Uvicorn установит и будет использовать ряд рекомендованных дополнительных зависимостей.
Включение опции `--reload` требует дополнительных ресурсов, влияет на стабильность работы приложения и может повлечь прочие неприятности.
В их числе `uvloop` — высокопроизводительная замена `asyncio`, дающая серьёзный прирост производительности при параллельной работе.
Она сильно помогает во время **разработки**, но **не следует** использовать её при **реальной работе** приложения.
Если вы устанавливаете FastAPI, например так: `pip install "fastapi[standard]"`, вы уже получаете и `uvicorn[standard]`.
///
## Hypercorn с Trio
Starlette и **FastAPI** основаны на <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, которая делает их совместимыми как с <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a> - стандартной библиотекой Python, так и с <a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a>.
## Запуск серверной программы { #run-the-server-program }
Тем не менее Uvicorn совместим только с asyncio и обычно используется совместно с <a href="https://github.com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a>, высокопроизводительной заменой `asyncio`.
Но если вы хотите использовать **Trio** напрямую, то можете воспользоваться **Hypercorn**, так как они совместимы. ✨
### Установка Hypercorn с Trio
Для начала, вам нужно установить Hypercorn с поддержкой Trio:
Если вы установили ASGI‑сервер вручную, обычно нужно передать строку импорта в специальном формате, чтобы он смог импортировать ваше приложение FastAPI:
<div class="termy">
```console
$ pip install "hypercorn[trio]"
---> 100%
$ uvicorn main:app --host 0.0.0.0 --port 80
<span style="color: green;">INFO</span>: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
</div>
### Запуск с Trio
/// note | Примечание
Далее запустите Hypercorn с опцией `--worker-class` и аргументом `trio`:
Команда `uvicorn main:app` означает:
<div class="termy">
* `main`: файл `main.py` (Python‑«модуль»).
* `app`: объект, созданный в `main.py` строкой `app = FastAPI()`.
```console
$ hypercorn main:app --worker-class trio
Эквивалентно:
```Python
from main import app
```
</div>
///
Hypercorn, в свою очередь, запустит ваше приложение использующее Trio.
У каждого альтернативного ASGI‑сервера будет похожая команда; подробнее см. в их документации.
Таким образом, вы сможете использовать Trio в своём приложении. Но лучше использовать AnyIO, для сохранения совместимости и с Trio, и с asyncio. 🎉
/// warning | Предупреждение
Uvicorn и другие серверы поддерживают опцию `--reload`, полезную в период разработки.
Опция `--reload` потребляет значительно больше ресурсов, менее стабильна и т.п.
Она сильно помогает во время **разработки**, но в **продакшн** её использовать **не следует**.
///
## Концепции развёртывания
## Концепции развёртывания { #deployment-concepts }
В вышеприведённых примерах серверные программы (например Uvicorn) запускали только **один процесс**, принимающий входящие запросы с любого IP (на это указывал аргумент `0.0.0.0`) на определённый порт (в примерах мы указывали порт `80`).
В этих примерах серверная программа (например, Uvicorn) запускает **один процесс**, слушающий все IP‑адреса (`0.0.0.0`) на заранее заданном порту (например, `80`).
Это основная идея. Но возможно, вы озаботитесь добавлением дополнительных возможностей, таких как:
Это базовая идея. Но, вероятно, вам понадобится позаботиться и о некоторых дополнительных вещах, например:
* Использование более безопасного протокола HTTPS
* Настройки запуска приложения
* Перезагрузка приложения
* Запуск нескольких экземпляров приложения
* Управление памятью
* Использование перечисленных функций перед запуском приложения.
* Безопасность — HTTPS
* Запуск при старте системы
* Перезапуски
* Репликация (количество запущенных процессов)
* Память
* Предварительные шаги перед запуском
Я расскажу вам больше о каждой из этих концепций в следующих главах, с конкретными примерами стратегий работы с ними. 🚀
В следующих главах я расскажу подробнее про каждую из этих концепций, о том, как о них думать, и приведу конкретные примеры со стратегиями, как с ними работать. 🚀

44
docs/ru/docs/deployment/versions.md

@ -1,42 +1,42 @@
# О версиях FastAPI
# О версиях FastAPI { #about-fastapi-versions }
**FastAPI** уже используется в продакшене во многих приложениях и системах. Покрытие тестами поддерживается на уровне 100%. Однако его разработка все еще продолжается.
**FastAPI** уже используется в продакшене во многих приложениях и системах. Покрытие тестами поддерживается на уровне 100%. Но его разработка всё ещё движется быстрыми темпами.
Часто добавляются новые функции, регулярно исправляются баги, код продолжает постоянно совершенствоваться.
По указанным причинам текущие версии до сих пор `0.x.x`. Это говорит о том, что каждая версия может содержать обратно несовместимые изменения, следуя <a href="https://semver.org/" class="external-link" target="_blank">соглашению о Семантическом Версионировании</a>.
По указанным причинам текущие версии до сих пор `0.x.x`. Это говорит о том, что каждая версия может содержать обратно несовместимые изменения, следуя <a href="https://semver.org/" class="external-link" target="_blank">Семантическому версионированию</a>.
Уже сейчас вы можете создавать приложения в продакшене, используя **FastAPI** (и скорее всего так и делаете), главное убедиться в том, что вы используете версию, которая корректно работает с вашим кодом.
Уже сейчас вы можете создавать приложения в продакшене, используя **FastAPI** (и скорее всего так и делаете), главное убедиться в том, что вы используете версию, которая корректно работает с вашим кодом.
## Закрепите вашу версию `fastapi`
## Закрепите вашу версию `fastapi` { #pin-your-fastapi-version }
Первым делом вам следует "закрепить" конкретную последнюю используемую версию **FastAPI**, которая корректно работает с вашим приложением.
Например, в своём приложении вы используете версию `0.45.0`.
Например, в своём приложении вы используете версию `0.112.0`.
Если вы используете файл `requirements.txt`, вы можете указать версию следующим способом:
```txt
fastapi==0.45.0
fastapi[standard]==0.112.0
```
это означает, что вы будете использовать именно версию `0.45.0`.
это означает, что вы будете использовать именно версию `0.112.0`.
Или вы можете закрепить версию следующим способом:
```txt
fastapi>=0.45.0,<0.46.0
fastapi[standard]>=0.112.0,<0.113.0
```
это значит, что вы используете версии `0.45.0` или выше, но меньше чем `0.46.0`. Например, версия `0.45.2` все еще будет подходить.
это значит, что вы используете версии `0.112.0` или выше, но меньше чем `0.113.0`. Например, версия `0.112.2` всё ещё будет подходить.
Если вы используете любой другой инструмент для управления зависимостями, например Poetry, Pipenv или др., у них у всех имеется способ определения специфической версии для ваших пакетов.
Если вы используете любой другой инструмент для управления установками/зависимостями, например `uv`, Poetry, Pipenv или др., у них у всех имеется способ определения специфической версии для ваших пакетов.
## Доступные версии
## Доступные версии { #available-versions }
Вы можете посмотреть доступные версии (например, проверить последнюю на данный момент) в [примечаниях к выпуску](../release-notes.md){.internal-link target=_blank}.
Вы можете посмотреть доступные версии (например, проверить последнюю на данный момент) в [Примечаниях к выпуску](../release-notes.md){.internal-link target=_blank}.
## О версиях
## О версиях { #about-versions }
Следуя соглашению о Семантическом Версионировании, любые версии ниже `1.0.0` потенциально могут добавить обратно несовместимые изменения.
@ -44,7 +44,7 @@ FastAPI следует соглашению в том, что любые изм
/// tip | Подсказка
"ПАТЧ" - это последнее число. Например, в `0.2.3`, ПАТЧ-версия - это `3`.
"ПАТЧ" это последнее число. Например, в `0.2.3`, ПАТЧ-версия это `3`.
///
@ -58,11 +58,11 @@ fastapi>=0.45.0,<0.46.0
/// tip | Подсказка
"МИНОРНАЯ" версия - это число в середине. Например, в `0.2.3` МИНОРНАЯ версия - это `2`.
"МИНОРНАЯ" версия это число в середине. Например, в `0.2.3` МИНОРНАЯ версия это `2`.
///
## Обновление версий FastAPI
## Обновление версий FastAPI { #upgrading-the-fastapi-versions }
Вам следует добавить тесты для вашего приложения.
@ -70,9 +70,9 @@ fastapi>=0.45.0,<0.46.0
После создания тестов вы можете обновить свою версию **FastAPI** до более новой. После этого следует убедиться, что ваш код работает корректно, запустив тесты.
Если все работает корректно, или после внесения необходимых изменений все ваши тесты проходят, только тогда вы можете закрепить вашу новую версию `fastapi`.
Если всё работает корректно, или после внесения необходимых изменений все ваши тесты проходят, только тогда вы можете закрепить вашу новую версию `fastapi`.
## О Starlette
## О Starlette { #about-starlette }
Не следует закреплять версию `starlette`.
@ -80,14 +80,14 @@ fastapi>=0.45.0,<0.46.0
Так что решение об используемой версии Starlette, вы можете оставить **FastAPI**.
## О Pydantic
## О Pydantic { #about-pydantic }
Pydantic включает свои собственные тесты для **FastAPI**, так что новые версии Pydantic (выше `1.0.0`) всегда совместимы с FastAPI.
Вы можете закрепить любую версию Pydantic, которая вам подходит, выше `1.0.0` и ниже `2.0.0`.
Вы можете закрепить любую версию Pydantic, которая вам подходит, выше `1.0.0`.
Например:
```txt
pydantic>=1.2.0,<2.0.0
pydantic>=2.7.0,<3.0.0
```

23
docs/ru/docs/environment-variables.md

@ -1,6 +1,6 @@
# Переменные окружения
# Переменные окружения { #environment-variables }
/// tip
/// tip | Совет
Если вы уже знаете, что такое «переменные окружения» и как их использовать, можете пропустить это.
@ -10,7 +10,7 @@
Переменные окружения могут быть полезны для работы с **настройками** приложений, как часть **установки** Python и т.д.
## Создание и использование переменных окружения
## Создание и использование переменных окружения { #create-and-use-env-vars }
Можно **создавать** и использовать переменные окружения в **оболочке (терминале)**, не прибегая к помощи Python:
@ -50,7 +50,7 @@ Hello Wade Wilson
////
## Чтение переменных окружения в python
## Чтение переменных окружения в python { #read-env-vars-in-python }
Так же существует возможность создания переменных окружения **вне** Python, в терминале (или любым другим способом), а затем **чтения их в Python**.
@ -63,11 +63,12 @@ name = os.getenv("MY_NAME", "World")
print(f"Hello {name} from Python")
```
/// tip
/// tip | Совет
Второй аргумент <a href=«https://docs.python.org/3.8/library/os.html#os.getenv» class=«external-link» target=«_blank»>`os.getenv()`</a> - это возвращаемое по умолчанию значение.
Второй аргумент <a href="https://docs.python.org/3.8/library/os.html#os.getenv" class="external-link" target="_blank">`os.getenv()`</a> - это возвращаемое по умолчанию значение.
Если значение не указано, то по умолчанию оно равно `None`. В данном случае мы указываем `«World»` в качестве значения по умолчанию.
///
Затем можно запустить эту программу на Python:
@ -150,13 +151,13 @@ Hello World from Python
</div>
/// tip
/// tip | Совет
Подробнее об этом можно прочитать на сайте <a href="https://12factor.net/config" class="external-link" target="_blank">The Twelve-Factor App: Config</a>.
///
## Типизация и Валидация
## Типизация и Валидация { #types-and-validation }
Эти переменные окружения могут работать только с **текстовыми строками**, поскольку они являются внешними по отношению к Python и должны быть совместимы с другими программами и остальной системой (и даже с различными операционными системами, такими как Linux, Windows, macOS).
@ -164,7 +165,7 @@ Hello World from Python
Подробнее об использовании переменных окружения для работы с **настройками приложения** вы узнаете в [Расширенное руководство пользователя - Настройки и переменные среды](./advanced/settings.md){.internal-link target=_blank}.
## Переменная окружения `PATH`
## Переменная окружения `PATH` { #path-environment-variable }
Существует **специальная** переменная окружения **`PATH`**, которая используется операционными системами (Linux, macOS, Windows) для поиска программ для запуска.
@ -208,7 +209,7 @@ C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System3
Если она ее находит, то **использует ее**. В противном случае она продолжает искать в **других каталогах**.
### Установка Python и обновление `PATH`
### Установка Python и обновление `PATH` { #installing-python-and-updating-the-path }
При установке Python вас могут спросить, нужно ли обновить переменную окружения `PATH`.
@ -286,7 +287,7 @@ $ C:\opt\custompython\bin\python
Эта информация будет полезна при изучении [Виртуальных окружений](virtual-environments.md){.internal-link target=_blank}.
## Вывод
## Вывод { #conclusion }
Благодаря этому вы должны иметь базовое представление о том, что такое **переменные окружения** и как использовать их в Python.

18
docs/ru/docs/fastapi-cli.md

@ -1,4 +1,4 @@
# FastAPI CLI
# FastAPI CLI { #fastapi-cli }
**FastAPI CLI** это программа командной строки, которую вы можете использовать для запуска вашего FastAPI приложения, для управления FastAPI-проектом, а также для многих других вещей.
@ -50,26 +50,26 @@ $ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid
FastAPI CLI берет путь к вашей Python-программе (напр. `main.py`) и автоматически находит объект `FastAPI` (обычно это `app`), затем определяет правильный процесс импорта и запускает сервер приложения.
Для работы в production окружении вместо `fastapi dev` нужно использовать `fastapi run`. 🚀
Для работы в режиме продакшн вместо `fastapi dev` нужно использовать `fastapi run`. 🚀
Внутри **FastAPI CLI** используется <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a>, высокопроизводительный, готовый к работе в production сервер ASGI. 😎
Внутри **FastAPI CLI** используется <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a>, высокопроизводительный, готовый к работе в продакшне ASGI-сервер. 😎
## `fastapi dev`
## `fastapi dev` { #fastapi-dev }
Вызов `fastapi dev` запускает режим разработки.
По умолчанию включена автоматическая перезагрузка (**auto-reload**), благодаря этому при изменении кода происходит перезагрузка сервера приложения. Эта установка требует значительных ресурсов и делает систему менее стабильной. Используйте её только при разработке. Приложение слушает входящие подключения на IP `127.0.0.1`. Это IP адрес вашей машины, предназначенный для внутренних коммуникаций (`localhost`).
По умолчанию включена авто-перезагрузка (**auto-reload**), благодаря этому при изменении кода происходит перезагрузка сервера приложения. Эта установка требует значительных ресурсов и делает систему менее стабильной. Используйте её только при разработке. Приложение слушает входящие подключения на IP `127.0.0.1`. Это IP адрес вашей машины, предназначенный для внутренних коммуникаций (`localhost`).
## `fastapi run`
## `fastapi run` { #fastapi-run }
Вызов `fastapi run` по умолчанию запускает FastAPI в режиме production.
Вызов `fastapi run` по умолчанию запускает FastAPI в режиме продакшн.
По умолчанию функция перезагрузки **auto-reload** отключена. Приложение слушает входящие подключения на IP `0.0.0.0`, т.е. на всех доступных адресах компьютера. Таким образом, приложение будет находиться в публичном доступе для любого, кто может подсоединиться к вашей машине. Продуктовые приложения запускаются именно так, например, с помощью контейнеров.
По умолчанию авто-перезагрузка (**auto-reload**) отключена. Приложение слушает входящие подключения на IP `0.0.0.0`, т.е. на всех доступных адресах компьютера. Таким образом, приложение будет находиться в публичном доступе для любого, кто может подсоединиться к вашей машине. Продуктовые приложения запускаются именно так, например, с помощью контейнеров.
В большинстве случаев вы будете (и должны) использовать прокси-сервер ("termination proxy"), который будет поддерживать HTTPS поверх вашего приложения. Всё будет зависеть от того, как вы развертываете приложение: за вас это либо сделает ваш провайдер, либо вам придется сделать настройки самостоятельно.
/// tip | Подсказка
Вы можете больше узнать об этом в документации по развертыванию приложений [deployment documentation](deployment/index.md){.internal-link target=_blank}.
Вы можете больше узнать об этом в [документации по развертыванию](deployment/index.md){.internal-link target=_blank}.
///

135
docs/ru/docs/features.md

@ -1,23 +1,21 @@
# Основные свойства
# Возможности { #features }
## Основные свойства FastAPI
## Возможности FastAPI { #fastapi-features }
**FastAPI** предлагает вам следующее:
### Использование открытых стандартов
### Основано на открытых стандартах { #based-on-open-standards }
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> для создания API, включая объявления <abbr title="также известных, как HTTP-методы, такие, как: POST, GET, PUT, DELETE">операций</abbr> <abbr title="известные как: эндпоинты, маршруты, 'ручки' и т.п.">пути</abbr>, параметров, тела запроса, безопасности и т.д.
* Автоматическое документирование моделей данных в соответствии с <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (так как спецификация OpenAPI сама основана на JSON Schema).
* Разработан, придерживаясь этих стандартов, после тщательного их изучения. Эти стандарты изначально включены во фреймфорк, а не являются дополнительной надстройкой.
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> для создания API, включая объявления <abbr title="также известные как HTTP-методы, например: POST, GET, PUT, DELETE">операций</abbr> <abbr title="также известен как: эндпоинты, маршруты)">пути</abbr>, параметров, тел запросов, безопасности и т. д.
* Автоматическая документация моделей данных с помощью <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (так как сама спецификация OpenAPI основана на JSON Schema).
* Разработан вокруг этих стандартов, после тщательного их изучения. Это не дополнительная надстройка поверх.
* Это также позволяет использовать автоматическую **генерацию клиентского кода** на многих языках.
### Автоматически генерируемая документация
### Автоматическая документация { #automatic-docs }
Интерактивная документация для API и исследования пользовательских веб-интерфейсов. Поскольку этот фреймворк основан на OpenAPI, существует несколько вариантов документирования, 2 из которых включены по умолчанию.
Интерактивная документация для API и исследовательские веб-интерфейсы. Поскольку фреймворк основан на OpenAPI, существует несколько вариантов документирования, 2 из них включены по умолчанию.
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank"><strong>Swagger UI</strong></a>, с интерактивным взаимодействием, вызывает и тестирует ваш API прямо из браузера.
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank"><strong>Swagger UI</strong></a>, с интерактивным исследованием, вызовом и тестированием вашего API прямо из браузера.
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
@ -25,22 +23,21 @@
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### Только современный Python
### Только современный Python { #just-modern-python }
Все эти возможности основаны на стандартных **аннотациях типов Python 3.8** (благодаря Pydantic). Не нужно изучать новый синтаксис. Только лишь стандартный современный Python.
Все основано на стандартных **аннотациях типов Python** (благодаря Pydantic). Не нужно изучать новый синтаксис. Только стандартный современный Python.
Если вам нужно освежить знания, как использовать аннотации типов в Python (даже если вы не используете FastAPI), выделите 2 минуты и просмотрите краткое руководство: [Введение в аннотации типов Python¶
](python-types.md){.internal-link target=_blank}.
Если вам нужно освежить знания о типах в Python (даже если вы не используете FastAPI), выделите 2 минуты и просмотрите краткое руководство: [Типы Python](python-types.md){.internal-link target=_blank}.
Вы пишете на стандартном Python с аннотациями типов:
Вы пишете стандартный Python с типами:
```Python
from datetime import date
from pydantic import BaseModel
# Объявляем параметр user_id с типом `str`
# и получаем поддержку редактора внутри функции
# Объявляем параметр как `str`
# и получаем поддержку редактора кода внутри функции
def main(user_id: str):
return user_id
@ -70,17 +67,17 @@ my_second_user: User = User(**second_user_data)
`**second_user_data` означает:
Передать ключи и значения словаря `second_user_data`, в качестве аргументов типа "ключ-значение", это эквивалентно: `User(id=4, name="Mary", joined="2018-11-30")` .
Передать ключи и значения словаря `second_user_data` в качестве аргументов "ключ-значение", эквивалентно: `User(id=4, name="Mary", joined="2018-11-30")`
///
### Поддержка редакторов (IDE)
### Поддержка редакторов (IDE) { #editor-support }
Весь фреймворк был продуман так, чтобы быть простым и интуитивно понятным в использовании, все решения были проверены на множестве редакторов еще до начала разработки, чтобы обеспечить наилучшие условия при написании кода.
В опросе Python-разработчиков было выяснено, <a href="https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features" class="external-link" target="_blank">что наиболее часто используемой функцией редакторов, является "автодополнение"</a>.
В опросах Python‑разработчиков видно, <a href="https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features" class="external-link" target="_blank">что одной из самых часто используемых функций является «автозавершение»</a>.
Вся структура **FastAPI** основана на удовлетворении этой возможности. Автодополнение работает везде.
Вся структура **FastAPI** основана на удовлетворении этой возможности. Автозавершение работает везде.
Вам редко нужно будет возвращаться к документации.
@ -94,23 +91,23 @@ my_second_user: User = User(**second_user_data)
![editor support](https://fastapi.tiangolo.com/img/pycharm-completion.png)
Вы будете получать автодополнение кода даже там, где вы считали это невозможным раньше.
Как пример, ключ `price` внутри тела JSON (который может быть вложенным), приходящего в запросе.
Вы будете получать автозавершение кода даже там, где вы считали это невозможным раньше. Как пример, ключ `price` внутри тела JSON (который может быть вложенным), приходящего в запросе.
Больше никаких неправильных имён ключей, метания по документации или прокручивания кода вверх и вниз в попытках узнать — использовали вы ранее `username` или `user_name`.
Больше никаких неправильных имён ключей, метания по документации или прокручивания кода вверх и вниз, в попытках узнать - использовали вы ранее `username` или `user_name`.
### Краткость { #short }
### Краткость
FastAPI имеет продуманные значения **по умолчанию** для всего, с произвольными настройками везде. Все параметры могут быть тонко подстроены так, чтобы делать то, что вам нужно и определять необходимый вам API.
FastAPI имеет продуманные значения **по умолчанию** для всего, с опциональными настройками везде. Все параметры могут быть тонко подстроены так, чтобы делать то, что вам нужно, и определять необходимый вам API.
Но, по умолчанию, всё это **"и так работает"**.
Но по умолчанию всё **«просто работает»**.
### Проверка значений
### Проверка значений { #validation }
* Проверка значений для большинства (или всех?) **типов данных** Python, включая:
* Проверка значений для большинства (или всех?) **типов данных** Python, включая:
* Объекты JSON (`dict`).
* Массивы JSON (`list`) с установленными типами элементов.
* Массив JSON (`list`) с определёнными типами элементов.
* Строковые (`str`) поля с ограничением минимальной и максимальной длины.
* Числа (`int`, `float`) с минимальными и максимальными значениями и т.п.
* Числа (`int`, `float`) с минимальными и максимальными значениями и т. п.
* Проверка для более экзотических типов, таких как:
* URL.
@ -118,11 +115,11 @@ FastAPI имеет продуманные значения **по умолчан
* UUID.
* ...и другие.
Все проверки обрабатываются хорошо зарекомендовавшим себя и надежным **Pydantic**.
Все проверки обрабатываются хорошо зарекомендовавшим себя и надёжным **Pydantic**.
### Безопасность и аутентификация
### Безопасность и аутентификация { #security-and-authentication }
Встроеные функции безопасности и аутентификации. Без каких-либо компромиссов с базами данных или моделями данных.
Встроенные функции безопасности и аутентификации. Без какихлибо компромиссов с базами данных или моделями данных.
Все схемы безопасности, определённые в OpenAPI, включая:
@ -137,68 +134,68 @@ FastAPI имеет продуманные значения **по умолчан
Все инструменты и компоненты спроектированы для многократного использования и легко интегрируются с вашими системами, хранилищами данных, реляционными и NoSQL базами данных и т. д.
### Внедрение зависимостей
### Внедрение зависимостей { #dependency-injection }
FastAPI включает в себя чрезвычайно простую в использовании, но чрезвычайно мощную систему <abbr title='известную как: "components", "resources", "services", "providers"'><strong>Внедрения зависимостей</strong></abbr>.
* Даже зависимости могут иметь зависимости, создавая иерархию или **"графы" зависимостей**.
* Даже зависимости могут иметь зависимости, создавая иерархию или **«граф» зависимостей**.
* Всё **автоматически обрабатывается** фреймворком.
* Все зависимости могут запрашивать данные из запросов и **дополнять операции пути** ограничениями и автоматической документацией.
* **Автоматическая проверка** даже для параметров *операций пути*, определенных в зависимостях.
* Поддержка сложных систем аутентификации пользователей, **соединений с базами данных** и т.д.
* **Никаких компромиссов** с базами данных, интерфейсами и т.д. Но легкая интеграция со всеми ними.
* **Автоматическая проверка** даже для параметров *операций пути*, определённых в зависимостях.
* Поддержка сложных систем аутентификации пользователей, **соединений с базами данных** и т. д.
* **Никаких компромиссов** с базами данных, интерфейсами и т. д. Но при этом — лёгкая интеграция со всеми ними.
### Нет ограничений на "Плагины"
### Нет ограничений на "Плагины" { #unlimited-plug-ins }
Или, другими словами, нет сложностей с ними, импортируйте и используйте нужный вам код.
Или, другими словами, нет необходимости в них — просто импортируйте и используйте нужный вам код.
Любая интеграция разработана настолько простой в использовании (с зависимостями), что вы можете создать "плагин" для своего приложения в пару строк кода, используя ту же структуру и синтаксис, что и для ваших *операций пути*.
Любая интеграция разработана настолько простой в использовании (с зависимостями), что вы можете создать «плагин» для своего приложения в пару строк кода, используя ту же структуру и синтаксис, что и для ваших *операций пути*.
### Проверен
### Проверен { #tested }
* 100% <abbr title="Количество автоматически проверямого кода">покрытие тестами</abbr>.
* 100% <abbr title="Аннотации типов Python, благодаря которым ваш редактор и другие инструменты могут обеспечить вам лучшую поддержку">аннотирование типов</abbr> в кодовой базе.
* Используется в реально работающих приложениях.
* 100% <abbr title="Количество автоматически проверяемого кода">покрытие тестами</abbr>.
* 100% <abbr title="Аннотации типов Python, благодаря которым ваш редактор и внешние инструменты могут обеспечить вам лучшую поддержку">аннотирование типов</abbr> в кодовой базе.
* Используется в продакшн‑приложениях.
## Основные свойства Starlette
## Возможности Starlette { #starlette-features }
**FastAPI** основан на <a href="https://www.starlette.io/" class="external-link" target="_blank"><strong>Starlette</strong></a> и полностью совместим с ним. Так что, любой дополнительный код Starlette, который у вас есть, будет также работать.
**FastAPI** основан на <a href="https://www.starlette.io/" class="external-link" target="_blank"><strong>Starlette</strong></a> и полностью совместим с ним. Так что любой дополнительный код Starlette, который у вас есть, также будет работать.
На самом деле, `FastAPI` - это класс, унаследованный от `Starlette`. Таким образом, если вы уже знаете или используете Starlette, большая часть функционала будет работать так же.
На самом деле, `FastAPI` — это подкласс `Starlette`. Таким образом, если вы уже знаете или используете Starlette, большая часть функционала будет работать так же.
С **FastAPI** вы получаете все возможности **Starlette** (так как FastAPI это всего лишь Starlette на стероидах):
С **FastAPI** вы получаете все возможности **Starlette** (так как FastAPI это всего лишь Starlette на стероидах):
* Серьёзно впечатляющая производительность. Это <a href="https://github.com/encode/starlette#performance" class="external-link" target="_blank">один из самых быстрых фреймворков на Python</a>, наравне с приложениями использующими **NodeJS** или **Go**.
* Серьёзно впечатляющая производительность. Это <a href="https://github.com/encode/starlette#performance" class="external-link" target="_blank">один из самых быстрых фреймворков на Python, наравне с **NodeJS** и **Go**</a>.
* Поддержка **WebSocket**.
* Фоновые задачи для процессов.
* Фоновые задачи в том же процессе.
* События запуска и выключения.
* Тестовый клиент построен на библиотеке HTTPX.
* Тестовый клиент построен на HTTPX.
* **CORS**, GZip, статические файлы, потоковые ответы.
* Поддержка **сессий и cookie**.
* 100% покрытие тестами.
* 100% аннотирование типов в кодовой базе.
## Особенности и возможности Pydantic
## Возможности Pydantic { #pydantic-features }
**FastAPI** основан на <a href="https://docs.pydantic.dev/" class="external-link" target="_blank"><strong>Pydantic</strong></a> и полностью совместим с ним. Так что, любой дополнительный код Pydantic, который у вас есть, будет также работать.
**FastAPI** полностью совместим с (и основан на) <a href="https://docs.pydantic.dev/" class="external-link" target="_blank"><strong>Pydantic</strong></a>. Поэтому любой дополнительный код Pydantic, который у вас есть, также будет работать.
Включая внешние библиотеки, также основанные на Pydantic, такие как: <abbr title="Object-Relational Mapper">ORM</abbr>, <abbr title="Object-Document Mapper">ODM</abbr> для баз данных.
Включая внешние библиотеки, также основанные на Pydantic, такие как <abbr title="Object-Relational Mapper">ORM</abbr>’ы, <abbr title="Object-Document Mapper">ODM</abbr>’ы для баз данных.
Это также означает, что во многих случаях вы можете передавать тот же объект, который получили из запроса, **непосредственно в базу данных**, так как всё проверяется автоматически.
И наоборот, во многих случаях вы можете просто передать объект, полученный из базы данных, **непосредственно клиенту**.
С **FastAPI** вы получаете все возможности **Pydantic** (так как, FastAPI основан на Pydantic, для обработки данных):
* **Никакой нервотрёпки** :
* Не нужно изучать новых схем в микроязыках.
* Если вы знаете аннотации типов в Python, вы знаете, как использовать Pydantic.
* Прекрасно сочетается с вашими **<abbr title="Интегрированное окружение для разработки, похожее на текстовый редактор">IDE</abbr>/<abbr title="программа проверяющая ошибки в коде">linter</abbr>/мозгом**:
* Потому что структуры данных pydantic - это всего лишь экземпляры классов, определённых вами. Автодополнение, проверка кода, mypy и ваша интуиция - всё будет работать с вашими проверенными данными.
* Проверка **сложных структур**:
* Использование иерархических моделей Pydantic; `List`, `Dict` и т.п. из модуля `typing` (входит в стандартную библиотеку Python).
* Валидаторы позволяют четко и легко определять, проверять и документировать сложные схемы данных в виде JSON Schema.
* У вас могут быть глубоко **вложенные объекты JSON** и все они будут проверены и аннотированы.
С **FastAPI** вы получаете все возможности **Pydantic** (так как FastAPI основан на Pydantic для обработки данных):
* **Никакой нервотрёпки**:
* Не нужно изучать новые схемы в микроязыках.
* Если вы знаете типы в Python, вы знаете, как использовать Pydantic.
* Прекрасно сочетается с вашим **<abbr title="Integrated Development Environment - Интегрированная среда разработки: попросту «редактора кода»">IDE</abbr>/<abbr title="Программа, проверяющая ошибки в коде">linter</abbr>/мозгом**:
* Потому что структуры данных pydantic — это всего лишь экземпляры классов, определённых вами; автозавершение, проверка кода, mypy и ваша интуиция — всё будет работать с вашими валидированными данными.
* Валидация **сложных структур**:
* Использование иерархических моделей Pydantic; `List`, `Dict` и т. п. из модуля `typing` (входит в стандартную библиотеку Python).
* Валидаторы позволяют чётко и легко определять, проверять и документировать сложные схемы данных в виде JSON Schema.
* У вас могут быть глубоко **вложенные объекты JSON**, и все они будут проверены и аннотированы.
* **Расширяемость**:
* Pydantic позволяет определять пользовательские типы данных или расширять проверку методами модели, с помощью проверочных декораторов.
* Pydantic позволяет определять пользовательские типы данных или расширять проверку методами модели с помощью декораторов валидаторов.
* 100% покрытие тестами.

242
docs/ru/docs/help-fastapi.md

@ -1,261 +1,255 @@
# Помочь FastAPI - Получить помощь
# Помочь FastAPI - Получить помощь { #help-fastapi-get-help }
Нравится ли Вам **FastAPI**?
Хотели бы Вы помочь FastAPI, его пользователям и автору?
Хотели бы Вы помочь FastAPI, другим пользователям и автору?
Может быть у Вас возникли трудности с **FastAPI** и Вам нужна помощь?
Или Вы хотите получить помощь по **FastAPI**?
Есть несколько очень простых способов оказания помощи (иногда достаточно всего лишь одного или двух кликов).
Есть несколько очень простых способов помочь (иногда достаточно всего лишь одного-двух кликов).
И также есть несколько способов получить помощь.
## Подписаться на новостную рассылку
## Подписаться на новостную рассылку { #subscribe-to-the-newsletter }
Вы можете подписаться на редкую [новостную рассылку **FastAPI и его друзья**](newsletter.md){.internal-link target=_blank} и быть в курсе о:
* Новостях о FastAPI и его друзьях 🚀
* Руководствах 📝
* Возможностях ✨
* Исправлениях 🚨
* Ломающих изменениях 🚨
* Подсказках и хитростях ✅
## Подписаться на FastAPI в X (Twitter)
## Подписаться на FastAPI в X (Twitter) { #follow-fastapi-on-x-twitter }
<a href="https://x.com/fastapi" class="external-link" target="_blank">Подписаться на @fastapi в **X (Twitter)**</a> для получения наисвежайших новостей о **FastAPI**. 🐦
## Добавить **FastAPI** звезду на GitHub
## Добавить **FastAPI** звезду на GitHub { #star-fastapi-in-github }
Вы можете добавить FastAPI "звезду" на GitHub (кликнуть на кнопку звезды в верхнем правом углу экрана): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. ⭐️
Вы можете добавить FastAPI "звезду" на GitHub (кликнув на кнопку звезды в правом верхнем углу): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. ⭐️
Чем больше звёзд, тем легче другим пользователям найти нас и увидеть, что проект уже стал полезным для многих.
Чем больше звёзд, тем легче другим пользователям найти проект и увидеть, что он уже оказался полезным для многих.
## Отслеживать свежие выпуски в репозитории на GitHub
## Отслеживать свежие выпуски в репозитории на GitHub { #watch-the-github-repository-for-releases }
Вы можете "отслеживать" FastAPI на GitHub (кликните по кнопке "watch" наверху справа): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. 👀
Вы можете "отслеживать" FastAPI на GitHub (кликнув по кнопке "watch" наверху справа): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. 👀
Там же Вы можете указать в настройках - "Releases only".
Там же Вы можете выбрать "Releases only".
С такой настройкой Вы будете получать уведомления на вашу электронную почту каждый раз, когда появится новый релиз (новая версия) **FastAPI** с исправлениями ошибок и новыми возможностями.
## Связаться с автором
## Связаться с автором { #connect-with-the-author }
Можно связаться со <a href="https://tiangolo.com" class="external-link" target="_blank">мной (Себястьян Рамирез / `tiangolo`)</a>, автором FastAPI.
Можно связаться со <a href="https://tiangolo.com" class="external-link" target="_blank">мной (Sebastián Ramírez / `tiangolo`)</a>, автором.
Вы можете:
* <a href="https://github.com/tiangolo" class="external-link" target="_blank">Подписаться на меня на **GitHub**</a>.
* Посмотреть другие мои проекты с открытым кодом, которые могут быть полезны Вам.
* Подписавшись на меня Вы сможете получать уведомления, что я создал новый проект с открытым кодом,.
* Подписаться, чтобы видеть, когда я создаю новый проект с открытым кодом.
* <a href="https://x.com/tiangolo" class="external-link" target="_blank">Подписаться на меня в **X (Twitter)**</a> или в <a href="https://fosstodon.org/@tiangolo" class="external-link" target="_blank">Mastodon</a>.
* Поделиться со мной, как Вы используете FastAPI (я обожаю читать про это).
* Получать уведомления, когда я делаю объявления и представляю новые инструменты.
* Поделиться со мной, как Вы используете FastAPI (я обожаю это читать).
* Узнавать, когда я делаю объявления или выпускаю новые инструменты.
* Вы также можете <a href="https://x.com/fastapi" class="external-link" target="_blank">подписаться на @fastapi в X (Twitter)</a> (это отдельный аккаунт).
* <a href="https://www.linkedin.com/in/tiangolo/" class="external-link" target="_blank">Подписаться на меня в **Linkedin**</a>.
* Получать уведомления, когда я делаю объявления и представляю новые инструменты (правда чаще всего я использую X (Twitter) 🤷‍♂).
* Читать, что я пишу (или подписаться на меня) в <a href="https://dev.to/tiangolo" class="external-link" target="_blank">**Dev.to**</a> или в <a href="https://medium.com/@tiangolo" class="external-link" target="_blank">**Medium**</a>.
* Читать другие идеи, статьи и читать об инструментах созданных мной.
* Подпишитесь на меня, чтобы прочитать, когда я опубликую что-нибудь новое.
* <a href="https://www.linkedin.com/in/tiangolo/" class="external-link" target="_blank">Подписаться на меня в **LinkedIn**</a>.
* Узнавать, когда я делаю объявления или выпускаю новые инструменты (хотя чаще я использую X (Twitter) 🤷‍♂).
* Читать, что я пишу (или подписаться на меня) на <a href="https://dev.to/tiangolo" class="external-link" target="_blank">**Dev.to**</a> или <a href="https://medium.com/@tiangolo" class="external-link" target="_blank">**Medium**</a>.
* Читать другие идеи, статьи и о созданных мной инструментах.
* Подписаться, чтобы читать, когда я публикую что-то новое.
## Оставить сообщение в X (Twitter) о **FastAPI**
## Оставить сообщение в X (Twitter) о **FastAPI** { #tweet-about-fastapi }
<a href="https://x.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi" class="external-link" target="_blank">Оставьте сообщение в X (Twitter) о **FastAPI**</a> и позвольте мне и другим узнать - почему он Вам нравится. 🎉
<a href="https://x.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi" class="external-link" target="_blank">Оставьте сообщение в X (Twitter) о **FastAPI**</a> и позвольте мне и другим узнать, почему он Вам нравится. 🎉
Я люблю узнавать о том, как **FastAPI** используется, что Вам понравилось в нём, в каких проектах/компаниях Вы используете его и т.п.
Я люблю узнавать о том, как **FastAPI** используется, что Вам понравилось в нём, в каких проектах/компаниях Вы его используете и т.д.
## Оставить голос за FastAPI
## Оставить голос за FastAPI { #vote-for-fastapi }
* <a href="https://www.slant.co/options/34241/~fastapi-review" class="external-link" target="_blank">Голосуйте за **FastAPI** в Slant</a>.
* <a href="https://alternativeto.net/software/fastapi/" class="external-link" target="_blank">Голосуйте за **FastAPI** в AlternativeTo</a>.
* <a href="https://stackshare.io/pypi-fastapi" class="external-link" target="_blank">Расскажите, как Вы используете **FastAPI** на StackShare</a>.
* <a href="https://alternativeto.net/software/fastapi/about/" class="external-link" target="_blank">Голосуйте за **FastAPI** в AlternativeTo</a>.
* <a href="https://stackshare.io/pypi-fastapi" class="external-link" target="_blank">Расскажите, что Вы используете **FastAPI** на StackShare</a>.
## Помочь другим с их проблемами на GitHub
## Помочь другим с вопросами на GitHub { #help-others-with-questions-in-github }
Вы можете посмотреть, какие <a href="https://github.com/fastapi/fastapi/issues" class="external-link" target="_blank">проблемы</a> испытывают другие люди и попытаться помочь им. Чаще всего это вопросы, на которые, весьма вероятно, Вы уже знаете ответ. 🤓
Вы можете попробовать помочь другим с их вопросами в:
Если Вы будете много помогать людям с решением их проблем, Вы можете стать официальным [Экспертом FastAPI](fastapi-people.md#_3){.internal-link target=_blank}. 🎉
* <a href="https://github.com/fastapi/fastapi/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered" class="external-link" target="_blank">GitHub Discussions</a>
* <a href="https://github.com/fastapi/fastapi/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aquestion+-label%3Aanswered+" class="external-link" target="_blank">GitHub Issues</a>
Только помните, самое важное при этом - доброта. Столкнувшись с проблемой, люди расстраиваются и часто задают вопросы не лучшим образом, но постарайтесь быть максимально доброжелательным. 🤗
Во многих случаях Вы уже можете знать ответы на эти вопросы. 🤓
Идея сообщества **FastAPI** в том, чтобы быть добродушным и гостеприимными. Не допускайте издевательств или неуважительного поведения по отношению к другим. Мы должны заботиться друг о друге.
Если Вы много помогаете людям с их вопросами, Вы станете официальным [Экспертом FastAPI](fastapi-people.md#fastapi-experts){.internal-link target=_blank}. 🎉
Только помните, самое важное — постарайтесь быть добрыми. Люди приходят со своими разочарованиями и часто задают вопросы не лучшим образом, но постарайтесь, насколько можете, быть доброжелательными. 🤗
Идея сообщества **FastAPI** — быть доброжелательным и гостеприимным. В то же время не допускайте травлю или неуважительное поведение по отношению к другим. Мы должны заботиться друг о друге.
---
Как помочь другим с их проблемами:
Как помочь другим с вопросами (в обсуждениях или Issues):
### Понять вопрос
### Понять вопрос { #understand-the-question }
* Удостоверьтесь, что поняли **цель** и обстоятельства случая вопрошающего.
* Убедитесь, что поняли **цель** и кейс использования задающего вопрос.
* Затем проверьте, что вопрос (в подавляющем большинстве - это вопросы) Вам **ясен**.
* Затем проверьте, что вопрос (в подавляющем большинстве это вопросы) сформулирован **ясно**.
* Во многих случаях вопрос касается решения, которое пользователь придумал сам, но может быть и решение **получше**. Если Вы поймёте проблему и обстоятельства случая, то сможете предложить **альтернативное решение**.
* Во многих случаях спрашивают о воображаемом решении пользователя, но может быть решение **получше**. Если Вы лучше поймёте проблему и кейс, сможете предложить **альтернативное решение**.
* Ежели вопрос Вам непонятен, запросите больше **деталей**.
* Если вопрос непонятен, запросите больше **деталей**.
### Воспроизвести проблему
### Воспроизвести проблему { #reproduce-the-problem }
В большинстве случаев есть что-то связанное с **исходным кодом** вопрошающего.
В большинстве случаев и вопросов есть что-то связанное с **исходным кодом** автора.
И во многих случаях будет предоставлен только фрагмент этого кода, которого недостаточно для **воспроизведения проблемы**.
Во многих случаях предоставляют только фрагмент кода, но этого недостаточно, чтобы **воспроизвести проблему**.
* Попросите предоставить <a href="https://stackoverflow.com/help/minimal-reproducible-example" class="external-link" target="_blank">минимальный воспроизводимый пример</a>, который можно **скопировать** и запустить локально дабы увидеть такую же ошибку, или поведение, или лучше понять обстоятельства случая.
* Попросите предоставить <a href="https://stackoverflow.com/help/minimal-reproducible-example" class="external-link" target="_blank">минимальный воспроизводимый пример</a>, который Вы сможете **скопировать-вставить** и запустить локально, чтобы увидеть ту же ошибку или поведение, или лучше понять их кейс.
* Если на Вас нахлынуло великодушие, то можете попытаться **создать похожий пример** самостоятельно, основываясь только на описании проблемы. Но имейте в виду, что это может занять много времени и, возможно, стоит сначала позадавать вопросы для прояснения проблемы.
* Если чувствуете себя особенно великодушными, можете попытаться **создать такой пример** сами, основываясь только на описании проблемы. Просто помните, что это может занять много времени, и, возможно, сначала лучше попросить уточнить проблему.
### Предложить решение
### Предложить решение { #suggest-solutions }
* После того как Вы поняли вопрос, Вы можете дать **ответ**.
* После того как Вы поняли вопрос, Вы можете дать возможный **ответ**.
* Следует понять **основную проблему и обстоятельства случая**, потому что может быть решение лучше, чем то, которое пытались реализовать.
* Во многих случаях лучше понять **исходную проблему или кейс**, потому что может существовать способ решить её лучше, чем то, что пытаются сделать.
### Попросить закрыть проблему
### Попросить закрыть { #ask-to-close }
Если Вам ответили, высоки шансы, что Вам удалось решить проблему, поздравляю, **Вы - герой**! 🦸
Если Вам ответили, велика вероятность, что Вы решили их проблему, поздравляю, **Вы — герой**! 🦸
* В таком случае, если вопрос решён, попросите **закрыть проблему**.
* Теперь, если проблема решена, можно попросить их:
* В GitHub Discussions: пометить комментарий как **answer** (ответ).
* В GitHub Issues: **закрыть** Issue.
## Отслеживать репозиторий на GitHub
## Отслеживать репозиторий на GitHub { #watch-the-github-repository }
Вы можете "отслеживать" FastAPI на GitHub (кликните по кнопке "watch" наверху справа): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. 👀
Вы можете "отслеживать" FastAPI на GitHub (кликнув по кнопке "watch" наверху справа): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. 👀
Если Вы выберете "Watching" вместо "Releases only", то будете получать уведомления когда кто-либо попросит о помощи с решением его проблемы.
Если Вы выберете "Watching" вместо "Releases only", то будете получать уведомления, когда кто-либо создаёт новый вопрос или Issue. Вы также можете указать, что хотите получать уведомления только о новых Issues, или обсуждениях, или пулл-реквестах и т.д.
Тогда Вы можете попробовать решить эту проблему.
Тогда Вы можете попробовать помочь им с решением этих вопросов.
## Запросить помощь с решением проблемы
## Задать вопросы { #ask-questions }
Вы можете <a href="https://github.com/fastapi/fastapi/issues/new/choose" class="external-link" target="_blank">создать новый запрос с просьбой о помощи</a> в репозитории на GitHub, например:
Вы можете <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">создать новый вопрос</a> в репозитории GitHub, например:
* Задать **вопрос** или попросить помощи в решении **проблемы**.
* Предложить новое **улучшение**.
* Задать **вопрос** или спросить о **проблеме**.
* Предложить новую **возможность**.
**Заметка**: Если Вы создаёте подобные запросы, то я попрошу Вас также оказывать аналогичную помощь другим. 😉
**Заметка**: если Вы это сделаете, то я попрошу Вас также помогать другим. 😉
## Проверять пул-реквесты
## Проверять пулл-реквесты { #review-pull-requests }
Вы можете помочь мне проверять пул-реквесты других участников.
Вы можете помочь мне проверять пулл-реквесты других участников.
И повторюсь, постарайтесь быть доброжелательным. 🤗
И, снова, постарайтесь быть доброжелательными. 🤗
---
О том, что нужно иметь в виду при проверке пул-реквестов:
О том, что нужно иметь в виду и как проверять пулл-реквест:
### Понять проблему
### Понять проблему { #understand-the-problem }
* Во-первых, убедитесь, что **поняли проблему**, которую пул-реквест пытается решить. Для этого может потребоваться продолжительное обсуждение.
* Во-первых, убедитесь, что **поняли проблему**, которую пулл-реквест пытается решить. Возможно, это обсуждалось более подробно в GitHub Discussion или Issue.
* Также есть вероятность, что пул-реквест не актуален, так как проблему можно решить **другим путём**. В таком случае Вы можете указать на этот факт.
* Также есть вероятность, что пулл-реквест не нужен, так как проблему можно решить **другим путём**. Тогда Вы можете предложить или спросить об этом.
### Не переживайте о стиле
### Не переживайте о стиле { #dont-worry-about-style }
* Не стоит слишком беспокоиться о таких вещах, как стиль сообщений в коммитах или количество коммитов. При слиянии пул-реквеста с основной веткой, я буду сжимать и настраивать всё вручную.
* Не стоит слишком беспокоиться о таких вещах, как стиль сообщений в коммитах — при слиянии я выполню squash и настрою коммит вручную.
* Также не беспокойтесь о правилах стиля, для проверки сего есть автоматизированные инструменты.
* Также не беспокойтесь о правилах стиля, это уже проверяют автоматизированные инструменты.
И если всё же потребуется какой-то другой стиль, я попрошу Вас об этом напрямую или добавлю сам коммиты с необходимыми изменениями.
Если будет нужна какая-то другая стилистика или единообразие, я попрошу об этом напрямую или добавлю поверх свои коммиты с нужными изменениями.
### Проверить код
### Проверить код { #check-the-code }
* Проверьте и прочитайте код, посмотрите, какой он имеет смысл, **запустите его локально** и посмотрите, действительно ли он решает поставленную задачу.
* Проверьте и прочитайте код, посмотрите, логичен ли он, **запустите его локально** и проверьте, действительно ли он решает проблему.
* Затем, используя **комментарий**, сообщите, что Вы сделали проверку, тогда я буду знать, что Вы действительно проверили код.
* Затем оставьте **комментарий**, что Вы это сделали, так я пойму, что Вы действительно проверили код.
/// info | Информация
К сожалению, я не могу так просто доверять пул-реквестам, у которых уже есть несколько одобрений.
К сожалению, я не могу просто доверять PR-ам только потому, что у них есть несколько одобрений.
Бывали случаи, что пул-реквесты имели 3, 5 или больше одобрений, вероятно из-за привлекательного описания, но когда я проверял эти пул-реквесты, они оказывались сломаны, содержали ошибки или вовсе не решали проблему, которую, как они утверждали, должны были решить. 😅
Несколько раз было так, что у PR-ов было 3, 5 или больше одобрений, вероятно из-за привлекательного описания, но когда я их проверял, они оказывались сломанными, содержали баги или вовсе не решали заявленную проблему. 😅
Потому это действительно важно - проверять и запускать код, и комментарием уведомлять меня, что Вы проделали эти действия. 🤓
Поэтому очень важно действительно прочитать и запустить код и сообщить мне об этом в комментарии. 🤓
///
* Если Вы считаете, что пул-реквест можно упростить, то можете попросить об этом, но не нужно быть слишком придирчивым, может быть много субъективных точек зрения (и у меня тоже будет своя 🙈), поэтому будет лучше, если Вы сосредоточитесь на фундаментальных вещах.
* Если PR можно упростить, Вы можете попросить об этом, но не нужно быть слишком придирчивым — может быть много субъективных мнений (и у меня тоже 🙈), поэтому лучше сосредоточиться на фундаментальных вещах.
### Тестировать
### Тестировать { #tests }
* Помогите мне проверить, что у пул-реквеста есть **тесты**.
* Помогите мне проверить, что у PR есть **тесты**.
* Проверьте, что тесты **падали** до пул-реквеста. 🚨
* Проверьте, что тесты **падают** до PR. 🚨
* Затем проверьте, что тесты **не валятся** после пул-реквеста. ✅
* Затем проверьте, что тесты **проходят** после PR. ✅
* Многие пул-реквесты не имеют тестов, Вы можете **напомнить** о необходимости добавления тестов или даже **предложить** какие-либо свои тесты. Это одна из тех вещей, которые отнимают много времени и Вы можете помочь с этим.
* Многие PR не имеют тестов — Вы можете **напомнить** добавить тесты или даже **предложить** некоторые тесты сами. Это одна из самых трудозатратных частей, и здесь Вы можете очень помочь.
* Затем добавьте комментарий, что Вы испробовали в ходе проверки. Таким образом я буду знать, как Вы произвели проверку. 🤓
* Затем добавьте комментарий, что Вы попробовали, чтобы я знал, что Вы это проверили. 🤓
## Создать пул-реквест
## Создать пулл-реквест { #create-a-pull-request }
Вы можете [сделать вклад](contributing.md){.internal-link target=_blank} в код фреймворка используя пул-реквесты, например:
Вы можете [сделать вклад](contributing.md){.internal-link target=_blank} в исходный код пулл-реквестами, например:
* Исправить опечатку, которую Вы нашли в документации.
* Поделиться статьёй, видео или подкастом о FastAPI, которые Вы создали или нашли <a href="https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml" class="external-link" target="_blank">изменив этот файл</a>.
* Убедитесь, что Вы добавили свою ссылку в начало соответствующего раздела.
* Помочь с [переводом документации](contributing.md#_8){.internal-link target=_blank} на Ваш язык.
* Вы также можете проверять переводы сделанные другими.
* Исправить опечатку, найденную в документации.
* Поделиться статьёй, видео или подкастом о FastAPI, которые Вы создали или нашли, <a href="https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml" class="external-link" target="_blank">изменив этот файл</a>.
* Убедитесь, что добавили свою ссылку в начало соответствующего раздела.
* Помочь с [переводом документации](contributing.md#translations){.internal-link target=_blank} на Ваш язык.
* Вы также можете проверять переводы, сделанные другими.
* Предложить новые разделы документации.
* Исправить существующуе проблемы/баги.
* Исправить существующую проблему/баг.
* Убедитесь, что добавили тесты.
* Добавить новую возможность.
* Убедитесь, что добавили тесты.
* Убедитесь, что добавили документацию, если она необходима.
* Убедитесь, что добавили документацию, если это уместно.
## Помочь поддерживать FastAPI
## Помочь поддерживать FastAPI { #help-maintain-fastapi }
Помогите мне поддерживать **FastAPI**! 🤓
Предстоит ещё много работы и, по большей части, **ВЫ** можете её сделать.
Предстоит ещё много работы, и, по большей части, **ВЫ** можете её сделать.
Основные задачи, которые Вы можете выполнить прямо сейчас:
* [Помочь другим с их проблемами на GitHub](#github_1){.internal-link target=_blank} (смотрите вышестоящую секцию).
* [Проверить пул-реквесты](#-){.internal-link target=_blank} (смотрите вышестоящую секцию).
* [Помочь другим с вопросами на GitHub](#help-others-with-questions-in-github){.internal-link target=_blank} (смотрите секцию выше).
* [Проверять пулл-реквесты](#review-pull-requests){.internal-link target=_blank} (смотрите секцию выше).
Эти две задачи **отнимают больше всего времени**. Это основная работа по поддержке FastAPI.
Именно эти две задачи **забирают больше всего времени**. Это основная работа по поддержке FastAPI.
Если Вы можете помочь мне с этим, **Вы помогаете поддерживать FastAPI** и следить за тем, чтобы он продолжал **развиваться быстрее и лучше**. 🚀
Если Вы можете помочь мне с этим, **Вы помогаете поддерживать FastAPI** и делаете так, чтобы он продолжал **развиваться быстрее и лучше**. 🚀
## Подключиться к чату
## Подключиться к чату { #join-the-chat }
Подключайтесь к 👥 <a href="https://discord.gg/VQjSZaeJmf" class="external-link" target="_blank"> чату в Discord</a> 👥 и общайтесь с другими участниками сообщества FastAPI.
Подключайтесь к 👥 <a href="https://discord.gg/VQjSZaeJmf" class="external-link" target="_blank">серверу чата в Discord</a> 👥 и общайтесь с другими участниками сообщества FastAPI.
/// tip | Подсказка
Вопросы по проблемам с фреймворком лучше задавать в <a href="https://github.com/fastapi/fastapi/issues/new/choose" class="external-link" target="_blank">GitHub issues</a>, так больше шансов, что Вы получите помощь от [Экспертов FastAPI](fastapi-people.md#_3){.internal-link target=_blank}.
По вопросам — задавайте их в <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">GitHub Discussions</a>, так гораздо выше шанс, что Вы получите помощь от [Экспертов FastAPI](fastapi-people.md#fastapi-experts){.internal-link target=_blank}.
Используйте этот чат только для бесед на отвлечённые темы.
Используйте чат только для прочих общих бесед.
///
### Не использовать чаты для вопросов
Имейте в виду, что чаты позволяют больше "свободного общения", потому там легко задавать вопросы, которые слишком общие и на которые труднее ответить, так что Вы можете не получить нужные Вам ответы.
В разделе "проблемы" на GitHub, есть шаблон, который поможет Вам написать вопрос правильно, чтобы Вам было легче получить хороший ответ или даже решить проблему самостоятельно, прежде чем Вы зададите вопрос. В GitHub я могу быть уверен, что всегда отвечаю на всё, даже если это займет какое-то время. И я не могу сделать то же самое в чатах. 😅
Кроме того, общение в чатах не так легкодоступно для поиска, как в GitHub, потому вопросы и ответы могут потеряться среди другого общения. И только проблемы решаемые на GitHub учитываются в получении лычки [Эксперт FastAPI](fastapi-people.md#_3){.internal-link target=_blank}, так что весьма вероятно, что Вы получите больше внимания на GitHub.
С другой стороны, в чатах тысячи пользователей, а значит есть большие шансы в любое время найти там кого-то, с кем можно поговорить. 😄
## Спонсировать автора
Вы также можете оказать мне финансовую поддержку посредством <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">спонсорства через GitHub</a>.
### Не используйте чат для вопросов { #dont-use-the-chat-for-questions }
Там можно просто купить мне кофе ☕️ в знак благодарности. 😄
Имейте в виду, что в чатах, благодаря "свободному общению", легко задать вопросы, которые слишком общие и на которые сложнее ответить, поэтому Вы можете не получить ответы.
А ещё Вы можете стать Серебряным или Золотым спонсором для FastAPI. 🏅🎉
На GitHub шаблон поможет Вам правильно сформулировать вопрос, чтобы Вам было легче получить хороший ответ или даже решить проблему самостоятельно ещё до того, как спросите. И на GitHub я могу следить за тем, чтобы всегда отвечать на всё, даже если это занимает время. А с чатами я не могу сделать этого лично. 😅
## Спонсировать инструменты, на которых зиждется мощь FastAPI
Кроме того, переписка в чатах хуже ищется, чем на GitHub, поэтому вопросы и ответы могут теряться среди остальных сообщений. И только те, что на GitHub, учитываются для получения лычки [Эксперт FastAPI](fastapi-people.md#fastapi-experts){.internal-link target=_blank}, так что вероятнее всего Вы получите больше внимания именно на GitHub.
Как Вы могли заметить в документации, FastAPI опирается на плечи титанов: Starlette и Pydantic.
С другой стороны, в чатах тысячи пользователей, так что почти всегда есть шанс найти там кого-то для разговора. 😄
Им тоже можно оказать спонсорскую поддержку:
## Спонсировать автора { #sponsor-the-author }
* <a href="https://github.com/sponsors/samuelcolvin" class="external-link" target="_blank">Samuel Colvin (Pydantic)</a>
* <a href="https://github.com/sponsors/encode" class="external-link" target="_blank">Encode (Starlette, Uvicorn)</a>
Если Ваш **продукт/компания** зависят от **FastAPI** или связаны с ним и Вы хотите донести до пользователей информацию о себе, Вы можете спонсировать автора (меня) через <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub Sponsors</a>. В зависимости от уровня поддержки Вы можете получить дополнительные бонусы, например, бейдж в документации. 🎁
---
Благодарствую! 🚀
Спасибо! 🚀

28
docs/ru/docs/history-design-future.md

@ -1,4 +1,4 @@
# История создания и дальнейшее развитие
# История, проектирование и будущее { #history-design-and-future }
Однажды, <a href="https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920" class="external-link" target="_blank">один из пользователей **FastAPI** задал вопрос</a>:
@ -6,7 +6,7 @@
Что ж, вот небольшая часть истории проекта.
## Альтернативы
## Альтернативы { #alternatives }
В течение нескольких лет я, возглавляя различные команды разработчиков, создавал довольно сложные API для машинного обучения, распределённых систем, асинхронных задач, баз данных NoSQL и т.д.
@ -24,45 +24,47 @@
Я всячески избегал создания нового фреймворка в течение нескольких лет. Сначала я пытался собрать все нужные возможности, которые ныне есть в **FastAPI**, используя множество различных фреймворков, плагинов и инструментов.
Но в какой-то момент не осталось другого выбора, кроме как создать что-то, что предоставляло бы все эти возможности сразу. Взять самые лучшие идеи из предыдущих инструментов и, используя введённые в Python подсказки типов (которых не было до версии 3.6), объединить их.
Но в какой-то момент не осталось другого выбора, кроме как создать что-то, что предоставляло бы все эти возможности сразу. Взять самые лучшие идеи из предыдущих инструментов и, используя введённые в Python аннотации типов (которых не было до версии 3.6), объединить их.
</blockquote>
## Исследования
## Исследования { #investigation }
Благодаря опыту использования существующих альтернатив, мы с коллегами изучили их основные идеи и скомбинировали собранные знания наилучшим образом.
Например, стало ясно, что необходимо брать за основу стандартные подсказки типов Python, а самым лучшим подходом является использование уже существующих стандартов.
Например, стало ясно, что необходимо брать за основу стандартные аннотации типов Python.
Также наилучшим подходом является использование уже существующих стандартов.
Итак, прежде чем приступить к написанию **FastAPI**, я потратил несколько месяцев на изучение OpenAPI, JSON Schema, OAuth2, и т.п. для понимания их взаимосвязей, совпадений и различий.
## Дизайн
## Проектирование { #design }
Затем я потратил некоторое время на придумывание "API" разработчика, который я хотел иметь как пользователь (как разработчик, использующий FastAPI).
Я проверил несколько идей на самых популярных редакторах кода среди Python-разработчиков: PyCharm, VS Code, Jedi.
Я проверил несколько идей на самых популярных редакторах кода: PyCharm, VS Code, редакторы на базе Jedi.
Данные по редакторам я взял из <a href="https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools" class="external-link" target="_blank">опроса Python-разработчиков</a>, который охватываает около 80% пользователей.
Данные по редакторам я взял из <a href="https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools" class="external-link" target="_blank">опроса Python-разработчиков</a>, который охватывает около 80% пользователей.
Это означает, что **FastAPI** был специально проверен на редакторах, используемых 80% Python-разработчиками. И поскольку большинство других редакторов, как правило, работают аналогичным образом, все его преимущества должны работать практически для всех редакторов.
Таким образом, я смог найти наилучшие способы сократить дублирование кода, обеспечить повсеместное автодополнение, проверку типов и ошибок и т.д.
Таким образом, я смог найти наилучшие способы сократить дублирование кода, обеспечить повсеместное автозавершение, проверку типов и ошибок и т.д.
И все это, чтобы все пользователи могли получать наилучший опыт разработки.
## Зависимости
## Зависимости { #requirements }
Протестировав несколько вариантов, я решил, что в качестве основы буду использовать <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">**Pydantic**</a> и его преимущества.
По моим предложениям был изменён код этого фреймворка, чтобы сделать его полностью совместимым с JSON Schema, поддержать различные способы определения ограничений и улучшить помощь редакторов (проверки типов, автозаполнение).
По моим предложениям был изменён код этого фреймворка, чтобы сделать его полностью совместимым с JSON Schema, поддержать различные способы определения ограничений и улучшить поддержку в редакторах кода (проверки типов, автозавершение) на основе тестов в нескольких редакторах.
В то же время, я принимал участие в разработке <a href="https://www.starlette.io/" class="external-link" target="_blank">**Starlette**</a>, ещё один из основных компонентов FastAPI.
## Разработка
## Разработка { #development }
К тому времени, когда я начал создавать **FastAPI**, большинство необходимых деталей уже существовало, дизайн был определён, зависимости и прочие инструменты были готовы, а знания о стандартах и спецификациях были четкими и свежими.
## Будущее
## Будущее { #future }
Сейчас уже ясно, что **FastAPI** со своими идеями стал полезен многим людям.

321
docs/ru/docs/index.md

@ -1,4 +1,4 @@
# FastAPI
# FastAPI { #fastapi }
<style>
.md-content .md-typeset h1 { display: none; }
@ -8,47 +8,47 @@
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
</p>
<p align="center">
<em>Готовый к внедрению высокопроизводительный фреймворк, простой в изучении и разработке.</em>
<em>Фреймворк FastAPI: высокая производительность, прост в изучении, быстрый в разработке, готов к продакшн</em>
</p>
<p align="center">
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Тест">
</a>
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Покрытие">
</a>
<a href="https://pypi.org/project/fastapi" target="_blank">
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Версия пакета">
</a>
<a href="https://pypi.org/project/fastapi" target="_blank">
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Поддерживаемые версии Python">
</a>
</p>
---
**Документация**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
**Документация**: <a href="https://fastapi.tiangolo.com/ru" target="_blank">https://fastapi.tiangolo.com</a>
**Исходный код**: <a href="https://github.com/fastapi/fastapi" target="_blank">https://github.com/fastapi/fastapi</a>
---
FastAPI — это современный, быстрый (высокопроизводительный) веб-фреймворк для создания API используя Python, в основе которого лежит стандартная аннотация типов Python.
FastAPI — это современный, быстрый (высокопроизводительный) веб-фреймворк для создания API на Python, основанный на стандартных аннотациях типов Python.
Ключевые особенности:
* **Скорость**: Очень высокая производительность, на уровне **NodeJS** и **Go** (благодаря Starlette и Pydantic). [Один из самых быстрых фреймворков Python](#_10).
* **Быстрота разработки**: Увеличьте скорость разработки примерно на 200–300%. *
* **Скорость**: Очень высокая производительность, на уровне **NodeJS** и **Go** (благодаря Starlette и Pydantic). [Один из самых быстрых доступных фреймворков Python](#performance).
* **Быстрота разработки**: Увеличьте скорость разработки фич примерно на 200–300%. *
* **Меньше ошибок**: Сократите примерно на 40% количество ошибок, вызванных человеком (разработчиком). *
* **Интуитивно понятный**: Отличная поддержка редактора. <abbr title="также известное как автозаполнение, автодополнение, IntelliSense">Автозавершение</abbr> везде. Меньше времени на отладку.
* **Лёгкость**: Разработан так, чтобы его было легко использовать и осваивать. Меньше времени на чтение документации.
* **Краткость**: Сведите к минимуму дублирование кода. Каждый объявленный параметр - определяет несколько функций. Меньше ошибок.
* **Надежность**: Получите готовый к работе код. С автоматической интерактивной документацией.
* **На основе стандартов**: Основан на открытых стандартах API и полностью совместим с ними: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (ранее известном как Swagger) и <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
* **Интуитивность**: Отличная поддержка редактора кода. <abbr title="также известное как: автодополнение, IntelliSense">Автозавершение</abbr> везде. Меньше времени на отладку.
* **Простота**: Разработан так, чтобы его было легко использовать и осваивать. Меньше времени на чтение документации.
* **Краткость**: Минимизируйте дублирование кода. Несколько возможностей из каждого объявления параметров. Меньше ошибок.
* **Надежность**: Получите код, готовый к продакшн. С автоматической интерактивной документацией.
* **На основе стандартов**: Основан на открытых стандартах API и полностью совместим с ними: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (ранее известный как Swagger) и <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
<small>* оценка на основе тестов внутренней команды разработчиков, создающих производственные приложения.</small>
<small>* оценка на основе тестов внутренней команды разработчиков, создающих продакшн-приложения.</small>
## Спонсоры
## Спонсоры { #sponsors }
<!-- sponsors -->
@ -63,23 +63,23 @@ FastAPI — это современный, быстрый (высокопрои
<!-- /sponsors -->
<a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">Другие спонсоры</a>
<a href="https://fastapi.tiangolo.com/ru/fastapi-people/#sponsors" class="external-link" target="_blank">Другие спонсоры</a>
## Отзывы
## Мнения { #opinions }
"_В последнее время я много где использую **FastAPI**. [...] На самом деле я планирую использовать его для всех **сервисов машинного обучения моей команды в Microsoft**. Некоторые из них интегрируются в основной продукт **Windows**, а некоторые — в продукты **Office**._"
"_[...] В последнее время я много где использую **FastAPI**. [...] На самом деле я планирую использовать его для всех **ML-сервисов моей команды в Microsoft**. Некоторые из них интегрируются в основной продукт **Windows**, а некоторые — в продукты **Office**._"
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/fastapi/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
---
"_Мы использовали библиотеку **FastAPI** для создания сервера **REST**, к которому можно делать запросы для получения **прогнозов**. [для Ludwig]_"
"_Мы начали использовать библиотеку **FastAPI**, чтобы поднять **REST**-сервер, к которому можно обращаться за **предсказаниями**. [для Ludwig]_"
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
---
"_**Netflix** рада объявить о выпуске опенсорсного фреймворка для оркестровки **антикризисного управления**: **Dispatch**! [создана с помощью **FastAPI**]_"
"_**Netflix** рада объявить об открытом релизе нашего фреймворка оркестрации **антикризисного управления**: **Dispatch**! [создан с помощью **FastAPI**]_"
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
@ -87,68 +87,66 @@ FastAPI — это современный, быстрый (высокопрои
"_Я в полном восторге от **FastAPI**. Это так весело!_"
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://x.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong>Ведущий подкаста <a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a></strong> <a href="https://x.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
---
"_Честно говоря, то, что вы создали, выглядит очень солидно и отполировано. Во многих смыслах я хотел, чтобы **Hug** был именно таким — это действительно вдохновляет, когда кто-то создаёт такое._"
"_Честно говоря, то, что вы создали, выглядит очень солидно и отполировано. Во многих смыслах это то, чем я хотел видеть **Hug** — очень вдохновляет видеть, как кто-то это создал._"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="https://github.com/hugapi/hug" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong>Создатель <a href="https://github.com/hugapi/hug" target="_blank">Hug</a></strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
"_Если вы хотите изучить какой-нибудь **современный фреймворк** для создания REST API, ознакомьтесь с **FastAPI** [...] Он быстрый, лёгкий и простой в изучении [...]_"
"_Если вы хотите изучить один **современный фреймворк** для создания REST API, посмотрите **FastAPI** [...] Он быстрый, простой в использовании и лёгкий в изучении [...]_"
"_Мы перешли на **FastAPI** для наших **API** [...] Я думаю, вам тоже понравится [...]_"
"_Мы переключились на **FastAPI** для наших **API** [...] Думаю, вам тоже понравится [...]_"
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <a href="https://spacy.io" target="_blank">spaCy</a> creators</strong> <a href="https://x.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://x.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong>Основатели <a href="https://explosion.ai" target="_blank">Explosion AI</a> — создатели <a href="https://spacy.io" target="_blank">spaCy</a></strong> <a href="https://x.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://x.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
---
## **Typer**, интерфейс командной строки для FastAPI
"_Если кто-то собирается делать продакшн-API на Python, я настоятельно рекомендую **FastAPI**. Он **прекрасно спроектирован**, **прост в использовании** и **отлично масштабируется**, стал **ключевым компонентом** нашей стратегии API-first и приводит в действие множество автоматизаций и сервисов, таких как наш Virtual TAC Engineer._"
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
Если вы создаете приложение <abbr title="Интерфейс командной строки">CLI</abbr> для использования в терминале вместо веб-API, ознакомьтесь с <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
<div style="text-align: right; margin-right: 10%;">Deon Pillsbury - <strong>Cisco</strong> <a href="https://www.linkedin.com/posts/deonpillsbury_cisco-cx-python-activity-6963242628536487936-trAp/" target="_blank"><small>(ref)</small></a></div>
**Typer** — младший брат FastAPI. И он предназначен для использования в качестве **интерфейса командной строки для FastAPI**. ⌨️ 🚀
---
## Зависимости
## **Typer**, FastAPI для CLI { #typer-the-fastapi-of-clis }
FastAPI стоит на плечах гигантов:
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
* <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> для части связанной с вебом.
* <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> для части связанной с данными.
Если вы создаёте приложение <abbr title="Command Line Interface – Интерфейс командной строки">CLI</abbr> для использования в терминале вместо веб-API, посмотрите <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
## Установка
**Typer** — младший брат FastAPI. И он задуман как **FastAPI для CLI**. ⌨️ 🚀
<div class="termy">
## Зависимости { #requirements }
```console
$ pip install fastapi
FastAPI стоит на плечах гигантов:
---> 100%
```
* <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> для части, связанной с вебом.
* <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> для части, связанной с данными.
</div>
## Установка { #installation }
Вам также понадобится сервер ASGI для производства, такой как <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a> или <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>.
Создайте и активируйте <a href="https://fastapi.tiangolo.com/ru/virtual-environments/" class="external-link" target="_blank">виртуальное окружение</a>, затем установите FastAPI:
<div class="termy">
```console
$ pip install "uvicorn[standard]"
$ pip install "fastapi[standard]"
---> 100%
```
</div>
## Пример
**Примечание**: Обязательно заключите `"fastapi[standard]"` в кавычки, чтобы это работало во всех терминалах.
## Пример { #example }
### Создание
### Создание { #create-it }
* Создайте файл `main.py` со следующим содержимым:
Создайте файл `main.py` со следующим содержимым:
```Python
from typing import Union
@ -193,22 +191,35 @@ async def read_item(item_id: int, q: Union[str, None] = None):
**Примечание**:
Если вы не знаете, проверьте раздел _"Торопитесь?"_ <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">в документации об `async` и `await`</a>.
Если не уверены, посмотрите раздел _«Нет времени?»_ о <a href="https://fastapi.tiangolo.com/ru/async/#in-a-hurry" target="_blank">`async` и `await` в документации</a>.
</details>
### Запуск
### Запуск { #run-it }
Запустите сервер с помощью:
Запустите сервер командой:
<div class="termy">
```console
$ uvicorn main:app --reload
$ fastapi dev main.py
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
@ -216,21 +227,21 @@ INFO: Application startup complete.
</div>
<details markdown="1">
<summary>О команде <code>uvicorn main:app --reload</code>...</summary>
<summary>О команде <code>fastapi dev main.py</code>...</summary>
Команда `uvicorn main:app` относится к:
Команда `fastapi dev` читает ваш файл `main.py`, находит в нём приложение **FastAPI** и запускает сервер с помощью <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a>.
* `main`: файл `main.py` (модуль Python).
* `app`: объект, созданный внутри `main.py` с помощью строки `app = FastAPI()`.
* `--reload`: перезапуск сервера после изменения кода. Делайте это только во время разработки.
По умолчанию `fastapi dev` запускается с включённой авто-перезагрузкой для локальной разработки.
Подробнее в <a href="https://fastapi.tiangolo.com/ru/fastapi-cli/" target="_blank">документации по FastAPI CLI</a>.
</details>
### Проверка
### Проверка { #check-it }
Откройте браузер на <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
Вы увидите следующий JSON ответ:
Вы увидите JSON-ответ:
```JSON
{"item_id": 5, "q": "somequery"}
@ -239,31 +250,31 @@ INFO: Application startup complete.
Вы уже создали API, который:
* Получает HTTP-запросы по _путям_ `/` и `/items/{item_id}`.
* И первый и второй _путь_ используют `GET` <em>операции</em> (также известные как HTTP _методы_).
* _путь_ `/items/{item_id}` имеет _параметр пути_ `item_id`, который должен быть `int`.
* _путь_ `/items/{item_id}` имеет необязательный `str` _параметр запроса_ `q`.
* Оба _пути_ используют `GET` <em>операции</em> (также известные как HTTP _методы_).
* _Путь_ `/items/{item_id}` имеет _параметр пути_ `item_id`, который должен быть `int`.
* _Путь_ `/items/{item_id}` имеет необязательный `str` _параметр запроса_ `q`.
### Интерактивная документация по API
### Интерактивная документация API { #interactive-api-docs }
Перейдите на <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Вы увидите автоматическую интерактивную документацию API (предоставленную <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
Вы увидите автоматическую интерактивную документацию API (предоставлена <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### Альтернативная документация по API
### Альтернативная документация API { #alternative-api-docs }
А теперь перейдите на <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
Теперь откройте <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
Вы увидите альтернативную автоматическую документацию (предоставленную <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
Вы увидите альтернативную автоматическую документацию (предоставлена <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## Пример обновления
## Пример обновления { #example-upgrade }
Теперь измените файл `main.py`, чтобы получить тело ответа из `PUT` запроса.
Теперь измените файл `main.py`, чтобы принимать тело запроса из `PUT` запроса.
Объявите тело, используя стандартную типизацию Python, спасибо Pydantic.
Объявите тело, используя стандартные типы Python, спасибо Pydantic.
```Python hl_lines="4 9-12 25-27"
from typing import Union
@ -295,39 +306,39 @@ def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
Сервер должен перезагрузиться автоматически (потому что вы добавили `--reload` к команде `uvicorn` выше).
Сервер `fastapi dev` должен перезагрузиться автоматически.
### Интерактивное обновление документации API
### Обновление интерактивной документации API { #interactive-api-docs-upgrade }
Перейдите на <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
* Интерактивная документация API будет автоматически обновляться, включая новое тело:
* Интерактивная документация API будет автоматически обновлена, включая новое тело:
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* Нажмите на кнопку "Try it out", это позволит вам заполнить параметры и напрямую взаимодействовать с API:
* Нажмите кнопку «Try it out», это позволит вам заполнить параметры и напрямую взаимодействовать с API:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-04-swagger-03.png)
* Затем нажмите кнопку "Execute", пользовательский интерфейс свяжется с вашим API, отправит параметры, получит результаты и отобразит их на экране:
* Затем нажмите кнопку «Execute», интерфейс свяжется с вашим API, отправит параметры, получит результаты и отобразит их на экране:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png)
### Альтернативное обновление документации API
### Обновление альтернативной документации API { #alternative-api-docs-upgrade }
А теперь перейдите на <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
Теперь откройте <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
* Альтернативная документация также будет отражать новый параметр и тело запроса:
* Альтернативная документация также отразит новый параметр запроса и тело:
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### Подведём итоги
### Подведём итоги { #recap }
Таким образом, вы объявляете **один раз** типы параметров, тело и т. д. в качестве параметров функции.
Итак, вы объявляете **один раз** типы параметров, тела запроса и т.д. как параметры функции.
Вы делаете это используя стандартную современную типизацию Python.
Вы делаете это с помощью стандартных современных типов Python.
Вам не нужно изучать новый синтаксис, методы или классы конкретной библиотеки и т. д.
Вам не нужно изучать новый синтаксис, методы или классы конкретной библиотеки и т.п.
Только стандартный **Python**.
@ -343,29 +354,29 @@ item_id: int
item: Item
```
... и с этим единственным объявлением вы получаете:
...и с этим единственным объявлением вы получаете:
* Поддержка редактора, в том числе:
* Поддержку редактора кода, включая:
* Автозавершение.
* Проверка типов.
* Валидация данных:
* Автоматические и четкие ошибки, когда данные недействительны.
* Проверка даже для глубоко вложенных объектов JSON.
* <abbr title="также известный как: сериализация, синтаксический анализ, маршалинг">Преобразование</abbr> входных данных: поступающие из сети в объекты Python с соблюдением типов. Чтение из:
* Проверку типов.
* Валидацию данных:
* Автоматические и понятные ошибки, когда данные некорректны.
* Валидацию даже для глубоко вложенных объектов JSON.
* <abbr title="также известное как: сериализация, парсинг, маршалинг">Преобразование</abbr> входных данных: из сети в данные и типы Python. Чтение из:
* JSON.
* Параметров пути.
* Параметров запроса.
* Cookies.
* Заголовков.
* HTTP-заголовков.
* Форм.
* Файлов.
* <abbr title="также известный как: сериализация, синтаксический анализ, маршалинг">Преобразование</abbr> выходных данных: преобразование объектов Python в данные передаваемые по сети интернет (такие как JSON):
* Преобразование типов Python (`str`, `int`, `float`, `bool`, `list`, и т.д.).
* <abbr title="также известное как: сериализация, парсинг, маршалинг">Преобразование</abbr> выходных данных: из данных и типов Python в данные сети (например, JSON):
* Преобразование типов Python (`str`, `int`, `float`, `bool`, `list` и т.д.).
* Объекты `datetime`.
* Объекты `UUID`.
* Модели баз данных.
* ...и многое другое.
* Автоматическая интерактивная документация по API, включая 2 альтернативных пользовательских интерфейса:
* Автоматическую интерактивную документацию API, включая 2 альтернативных интерфейса:
* Swagger UI.
* ReDoc.
@ -373,28 +384,28 @@ item: Item
Возвращаясь к предыдущему примеру кода, **FastAPI** будет:
* Проверять наличие `item_id` в пути для запросов `GET` и `PUT`.
* Проверять, что `item_id` имеет тип `int` для запросов `GET` и `PUT`.
* Если это не так, клиент увидит полезную чёткую ошибку.
* Валидировать наличие `item_id` в пути для `GET` и `PUT` запросов.
* Валидировать, что `item_id` имеет тип `int` для `GET` и `PUT` запросов.
* Если это не так, клиент увидит полезную понятную ошибку.
* Проверять, есть ли необязательный параметр запроса с именем `q` (например, `http://127.0.0.1:8000/items/foo?q=somequery`) для `GET` запросов.
* Поскольку параметр `q` объявлен с `= None`, он является необязательным.
* Без `None` он был бы необходим (как тело в случае с `PUT`).
* Для `PUT` запросов к `/items/{item_id}` читать тело как JSON:
* Проверять, что у него есть обязательный атрибут `name`, который должен быть `str`.
* Проверять, что у него есть обязательный атрибут `price`, который должен быть `float`.
* Проверять, что у него есть необязательный атрибут `is_offer`, который должен быть `bool`, если он присутствует.
* Все это также будет работать для глубоко вложенных объектов JSON.
* Преобразовывать из и в JSON автоматически.
* Документировать с помощью OpenAPI все, что может быть использовано:
* Системы интерактивной документации.
* Системы автоматической генерации клиентского кода для многих языков.
* Обеспечит 2 интерактивных веб-интерфейса документации напрямую.
* Поскольку параметр `q` объявлен с `= None`, он необязателен.
* Без `None` он был бы обязательным (как тело запроса в случае с `PUT`).
* Для `PUT` запросов к `/items/{item_id}` читать тело запроса как JSON:
* Проверять, что есть обязательный атрибут `name`, который должен быть `str`.
* Проверять, что есть обязательный атрибут `price`, который должен быть `float`.
* Проверять, что есть необязательный атрибут `is_offer`, который должен быть `bool`, если он присутствует.
* Всё это также будет работать для глубоко вложенных объектов JSON.
* Автоматически преобразовывать из и в JSON.
* Документировать всё с помощью OpenAPI, что может быть использовано:
* Системами интерактивной документации.
* Системами автоматической генерации клиентского кода для многих языков.
* Предоставлять 2 веб-интерфейса интерактивной документации напрямую.
---
Мы только немного копнули поверхность, но вы уже поняли, как все это работает.
Мы только поверхностно ознакомились, но вы уже понимаете, как всё это работает.
Попробуйте изменить строку с помощью:
Попробуйте изменить строку:
```Python
return {"item_name": item.name, "item_id": item_id}
@ -406,61 +417,85 @@ item: Item
... "item_name": item.name ...
```
...в:
...на:
```Python
... "item_price": item.price ...
```
... и посмотрите, как ваш редактор будет автоматически заполнять атрибуты и узнавать их типы:
...и посмотрите, как ваш редактор кода будет автоматически дополнять атрибуты и знать их типы:
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
Более полный пример с дополнительными функциями см. в <a href="https://fastapi.tiangolo.com/tutorial/">Учебное руководство - Руководство пользователя</a>.
Более полный пример с дополнительными возможностями см. в <a href="https://fastapi.tiangolo.com/ru/tutorial/">Учебник - Руководство пользователя</a>.
**Осторожно, спойлер**: руководство пользователя включает в себя:
**Осторожно, спойлер**: учебник - руководство включает:
* Объявление **параметров** из других мест, таких как: **заголовки**, **cookies**, **поля формы** и **файлы**.
* Как установить **ограничительные проверки** такие как `maximum_length` или `regex`.
* Очень мощная и простая в использовании система **<abbr title="также известная как компоненты, ресурсы, провайдеры, сервисы, инъекции">внедрения зависимостей</abbr>**.
* Безопасность и аутентификация, включая поддержку **OAuth2** с **токенами JWT** и **HTTP Basic** аутентификацию.
* Более продвинутые (но столь же простые) методы объявления **глубоко вложенных моделей JSON** (спасибо Pydantic).
* **GraphQL** интеграция с <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> и другими библиотеками.
* Объявление **параметров** из других источников: **HTTP-заголовки**, **cookies**, **поля формы** и **файлы**.
* Как задать **ограничения валидации** вроде `maximum_length` или `regex`.
* Очень мощную и простую в использовании систему **<abbr title="также известная как: компоненты, ресурсы, провайдеры, сервисы, инъекции">внедрения зависимостей</abbr>**.
* Безопасность и аутентификацию, включая поддержку **OAuth2** с **JWT токенами** и **HTTP Basic** аутентификацию.
* Более продвинутые (но столь же простые) приёмы объявления **глубоко вложенных JSON-моделей** (спасибо Pydantic).
* Интеграцию **GraphQL** с <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> и другими библиотеками.
* Множество дополнительных функций (благодаря Starlette), таких как:
* **Веб-сокеты**
* очень простые тесты на основе HTTPX и `pytest`
* **WebSockets**
* чрезвычайно простые тесты на основе HTTPX и `pytest`
* **CORS**
* **Cookie сеансы(сессии)**
* **сессии с использованием cookie**
* ...и многое другое.
## Производительность
## Производительность { #performance }
Независимые бенчмарки TechEmpower показывают приложения **FastAPI**, работающие под управлением Uvicorn, как <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">один из самых быстрых доступных фреймворков Python</a>, уступающий только самим Starlette и Uvicorn (используются внутри FastAPI). (*)
Чтобы узнать больше, см. раздел <a href="https://fastapi.tiangolo.com/ru/benchmarks/" class="internal-link" target="_blank">Бенчмарки</a>.
## Зависимости { #dependencies }
Независимые тесты TechEmpower показывают приложения **FastAPI**, работающие под управлением Uvicorn, как <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">один из самых быстрых доступных фреймворков Python</a>, уступающий только самим Starlette и Uvicorn (используемых внутри FastAPI). (*)
FastAPI зависит от Pydantic и Starlette.
Чтобы узнать больше об этом, см. раздел <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Тесты производительности</a>.
### Зависимости `standard` { #standard-dependencies }
## Необязательные зависимости
Когда вы устанавливаете FastAPI с помощью `pip install "fastapi[standard]"`, он идёт с группой опциональных зависимостей `standard`:
Используется Pydantic:
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email-validator</code></a> - для проверки электронной почты.
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email-validator</code></a> — для проверки адресов электронной почты.
Используется Starlette:
* <a href="https://www.python-httpx.org" target="_blank"><code>HTTPX</code></a> - Обязательно, если вы хотите использовать `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Обязательно, если вы хотите использовать конфигурацию шаблона по умолчанию.
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Обязательно, если вы хотите поддерживать форму <abbr title="преобразование строки, полученной из HTTP-запроса, в данные Python">"парсинга"</abbr> с помощью `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Обязательно, для поддержки `SessionMiddleware`.
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Обязательно, для поддержки `SchemaGenerator` Starlette (возможно, вам это не нужно с FastAPI).
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> — обязателен, если вы хотите использовать `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> — обязателен, если вы хотите использовать конфигурацию шаблонов по умолчанию.
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> — обязателен, если вы хотите поддерживать <abbr title="преобразование строки, полученной из HTTP-запроса, в данные Python">«парсинг»</abbr> форм через `request.form()`.
Используется FastAPI:
* <a href="https://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> — сервер, который загружает и обслуживает ваше приложение. Включает `uvicorn[standard]`, содержащий некоторые зависимости (например, `uvloop`), нужные для высокой производительности.
* `fastapi-cli[standard]` — чтобы предоставить команду `fastapi`.
* Включает `fastapi-cloud-cli`, который позволяет развернуть ваше приложение FastAPI в <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>.
### Без зависимостей `standard` { #without-standard-dependencies }
Если вы не хотите включать опциональные зависимости `standard`, можно установить `pip install fastapi` вместо `pip install "fastapi[standard]"`.
### Без `fastapi-cloud-cli` { #without-fastapi-cloud-cli }
Если вы хотите установить FastAPI со стандартными зависимостями, но без `fastapi-cloud-cli`, установите `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
### Дополнительные опциональные зависимости { #additional-optional-dependencies }
Есть дополнительные зависимости, которые вы можете установить.
Дополнительные опциональные зависимости Pydantic:
Используется FastAPI / Starlette:
* <a href="https://docs.pydantic.dev/latest/usage/pydantic_settings/" target="_blank"><code>pydantic-settings</code></a> — для управления настройками.
* <a href="https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/" target="_blank"><code>pydantic-extra-types</code></a> — дополнительные типы для использования с Pydantic.
* <a href="https://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - сервер, который загружает и обслуживает ваше приложение.
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Обязательно, если вы хотите использовать `ORJSONResponse`.
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Обязательно, если вы хотите использовать `UJSONResponse`.
Дополнительные опциональные зависимости FastAPI:
Вы можете установить все это с помощью `pip install "fastapi[all]"`.
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> — обязателен, если вы хотите использовать `ORJSONResponse`.
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> — обязателен, если вы хотите использовать `UJSONResponse`.
## Лицензия
## Лицензия { #license }
Этот проект распространяется на условиях лицензии MIT.

2
docs/ru/docs/learn/index.md

@ -1,4 +1,4 @@
# Обучение
# Обучение { #learn }
Здесь представлены вводные разделы и учебные пособия для изучения **FastAPI**.

112
docs/ru/docs/project-generation.md

@ -1,84 +1,28 @@
# Генераторы проектов - Шаблоны
Чтобы начать работу быстрее, Вы можете использовать "генераторы проектов", в которые включены множество начальных настроек для функций безопасности, баз данных и некоторые <dfn title="также известные как маршруты, URLы, ручки, ">эндпоинты</dfn> API.
В генераторе проектов всегда будут предустановлены какие-то настройки, которые Вам следует обновить и подогнать под свои нужды, но это может быть хорошей отправной точкой для Вашего проекта.
## Full Stack FastAPI PostgreSQL
GitHub: <a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-fastapi-postgresql</a>
### Full Stack FastAPI PostgreSQL - Особенности
* Полностью интегрирован с **Docker** (основан на Docker).
* Развёртывается в режиме Docker Swarm.
* Интегрирован с **Docker Compose** и оптимизирован для локальной разработки.
* **Готовый к реальной работе** веб-сервер Python использующий Uvicorn и Gunicorn.
* Бэкенд построен на фреймворке <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">**FastAPI**</a>:
* **Быстрый**: Высокопроизводительный, на уровне **NodeJS** и **Go** (благодаря Starlette и Pydantic).
* **Интуитивно понятный**: Отличная поддержка редактора. <dfn title="также известное как автозаполнение, интеллектуальность, автозавершение">Автодополнение кода</dfn> везде. Меньше времени на отладку.
* **Простой**: Разработан так, чтоб быть простым в использовании и изучении. Меньше времени на чтение документации.
* **Лаконичный**: Минимизировано повторение кода. Каждый объявленный параметр определяет несколько функций.
* **Надёжный**: Получите готовый к работе код. С автоматической интерактивной документацией.
* **Стандартизированный**: Основан на открытых стандартах API (<a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> и <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>) и полностью совместим с ними.
* <a href="https://fastapi.tiangolo.com/features/" class="external-link" target="_blank">**Множество других возможностей**</a> включая автоматическую проверку и сериализацию данных, интерактивную документацию, аутентификацию с помощью OAuth2 JWT-токенов и т.д.
* **Безопасное хранение паролей**, которые хэшируются по умолчанию.
* Аутентификация посредством **JWT-токенов**.
* <dfn title="Python-объекты связанные с базами данных">Модели</dfn> **SQLAlchemy** (независящие от расширений Flask, а значит могут быть непосредственно использованы процессами Celery).
* Базовая модель пользователя (измените или удалите её по необходимости).
* **Alembic** для организации миграций.
* **CORS** (Совместное использование ресурсов из разных источников).
* **Celery**, процессы которого могут выборочно импортировать и использовать модели и код из остальной части бэкенда.
* Тесты, на основе **Pytest**, интегрированные в Docker, чтобы Вы могли полностью проверить Ваше API, независимо от базы данных. Так как тесты запускаются в Docker, для них может создаваться новое хранилище данных каждый раз (Вы можете, по своему желанию, использовать ElasticSearch, MongoDB, CouchDB или другую СУБД, только лишь для проверки - будет ли Ваше API работать с этим хранилищем).
* Простая интеграция Python с **Jupyter Kernels** для разработки удалённо или в Docker с расширениями похожими на Atom Hydrogen или Visual Studio Code Jupyter.
* Фронтенд построен на фреймворке **Vue**:
* Сгенерирован с помощью Vue CLI.
* Поддерживает **аутентификацию с помощью JWT-токенов**.
* Страница логина.
* Перенаправление на страницу главной панели мониторинга после логина.
* Главная страница мониторинга с возможностью создания и изменения пользователей.
* Пользователь может изменять свои данные.
* **Vuex**.
* **Vue-router**.
* **Vuetify** для конструирования красивых компонентов страниц.
* **TypeScript**.
* Сервер Docker основан на **Nginx** (настроен для удобной работы с Vue-router).
* Многоступенчатая сборка Docker, то есть Вам не нужно сохранять или коммитить скомпилированный код.
* Тесты фронтенда запускаются во время сборки (можно отключить).
* Сделан настолько модульно, насколько возможно, поэтому работает "из коробки", но Вы можете повторно сгенерировать фронтенд с помощью Vue CLI или создать то, что Вам нужно и повторно использовать то, что захотите.
* **PGAdmin** для СУБД PostgreSQL, которые легко можно заменить на PHPMyAdmin и MySQL.
* **Flower** для отслеживания работы Celery.
* Балансировка нагрузки между фронтендом и бэкендом с помощью **Traefik**, а значит, Вы можете расположить их на одном домене, разделив url-пути, так как они обслуживаются разными контейнерами.
* Интеграция с Traefik включает автоматическую генерацию сертификатов Let's Encrypt для поддержки протокола **HTTPS**.
* GitLab **CI** (непрерывная интеграция), которая включает тестирование фронтенда и бэкенда.
## Full Stack FastAPI Couchbase
GitHub: <a href="https://github.com/tiangolo/full-stack-fastapi-couchbase" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-fastapi-couchbase</a>
⚠️ **ПРЕДУПРЕЖДЕНИЕ** ⚠️
Если Вы начинаете новый проект, ознакомьтесь с представленными здесь альтернативами.
Например, генератор проектов <a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" class="external-link" target="_blank">Full Stack FastAPI PostgreSQL</a> может быть более подходящей альтернативой, так как он активно поддерживается и используется. И он включает в себя все новые возможности и улучшения.
Но никто не запрещает Вам использовать генератор с СУБД Couchbase, возможно, он всё ещё работает нормально. Или у Вас уже есть проект, созданный с помощью этого генератора ранее, и Вы, вероятно, уже обновили его в соответствии со своими потребностями.
Вы можете прочитать о нём больше в документации соответствующего репозитория.
## Full Stack FastAPI MongoDB
...может быть когда-нибудь появится, в зависимости от наличия у меня свободного времени и прочих факторов. 😅 🎉
## Модели машинного обучения на основе spaCy и FastAPI
GitHub: <a href="https://github.com/microsoft/cookiecutter-spacy-fastapi" class="external-link" target="_blank">https://github.com/microsoft/cookiecutter-spacy-fastapi</a>
### Модели машинного обучения на основе spaCy и FastAPI - Особенности
* Интеграция с моделями **spaCy** NER.
* Встроенный формат запросов к **когнитивному поиску Azure**.
* **Готовый к реальной работе** веб-сервер Python использующий Uvicorn и Gunicorn.
* Встроенное развёртывание на основе **Azure DevOps** Kubernetes (AKS) CI/CD.
* **Многоязычность**. Лёгкий выбор одного из встроенных в spaCy языков во время настройки проекта.
* **Легко подключить** модели из других фреймворков (Pytorch, Tensorflow) не ограничиваясь spaCy.
# Шаблон Full Stack FastAPI { #full-stack-fastapi-template }
Шаблоны, хотя обычно поставляются с определённой конфигурацией, спроектированы так, чтобы быть гибкими и настраиваемыми. Это позволяет вам изменять их и адаптировать под требования вашего проекта, что делает их отличной отправной точкой. 🏁
Вы можете использовать этот шаблон для старта: в нём уже сделана значительная часть начальной настройки, безопасность, база данных и несколько эндпоинтов API.
Репозиторий GitHub: <a href="https://github.com/tiangolo/full-stack-fastapi-template" class="external-link" target="_blank">Full Stack FastAPI Template</a>
## Шаблон Full Stack FastAPI — Технологический стек и возможности { #full-stack-fastapi-template-technology-stack-and-features }
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com/ru) для бэкенд‑API на Python.
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com) для взаимодействия с SQL‑базой данных на Python (ORM).
- 🔍 [Pydantic](https://docs.pydantic.dev), используется FastAPI, для валидации данных и управления настройками.
- 💾 [PostgreSQL](https://www.postgresql.org) в качестве SQL‑базы данных.
- 🚀 [React](https://react.dev) для фронтенда.
- 💃 Используются TypeScript, хуки, [Vite](https://vitejs.dev) и другие части современного фронтенд‑стека.
- 🎨 [Chakra UI](https://chakra-ui.com) для компонентов фронтенда.
- 🤖 Автоматически сгенерированный фронтенд‑клиент.
- 🧪 [Playwright](https://playwright.dev) для End‑to‑End тестирования.
- 🦇 Поддержка тёмной темы.
- 🐋 [Docker Compose](https://www.docker.com) для разработки и продакшна.
- 🔒 Безопасное хэширование паролей по умолчанию.
- 🔑 Аутентификация по JWT‑токенам.
- 📫 Восстановление пароля по электронной почте.
- ✅ Тесты с [Pytest](https://pytest.org).
- 📞 [Traefik](https://traefik.io) в роли обратного прокси / балансировщика нагрузки.
- 🚢 Инструкции по развёртыванию с использованием Docker Compose, включая настройку фронтенд‑прокси Traefik для автоматического получения сертификатов HTTPS.
- 🏭 CI (continuous integration) и CD (continuous deployment) на основе GitHub Actions.

481
docs/ru/docs/python-types.md

@ -1,30 +1,29 @@
# Введение в аннотации типов Python
# Введение в типы Python { #python-types-intro }
Python имеет поддержку необязательных аннотаций типов.
Python поддерживает необязательные «подсказки типов» (их также называют «аннотациями типов»).
**Аннотации типов** являются специальным синтаксисом, который позволяет определять <abbr title="например: str, int, float, bool">тип</abbr> переменной.
Эти **«подсказки типов»** или аннотации — это специальный синтаксис, позволяющий объявлять <abbr title="например: str, int, float, bool">тип</abbr> переменной.
Объявление типов для переменных позволяет улучшить поддержку вашего кода редакторами и различными инструментами.
Объявляя типы для ваших переменных, редакторы кода и инструменты смогут лучше вас поддерживать.
Это просто **краткое руководство / напоминание** об аннотациях типов в Python. Оно охватывает только минимум, необходимый для их использования с **FastAPI**... что на самом деле очень мало.
Это всего лишь **краткое руководство / напоминание** о подсказках типов в Python. Оно охватывает только минимум, необходимый для их использования с **FastAPI**... что на самом деле очень мало.
**FastAPI** целиком основан на аннотациях типов, у них много выгод и преимуществ.
**FastAPI** целиком основан на этих подсказках типов — они дают ему множество преимуществ и выгод.
Но даже если вы никогда не используете **FastAPI**, вам будет полезно немного узнать о них.
/// note
/// note | Примечание
Если вы являетесь экспертом в Python и уже знаете всё об аннотациях типов, переходите к следующему разделу.
Если вы являетесь экспертом в Python и уже знаете всё о подсказках типов, переходите к следующей главе.
///
## Мотивация
## Мотивация { #motivation }
Давайте начнем с простого примера:
{* ../../docs_src/python_types/tutorial001.py *}
Вызов этой программы выводит:
```
@ -34,13 +33,12 @@ John Doe
Функция делает следующее:
* Принимает `first_name` и `last_name`.
* Преобразует первую букву содержимого каждой переменной в верхний регистр с `title()`.
* <abbr title="Объединяет в одно целое, последовательно, друг за другом.">Соединяет</abbr> их через пробел.
* Преобразует первую букву каждого значения в верхний регистр с помощью `title()`.
* <abbr title="Объединяет в одно целое. Содержимое одного — сразу после другого.">Соединяет</abbr> их пробелом посередине.
{* ../../docs_src/python_types/tutorial001.py hl[2] *}
### Отредактируем пример
### Отредактируем пример { #edit-it }
Это очень простая программа.
@ -48,23 +46,23 @@ John Doe
В какой-то момент вы бы начали определение функции, у вас были бы готовы параметры...
Но затем вы должны вызвать «тот метод, который преобразует первую букву в верхний регистр».
Но затем нужно вызвать «тот метод, который делает первую букву заглавной».
Было это `upper`? Или `uppercase`? `first_uppercase`? `capitalize`?
Это был `upper`? Или `uppercase`? `first_uppercase`? `capitalize`?
Тогда вы попробуете с давним другом программиста: автодополнением редактора.
Тогда вы пробуете старого друга программиста — автозавершение редактора кода.
Вы вводите первый параметр функции, `first_name`, затем точку (`.`), а затем нажимаете `Ctrl+Space`, чтобы запустить дополнение.
Вы вводите первый параметр функции, `first_name`, затем точку (`.`) и нажимаете `Ctrl+Space`, чтобы вызвать автозавершение.
Но, к сожалению, ничего полезного не выходит:
Но, к сожалению, ничего полезного не находится:
<img src="/img/python-types/image01.png">
### Добавим типы
### Добавим типы { #add-types }
Давайте изменим одну строчку в предыдущей версии.
Давайте изменим одну строку из предыдущей версии.
Мы изменим именно этот фрагмент, параметры функции, с:
Мы поменяем ровно этот фрагмент — параметры функции — с:
```Python
first_name, last_name
@ -76,14 +74,13 @@ John Doe
first_name: str, last_name: str
```
Вот и все.
Вот и всё.
Это аннотации типов:
Это и есть «подсказки типов»:
{* ../../docs_src/python_types/tutorial002.py hl[1] *}
Это не то же самое, что объявление значений по умолчанию, например:
Это не то же самое, что объявление значений по умолчанию, как, например:
```Python
first_name="john", last_name="doe"
@ -91,47 +88,45 @@ John Doe
Это другая вещь.
Мы используем двоеточия (`:`), а не равно (`=`).
Здесь мы используем двоеточия (`:`), а не знак равенства (`=`).
И добавление аннотаций типов обычно не меняет происходящего по сравнению с тем, что произошло бы без неё.
И добавление подсказок типов обычно не меняет поведение программы по сравнению с вариантом без них.
Но теперь представьте, что вы снова находитесь в процессе создания этой функции, но уже с аннотациями типов.
Но теперь представьте, что вы снова посередине написания этой функции, только уже с подсказками типов.
В тот же момент вы пытаетесь запустить автодополнение с помощью `Ctrl+Space` и вы видите:
В тот же момент вы пробуете вызвать автозавершение с помощью `Ctrl+Space` и видите:
<img src="/img/python-types/image02.png">
При этом вы можете просматривать варианты, пока не найдёте подходящий:
С этим вы можете прокручивать варианты, пока не найдёте тот самый:
<img src="/img/python-types/image03.png">
## Больше мотивации
## Больше мотивации { #more-motivation }
Проверьте эту функцию, она уже имеет аннотации типов:
Посмотрите на эту функцию — у неё уже есть подсказки типов:
{* ../../docs_src/python_types/tutorial003.py hl[1] *}
Поскольку редактор знает типы переменных, вы получаете не только дополнение, но и проверки ошибок:
Так как редактор кода знает типы переменных, вы получаете не только автозавершение, но и проверки ошибок:
<img src="/img/python-types/image04.png">
Теперь вы знаете, что вам нужно исправить, преобразовав `age` в строку с `str(age)`:
Теперь вы знаете, что нужно исправить — преобразовать `age` в строку с помощью `str(age)`:
{* ../../docs_src/python_types/tutorial004.py hl[2] *}
## Объявление типов { #declaring-types }
## Объявление типов
Вы только что видели основное место для объявления подсказок типов. В качестве параметров функции.
Вы только что увидели основное место, где объявляют подсказки типов — параметры функции.
Это также основное место, где вы можете использовать их с **FastAPI**.
Это также основное место, где вы будете использовать их с **FastAPI**.
### Простые типы
### Простые типы { #simple-types }
Вы можете объявить все стандартные типы Python, а не только `str`.
Вы можете объявлять все стандартные типы Python, не только `str`.
Вы можете использовать, к примеру:
Можно использовать, например:
* `int`
* `float`
@ -140,143 +135,361 @@ John Doe
{* ../../docs_src/python_types/tutorial005.py hl[1] *}
### Generic-типы с параметрами типов { #generic-types-with-type-parameters }
Есть структуры данных, которые могут содержать другие значения, например, `dict`, `list`, `set` и `tuple`. И внутренние значения тоже могут иметь свой тип.
Такие типы, которые содержат внутренние типы, называют «**generic**»-типами. И их можно объявлять, в том числе с указанием внутренних типов.
Чтобы объявлять эти типы и их внутренние типы, вы можете использовать стандартный модуль Python `typing`. Он существует специально для поддержки подсказок типов.
#### Новые версии Python { #newer-versions-of-python }
Синтаксис с использованием `typing` **совместим** со всеми версиями, от Python 3.6 до самых новых, включая Python 3.9, Python 3.10 и т.д.
### Generic-типы с параметрами типов
По мере развития Python **новые версии** получают улучшенную поддержку этих аннотаций типов, и во многих случаях вам даже не нужно импортировать и использовать модуль `typing`, чтобы объявлять аннотации типов.
Существуют некоторые структуры данных, которые могут содержать другие значения, например, `dict`, `list`, `set` и `tuple`. И внутренние значения тоже могут иметь свой тип.
Если вы можете выбрать более свежую версию Python для проекта, вы получите дополнительную простоту.
Чтобы объявить эти типы и внутренние типы, вы можете использовать стандартный Python-модуль `typing`.
Во всей документации есть примеры, совместимые с каждой версией Python (когда есть различия).
Он существует специально для поддержки подсказок этих типов.
Например, «**Python 3.6+**» означает совместимость с Python 3.6 и выше (включая 3.7, 3.8, 3.9, 3.10 и т.д.). А «**Python 3.9+**» — совместимость с Python 3.9 и выше (включая 3.10 и т.п.).
#### `List`
Если вы можете использовать **последние версии Python**, используйте примеры для самой новой версии — у них будет **самый лучший и простой синтаксис**, например, «**Python 3.10+**».
#### List { #list }
Например, давайте определим переменную как `list` из `str`.
//// tab | Python 3.9+
Объявите переменную с тем же синтаксисом двоеточия (`:`).
В качестве типа укажите `list`.
Так как список — это тип, содержащий внутренние типы, укажите их в квадратных скобках:
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial006_py39.py!}
```
Например, давайте определим переменную как `list`, состоящий из `str`.
////
Импортируйте `List` из `typing` (с заглавной `L`):
//// tab | Python 3.8+
{* ../../docs_src/python_types/tutorial006.py hl[1] *}
Из `typing` импортируйте `List` (с заглавной `L`):
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial006.py!}
```
Объявите переменную с тем же синтаксисом двоеточия (`:`).
В качестве типа укажите `List`.
В качестве типа используйте `List`, который вы импортировали из `typing`.
Поскольку список является типом, содержащим некоторые внутренние типы, вы помещаете их в квадратные скобки:
Так как список — это тип, содержащий внутренние типы, укажите их в квадратных скобках:
{* ../../docs_src/python_types/tutorial006.py hl[4] *}
```Python hl_lines="4"
{!> ../../docs_src/python_types/tutorial006.py!}
```
////
/// tip
/// info | Информация
Эти внутренние типы в квадратных скобках называются «параметрами типов».
В этом случае `str` является параметром типа, передаваемым в `List`.
В данном случае `str` — это параметр типа, передаваемый в `List` (или `list` в Python 3.9 и выше).
///
Это означает: "переменная `items` является `list`, и каждый из элементов этого списка является `str`".
Это означает: «переменная `items` — это `list`, и каждый элемент этого списка — `str`».
/// tip | Совет
Если вы используете Python 3.9 или выше, вам не нужно импортировать `List` из `typing`, можно использовать обычный встроенный тип `list`.
///
Если вы будете так поступать, редактор может оказывать поддержку даже при обработке элементов списка:
Таким образом, ваш редактор кода сможет помогать даже при обработке элементов списка:
<img src="/img/python-types/image05.png">
Без типов добиться этого практически невозможно.
Без типов добиться этого почти невозможно.
Обратите внимание, что переменная `item` — один из элементов списка `items`.
Обратите внимание, что переменная `item` является одним из элементов списка `items`.
И всё же редактор кода знает, что это `str`, и даёт соответствующую поддержку.
И все же редактор знает, что это `str`, и поддерживает это.
#### Tuple и Set { #tuple-and-set }
#### `Tuple` и `Set`
Аналогично вы бы объявили `tuple` и `set`:
Вы бы сделали то же самое, чтобы объявить `tuple` и `set`:
//// tab | Python 3.9+
{* ../../docs_src/python_types/tutorial007.py hl[1,4] *}
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial007_py39.py!}
```
////
//// tab | Python 3.8+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial007.py!}
```
////
Это означает:
* Переменная `items_t` является `tuple` с 3 элементами: `int`, другим `int` и `str`.
* Переменная `items_s` является `set` и каждый элемент имеет тип `bytes`.
* Переменная `items_t` — это `tuple` из 3 элементов: `int`, ещё один `int` и `str`.
* Переменная `items_s` — это `set`, и каждый элемент имеет тип `bytes`.
#### `Dict`
#### Dict { #dict }
Чтобы определить `dict`, вы передаёте 2 параметра типов, разделённых запятыми.
Чтобы определить `dict`, вы передаёте 2 параметра типов, разделённые запятой.
Первый параметр типа предназначен для ключей `dict`.
Первый параметр типа для ключей `dict`.
Второй параметр типа предназначен для значений `dict`:
Второй параметр типа для значений `dict`:
{* ../../docs_src/python_types/tutorial008.py hl[1,4] *}
//// tab | Python 3.9+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008_py39.py!}
```
////
//// tab | Python 3.8+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial008.py!}
```
////
Это означает:
* Переменная `prices` является `dict`:
* Ключи этого `dict` имеют тип `str` (скажем, название каждого элемента).
* Переменная `prices` — это `dict`:
* Ключи этого `dict` имеют тип `str` (скажем, название каждой позиции).
* Значения этого `dict` имеют тип `float` (скажем, цена каждой позиции).
#### `Optional`
#### Union { #union }
Вы можете объявить, что переменная может быть **одним из нескольких типов**, например, `int` или `str`.
В Python 3.6 и выше (включая Python 3.10) вы можете использовать тип `Union` из `typing` и перечислить в квадратных скобках все допустимые типы.
В Python 3.10 также появился **новый синтаксис**, где допустимые типы можно указать через <abbr title='также называется «побитовый оператор OR», но это значение здесь нерелевантно'>вертикальную черту (`|`)</abbr>.
//// tab | Python 3.10+
Вы также можете использовать `Optional`, чтобы объявить, что переменная имеет тип, например, `str`, но это является «необязательным», что означает, что она также может быть `None`:
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
////
//// tab | Python 3.8+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial008b.py!}
```
////
В обоих случаях это означает, что `item` может быть `int` или `str`.
#### Возможно `None` { #possibly-none }
Вы можете объявить, что значение может иметь определённый тип, например `str`, но также может быть и `None`.
В Python 3.6 и выше (включая Python 3.10) это можно объявить, импортировав и используя `Optional` из модуля `typing`.
```Python hl_lines="1 4"
{!../../docs_src/python_types/tutorial009.py!}
```
Использование `Optional[str]` вместо просто `str` позволит редактору помочь вам в обнаружении ошибок, в которых вы могли бы предположить, что значение всегда является `str`, хотя на самом деле это может быть и `None`.
Использование `Optional[str]` вместо просто `str` позволит редактору кода помочь вам обнаружить ошибки, когда вы предполагаете, что значение всегда `str`, хотя на самом деле оно может быть и `None`.
`Optional[Something]` — это на самом деле сокращение для `Union[Something, None]`, они эквивалентны.
Это также означает, что в Python 3.10 вы можете использовать `Something | None`:
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
//// tab | Python 3.8+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial009.py!}
```
////
//// tab | Python 3.8+ альтернативный вариант
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial009b.py!}
```
////
#### Использовать `Union` или `Optional` { #using-union-or-optional }
Если вы используете версию Python ниже 3.10, вот совет с моей весьма **субъективной** точки зрения:
* 🚨 Избегайте использования `Optional[SomeType]`
* Вместо этого ✨ **используйте `Union[SomeType, None]`** ✨.
Оба варианта эквивалентны и внутри одинаковы, но я бы рекомендовал `Union` вместо `Optional`, потому что слово «**optional**» («необязательный») может навести на мысль, что значение необязательное, хотя на самом деле оно означает «может быть `None`», даже если параметр не является необязательным и всё ещё обязателен.
Мне кажется, `Union[SomeType, None]` более явно выражает смысл.
Речь только о словах и названиях. Но эти слова могут влиять на то, как вы и ваши коллеги думаете о коде.
В качестве примера возьмём эту функцию:
{* ../../docs_src/python_types/tutorial009c.py hl[1,4] *}
Параметр `name` определён как `Optional[str]`, но он **не необязательный** — вы не можете вызвать функцию без этого параметра:
```Python
say_hi() # О нет, это вызывает ошибку! 😱
```
Параметр `name` всё ещё **обязателен** (не *optional*), потому что у него нет значения по умолчанию. При этом `name` принимает `None` как значение:
```Python
say_hi(name=None) # Это работает, None допустим 🎉
```
Хорошая новость: как только вы перейдёте на Python 3.10, об этом можно не переживать — вы сможете просто использовать `|` для объединения типов:
{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *}
И тогда вам не придётся задумываться о названиях вроде `Optional` и `Union`. 😎
#### Generic-типы { #generic-types }
Типы, которые принимают параметры типов в квадратных скобках, называются **Generic-типами** или **Generics**, например:
//// tab | Python 3.10+
Вы можете использовать те же встроенные типы как generics (с квадратными скобками и типами внутри):
* `list`
* `tuple`
* `set`
* `dict`
#### Generic-типы
И, как и в Python 3.8, из модуля `typing`:
Эти типы принимают параметры в квадратных скобках:
* `Union`
* `Optional` (так же, как в Python 3.8)
* ...и другие.
В Python 3.10, как альтернативу generics `Union` и `Optional`, можно использовать <abbr title='также называется «побитовый оператор OR», но это значение здесь нерелевантно'>вертикальную черту (`|`)</abbr> для объявления объединений типов — это гораздо лучше и проще.
////
//// tab | Python 3.9+
Вы можете использовать те же встроенные типы как generics (с квадратными скобками и типами внутри):
* `list`
* `tuple`
* `set`
* `dict`
И, как и в Python 3.8, из модуля `typing`:
* `Union`
* `Optional`
* ...и другие.
////
//// tab | Python 3.8+
* `List`
* `Tuple`
* `Set`
* `Dict`
* `Union`
* `Optional`
* ...и др.
* ...и другие.
называются **Generic-типами** или **Generics**.
////
### Классы как типы
### Классы как типы { #classes-as-types }
Вы также можете объявить класс как тип переменной.
Вы также можете объявлять класс как тип переменной.
Допустим, у вас есть класс `Person` с полем `name`:
Допустим, у вас есть класс `Person` с именем:
{* ../../docs_src/python_types/tutorial010.py hl[1:3] *}
Тогда вы можете объявить переменную типа `Person`:
{* ../../docs_src/python_types/tutorial010.py hl[6] *}
И снова вы получаете полную поддержку редактора:
И снова вы получите полную поддержку редактора кода:
<img src="/img/python-types/image06.png">
## Pydantic-модели
Обратите внимание, что это означает: «`one_person` — это **экземпляр** класса `Person`».
Это не означает: «`one_person` — это **класс** с именем `Person`».
<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> является Python-библиотекой для выполнения валидации данных.
## Pydantic-модели { #pydantic-models }
<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> — это библиотека Python для валидации данных.
Вы объявляете «форму» данных как классы с атрибутами.
И каждый атрибут имеет тип.
И у каждого атрибута есть тип.
Затем вы создаете экземпляр этого класса с некоторыми значениями, и он проверяет значения, преобразует их в соответствующий тип (если все верно) и предоставляет вам объект со всеми данными.
Затем вы создаёте экземпляр этого класса с некоторыми значениями — он провалидирует значения, преобразует их к соответствующему типу (если это применимо) и вернёт вам объект со всеми данными.
И вы получаете полную поддержку редактора для этого итогового объекта.
И вы получите полную поддержку редактора кода для этого результирующего объекта.
Взято из официальной документации Pydantic:
Пример из официальной документации Pydantic:
{* ../../docs_src/python_types/tutorial011.py *}
//// tab | Python 3.10+
```Python
{!> ../../docs_src/python_types/tutorial011_py310.py!}
```
////
/// info
//// tab | Python 3.9+
Чтобы узнать больше о <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic, читайте его документацию</a>.
```Python
{!> ../../docs_src/python_types/tutorial011_py39.py!}
```
////
//// tab | Python 3.8+
```Python
{!> ../../docs_src/python_types/tutorial011.py!}
```
////
/// info | Информация
Чтобы узнать больше о <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic, ознакомьтесь с его документацией</a>.
///
@ -284,30 +497,80 @@ John Doe
Вы увидите намного больше всего этого на практике в [Руководстве пользователя](tutorial/index.md){.internal-link target=_blank}.
## Аннотации типов в **FastAPI**
/// tip | Совет
У Pydantic есть особое поведение, когда вы используете `Optional` или `Union[Something, None]` без значения по умолчанию. Подробнее читайте в документации Pydantic: <a href="https://docs.pydantic.dev/2.3/usage/models/#required-fields" class="external-link" target="_blank">Required Optional fields</a>.
///
## Подсказки типов с аннотациями метаданных { #type-hints-with-metadata-annotations }
В Python также есть возможность добавлять **дополнительные <abbr title="Данные о данных, в данном случае — информация о типе, например описание.">метаданные</abbr>** к подсказкам типов с помощью `Annotated`.
//// tab | Python 3.9+
В Python 3.9 `Annotated` входит в стандартную библиотеку, поэтому вы можете импортировать его из `typing`.
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial013_py39.py!}
```
////
//// tab | Python 3.8+
В версиях ниже Python 3.9 импортируйте `Annotated` из `typing_extensions`.
Он уже будет установлен вместе с **FastAPI**.
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial013.py!}
```
////
Сам Python ничего не делает с `Annotated`. А для редакторов кода и других инструментов тип по-прежнему `str`.
Но вы можете использовать это место в `Annotated`, чтобы передать **FastAPI** дополнительные метаданные о том, как вы хотите, чтобы ваше приложение себя вело.
Важно помнить, что **первый параметр типа**, который вы передаёте в `Annotated`, — это **фактический тип**. Всё остальное — просто метаданные для других инструментов.
Пока вам достаточно знать, что `Annotated` существует и это — стандартный Python. 😎
Позже вы увидите, насколько это **мощно**.
/// tip | Совет
Тот факт, что это **стандартный Python**, означает, что вы по-прежнему получите **лучший возможный разработческий опыт** в вашем редакторе кода, с инструментами для анализа и рефакторинга кода и т.д. ✨
А ещё ваш код будет очень совместим со множеством других инструментов и библиотек Python. 🚀
///
## Аннотации типов в **FastAPI** { #type-hints-in-fastapi }
**FastAPI** получает преимущества аннотаций типов для выполнения определённых задач.
**FastAPI** использует эти подсказки типов для выполнения нескольких задач.
С **FastAPI** вы объявляете параметры с аннотациями типов и получаете:
С **FastAPI** вы объявляете параметры с подсказками типов и получаете:
* **Поддержку редактора**.
* **Поддержку редактора кода**.
* **Проверки типов**.
...и **FastAPI** использует тот же механизм для:
...и **FastAPI** использует эти же объявления для:
* **Определения требований**: из параметров пути запроса, параметров запроса, заголовков, зависимостей и т.д.
* **Преобразования данных**: от запроса к нужному типу.
* **Валидации данных**: исходя из каждого запроса:
* Генерации **автоматических ошибок**, возвращаемых клиенту, когда данные не являются корректными.
* **Определения требований**: из path-параметров, query-параметров, HTTP-заголовков, тел запросов, зависимостей и т.д.
* **Преобразования данных**: из HTTP-запроса к требуемому типу.
* **Валидации данных**: приходящих с каждого HTTP-запроса:
* Генерации **автоматических ошибок**, возвращаемых клиенту, когда данные некорректны.
* **Документирования** API с использованием OpenAPI:
* который затем используется пользовательскими интерфейсами автоматической интерактивной документации.
* что затем используется пользовательскими интерфейсами автоматической интерактивной документации.
Всё это может показаться абстрактным. Не волнуйтесь. Вы увидите всё это в действии в [Руководстве пользователя](tutorial/index.md){.internal-link target=_blank}.
Всё это может звучать абстрактно. Не волнуйтесь. Вы увидите всё это в действии в [Руководстве пользователя](tutorial/index.md){.internal-link target=_blank}.
Важно то, что при использовании стандартных типов Python в одном месте (вместо добавления дополнительных классов, декораторов и т.д.) **FastAPI** сделает за вас большую часть работы.
Важно то, что, используя стандартные типы Python в одном месте (вместо добавления дополнительных классов, декораторов и т.д.), **FastAPI** сделает за вас большую часть работы.
/// info
/// info | Информация
Если вы уже прошли всё руководство и вернулись, чтобы узнать больше о типах, хорошим ресурсом является <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">«шпаргалка» от `mypy`</a>.
Если вы уже прошли всё руководство и вернулись, чтобы узнать больше о типах, хорошим ресурсом будет <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">«шпаргалка» от `mypy`</a>.
///

80
docs/ru/docs/tutorial/background-tasks.md

@ -1,84 +1,84 @@
# Фоновые задачи
# Фоновые задачи { #background-tasks }
Вы можете создавать фоновые задачи, которые будут выполнятся *после* возвращения ответа сервером.
Вы можете создавать фоновые задачи, которые будут выполняться после возврата ответа.
Это может быть полезно для функций, которые должны выполниться после получения запроса, но ожидание их выполнения необязательно для пользователя.
Это полезно для операций, которые должны произойти после HTTP-запроса, но клиенту не обязательно ждать их завершения, чтобы получить ответ.
К примеру:
Например:
* Отправка писем на почту после выполнения каких-либо действий:
* Т.к. соединение с почтовым сервером и отправка письма идут достаточно "долго" (несколько секунд), вы можете отправить ответ пользователю, а отправку письма выполнить в фоне.
* Уведомления по электронной почте, отправляемые после выполнения действия:
* Так как подключение к почтовому серверу и отправка письма обычно «медленные» (несколько секунд), вы можете сразу вернуть ответ, а отправку уведомления выполнить в фоне.
* Обработка данных:
* К примеру, если вы получаете файл, который должен пройти через медленный процесс, вы можете отправить ответ "Accepted" (HTTP 202) и отправить работу с файлом в фон.
* Например, если вы получаете файл, который должен пройти через медленный процесс, вы можете вернуть ответ «Accepted» (HTTP 202) и обработать файл в фоне.
## Использование класса `BackgroundTasks`
## Использование `BackgroundTasks` { #using-backgroundtasks }
Сначала импортируйте `BackgroundTasks`, потом добавьте в функцию параметр с типом `BackgroundTasks`:
Сначала импортируйте `BackgroundTasks` и объявите параметр в вашей функции‑обработчике пути с типом `BackgroundTasks`:
{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
**FastAPI** создаст объект класса `BackgroundTasks` для вас и запишет его в параметр.
**FastAPI** создаст объект типа `BackgroundTasks` для вас и передаст его через этот параметр.
## Создание функции для фоновой задачи
## Создание функции для фоновой задачи { #create-a-task-function }
Создайте функцию, которую хотите запустить в фоне.
Создайте функцию, которую нужно запустить как фоновую задачу.
Это совершенно обычная функция, которая может принимать параметры.
Это обычная функция, которая может принимать параметры.
Она может быть как асинхронной `async def`, так и обычной `def` функцией, **FastAPI** знает, как правильно ее выполнить.
Это может быть как `async def`, так и обычная функция `def`, **FastAPI** знает, как корректно её выполнить.
В нашем примере фоновая задача будет вести запись в файл (симулируя отправку письма).
В этом случае функция задачи будет записывать данные в файл (имитируя отправку письма).
Так как операция записи не использует `async` и `await`, мы определим ее как обычную `def`:
Так как операция записи не использует `async` и `await`, мы определим функцию как обычную `def`:
{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
## Добавление фоновой задачи
## Добавление фоновой задачи { #add-the-background-task }
Внутри функции вызовите метод `.add_task()` у объекта *background tasks* и передайте ему функцию, которую хотите выполнить в фоне:
Внутри вашей функции‑обработчика пути передайте функцию задачи объекту фоновых задач методом `.add_task()`:
{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
`.add_task()` принимает следующие аргументы:
* Функцию, которая будет выполнена в фоне (`write_notification`). Обратите внимание, что передается объект функции, без скобок.
* Любое упорядоченное количество аргументов, которые принимает функция (`email`).
* Любое количество именованных аргументов, которые принимает функция (`message="some notification"`).
* Функцию задачи, которую нужно выполнить в фоне (`write_notification`).
* Последовательность позиционных аргументов, которые должны быть переданы функции задачи, в порядке (`email`).
* Любые именованные аргументы, которые должны быть переданы функции задачи (`message="some notification"`).
## Встраивание зависимостей
## Встраивание зависимостей { #dependency-injection }
Класс `BackgroundTasks` также работает с системой встраивания зависимостей, вы можете определить `BackgroundTasks` на разных уровнях: как параметр функции, как завимость, как подзависимость и так далее.
Использование `BackgroundTasks` также работает с системой встраивания зависимостей, вы можете объявить параметр типа `BackgroundTasks` на нескольких уровнях: в функции‑обработчике пути, в зависимости (dependable), в подзависимости и т. д.
**FastAPI** знает, что нужно сделать в каждом случае и как переиспользовать тот же объект `BackgroundTasks`, так чтобы все фоновые задачи собрались и запустились вместе в фоне:
**FastAPI** знает, что делать в каждом случае и как переиспользовать один и тот же объект, так чтобы все фоновые задачи были объединены и затем выполнены в фоне:
{* ../../docs_src/background_tasks/tutorial002_py310.py hl[11,13,20,23] *}
{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
В этом примере сообщения будут записаны в `log.txt` *после* того, как ответ сервера был отправлен.
В этом примере сообщения будут записаны в файл `log.txt` после отправки ответа.
Если бы в запрос был передан query-параметр `q`, он бы первыми записался в `log.txt` фоновой задачей (потому что вызывается в зависимости `get_query`).
Если в запросе была строка запроса (query), она будет записана в лог фоновой задачей.
После другая фоновая задача, которая была сгенерирована в функции, запишет сообщение из параметра `email`.
Затем другая фоновая задача, созданная в функции‑обработчике пути, запишет сообщение, используя path‑параметр `email`.
## Технические детали
## Технические детали { #technical-details }
Класс `BackgroundTasks` основан на <a href="https://www.starlette.io/background/" class="external-link" target="_blank">`starlette.background`</a>.
Класс `BackgroundTasks` приходит напрямую из <a href="https://www.starlette.io/background/" class="external-link" target="_blank">`starlette.background`</a>.
Он интегрирован в FastAPI, так что вы можете импортировать его прямо из `fastapi` и избежать случайного импорта `BackgroundTask` (без `s` на конце) из `starlette.background`.
Он импортируется/включается прямо в FastAPI, чтобы вы могли импортировать его из `fastapi` и избежать случайного импорта альтернативного `BackgroundTask` (без `s` на конце) из `starlette.background`.
При использовании `BackgroundTasks` (а не `BackgroundTask`), вам достаточно только определить параметр функции с типом `BackgroundTasks` и **FastAPI** сделает все за вас, также как при использовании объекта `Request`.
Используя только `BackgroundTasks` (а не `BackgroundTask`), его можно применять как параметр функции‑обработчика пути, и **FastAPI** сделает остальное за вас, как при использовании объекта `Request` напрямую.
Вы все равно можете использовать `BackgroundTask` из `starlette` в FastAPI, но вам придется самостоятельно создавать объект фоновой задачи и вручную обработать `Response` внутри него.
По‑прежнему можно использовать один `BackgroundTask` в FastAPI, но тогда вам нужно создать объект в своём коде и вернуть Starlette `Response`, включающий его.
Вы можете подробнее изучить его в <a href="https://www.starlette.io/background/" class="external-link" target="_blank">Официальной документации Starlette для BackgroundTasks</a>.
Подробнее см. в <a href="https://www.starlette.io/background/" class="external-link" target="_blank">официальной документации Starlette по фоновым задачам</a>.
## Предостережение
## Предостережение { #caveat }
Если вам нужно выполнить тяжелые вычисления в фоне, которым необязательно быть запущенными в одном процессе с приложением **FastAPI** (к примеру, вам не нужны обрабатываемые переменные или вы не хотите делиться памятью процесса и т.д.), вы можете использовать более серьезные инструменты, такие как <a href="https://docs.celeryproject.org" class="external-link" target="_blank">Celery</a>.
Если вам нужно выполнять тяжелые вычисления в фоне, и при этом они не обязательно должны запускаться тем же процессом (например, вам не нужно делиться памятью, переменными и т. п.), вам могут подойти более мощные инструменты, такие как <a href="https://docs.celeryq.dev" class="external-link" target="_blank">Celery</a>.
Их тяжелее настраивать, также им нужен брокер сообщений наподобие RabbitMQ или Redis, но зато они позволяют вам запускать фоновые задачи в нескольких процессах и даже на нескольких серверах.
Они обычно требуют более сложной конфигурации, менеджера очереди сообщений/заданий (например, RabbitMQ или Redis), но позволяют запускать фоновые задачи в нескольких процессах и, что особенно важно, на нескольких серверах.
Но если вам нужен доступ к общим переменным и объектам вашего **FastAPI** приложения или вам нужно выполнять простые фоновые задачи (наподобие отправки письма из примера) вы можете просто использовать `BackgroundTasks`.
Но если вам нужен доступ к переменным и объектам из того же приложения **FastAPI**, или нужно выполнять небольшие фоновые задачи (например, отправку email‑уведомления), вы можете просто использовать `BackgroundTasks`.
## Резюме
## Резюме { #recap }
Для создания фоновых задач вам необходимо импортировать `BackgroundTasks` и добавить его в функцию, как параметр с типом `BackgroundTasks`.
Импортируйте и используйте `BackgroundTasks` с параметрами в функциях‑обработчиках пути и зависимостях, чтобы добавлять фоновые задачи.

53
docs/ru/docs/tutorial/bigger-applications.md

@ -1,4 +1,4 @@
# Большие приложения, в которых много файлов
# Большие приложения, в которых много файлов { #bigger-applications-multiple-files }
При построении приложения или веб-API нам редко удается поместить всё в один файл.
@ -10,7 +10,7 @@
///
## Пример структуры приложения
## Пример структуры приложения { #an-example-file-structure }
Давайте предположим, что наше приложение имеет следующую структуру:
@ -71,7 +71,7 @@ from app.routers import items
│   └── admin.py # суб-модуль "admin", напр.: import app.internal.admin
```
## `APIRouter`
## `APIRouter` { #apirouter }
Давайте предположим, что для работы с пользователями используется отдельный файл (суб-модуль) `/app/routers/users.py`.
@ -81,8 +81,7 @@ from app.routers import items
С помощью `APIRouter` вы можете создать *операции пути* (*эндпоинты*) для данного модуля.
### Импорт `APIRouter`
### Импорт `APIRouter` { #import-apirouter }
Точно также, как и в случае с классом `FastAPI`, вам нужно импортировать и создать объект класса `APIRouter`.
@ -90,7 +89,7 @@ from app.routers import items
{!../../docs_src/bigger_applications/app/routers/users.py!}
```
### Создание *эндпоинтов* с помощью `APIRouter`
### Создание *эндпоинтов* с помощью `APIRouter` { #path-operations-with-apirouter }
В дальнейшем используйте `APIRouter` для объявления *эндпоинтов*, точно также, как вы используете класс `FastAPI`:
@ -112,7 +111,7 @@ from app.routers import items
Мы собираемся подключить данный `APIRouter` к нашему основному приложению на `FastAPI`, но сначала давайте проверим зависимости и создадим ещё один модуль с `APIRouter`.
## Зависимости
## Зависимости { #dependencies }
Нам понадобятся некоторые зависимости, которые мы будем использовать в разных местах нашего приложения.
@ -154,11 +153,11 @@ from app.routers import items
Для простоты мы воспользовались неким воображаемым заголовоком.
В реальных случаях для получения наилучших результатов используйте интегрированные утилиты обеспечения безопасности [Security utilities](security/index.md){.internal-link target=_blank}.
В реальных случаях для получения наилучших результатов используйте интегрированные [утилиты безопасности](security/index.md){.internal-link target=_blank}.
///
## Ещё один модуль с `APIRouter`
## Ещё один модуль с `APIRouter` { #another-module-with-apirouter }
Давайте также предположим, что у вас есть *эндпоинты*, отвечающие за обработку "items", и они находятся в модуле `app/routers/items.py`.
@ -203,7 +202,7 @@ async def read_item(item_id: str):
/// tip | Подсказка
Обратите внимание, что также, как и в случае с зависимостями в декораторах *эндпоинтов* ([dependencies in *path operation decorators*](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}), никакого значения в *функцию эндпоинта* передано не будет.
Обратите внимание, что также, как и в случае с зависимостями в декораторах *эндпоинтов* ([зависимости в декораторах операций пути](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}), никакого значения в *функцию эндпоинта* передано не будет.
///
@ -219,8 +218,8 @@ async def read_item(item_id: str):
* Каждый из них будет включать предопределенные ответы `responses`.
* Каждый *эндпоинт* будет иметь список зависимостей (`dependencies`), исполняемых перед вызовом *эндпоинта*.
* Если вы определили зависимости в самой операции пути, **то она также будет выполнена**.
* Сначала выполняются зависимости маршрутизатора, затем вызываются зависимости, определенные в декораторе *эндпоинта* ([`dependencies` in the decorator](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}), и, наконец, обычные параметрические зависимости.
* Вы также можете добавить зависимости безопасности с областями видимости (`scopes`) [`Security` dependencies with `scopes`](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}.
* Сначала выполняются зависимости маршрутизатора, затем вызываются [зависимости в декораторе](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, и, наконец, обычные параметрические зависимости.
* Вы также можете добавить [зависимости `Security` с `scopes`](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}.
/// tip | Подсказка
@ -234,7 +233,7 @@ async def read_item(item_id: str):
///
### Импорт зависимостей
### Импорт зависимостей { #import-the-dependencies }
Наш код находится в модуле `app.routers.items` (файл `app/routers/items.py`).
@ -246,7 +245,7 @@ async def read_item(item_id: str):
{!../../docs_src/bigger_applications/app/routers/items.py!}
```
#### Как работает относительный импорт?
#### Как работает относительный импорт? { #how-relative-imports-work }
/// tip | Подсказка
@ -308,7 +307,7 @@ from ...dependencies import get_token_header
Теперь вы знаете, как работает импорт в Python, и сможете использовать относительное импортирование в своих собственных приложениях любого уровня сложности. 🤓
### Добавление пользовательских тегов (`tags`), ответов (`responses`) и зависимостей (`dependencies`)
### Добавление пользовательских тегов (`tags`), ответов (`responses`) и зависимостей (`dependencies`) { #add-some-custom-tags-responses-and-dependencies }
Мы не будем добавлять префикс `/items` и список тегов `tags=["items"]` для каждого *эндпоинта*, т.к. мы уже их добавили с помощью `APIRouter`.
@ -326,7 +325,7 @@ from ...dependencies import get_token_header
///
## Модуль main в `FastAPI`
## Модуль main в `FastAPI` { #the-main-fastapi }
Теперь давайте посмотрим на модуль `app/main.py`.
@ -336,17 +335,17 @@ from ...dependencies import get_token_header
И теперь, когда большая часть логики приложения разделена на отдельные модули, основной файл `app/main.py` будет достаточно простым.
### Импорт `FastAPI`
### Импорт `FastAPI` { #import-fastapi }
Вы импортируете и создаете класс `FastAPI` как обычно.
Мы даже можем объявить глобальные зависимости [global dependencies](dependencies/global-dependencies.md){.internal-link target=_blank}, которые будут объединены с зависимостями для каждого отдельного маршрутизатора:
Мы даже можем объявить [глобальные зависимости](dependencies/global-dependencies.md){.internal-link target=_blank}, которые будут объединены с зависимостями для каждого отдельного маршрутизатора:
```Python hl_lines="1 3 7" title="app/main.py"
{!../../docs_src/bigger_applications/app/main.py!}
```
### Импорт `APIRouter`
### Импорт `APIRouter` { #import-the-apirouter }
Теперь мы импортируем другие суб-модули, содержащие `APIRouter`:
@ -356,7 +355,7 @@ from ...dependencies import get_token_header
Так как файлы `app/routers/users.py` и `app/routers/items.py` являются суб-модулями одного и того же Python-пакета `app`, то мы сможем их импортировать, воспользовавшись операцией относительного импорта `.`.
### Как работает импорт?
### Как работает импорт? { #how-the-importing-works }
Данная строка кода:
@ -398,7 +397,7 @@ from app.routers import items, users
///
### Избегайте конфликтов имен
### Избегайте конфликтов имен { #avoid-name-collisions }
Вместо того чтобы импортировать только переменную `router`, мы импортируем непосредственно суб-модуль `items`.
@ -419,7 +418,7 @@ from .routers.users import router
{!../../docs_src/bigger_applications/app/main.py!}
```
### Подключение маршрутизаторов (`APIRouter`) для `users` и для `items`
### Подключение маршрутизаторов (`APIRouter`) для `users` и для `items` { #include-the-apirouters-for-users-and-items }
Давайте подключим маршрутизаторы (`router`) из суб-модулей `users` и `items`:
@ -457,7 +456,7 @@ from .routers.users import router
///
### Подключение `APIRouter` с пользовательскими префиксом (`prefix`), тегами (`tags`), ответами (`responses`), и зависимостями (`dependencies`)
### Подключение `APIRouter` с пользовательскими префиксом (`prefix`), тегами (`tags`), ответами (`responses`), и зависимостями (`dependencies`) { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies }
Теперь давайте представим, что ваша организация передала вам файл `app/internal/admin.py`.
@ -491,7 +490,7 @@ from .routers.users import router
Например, другие проекты, могут использовать тот же самый `APIRouter` с другими методами аутентификации.
### Подключение отдельного *эндпоинта*
### Подключение отдельного *эндпоинта* { #include-a-path-operation }
Мы также можем добавить *эндпоинт* непосредственно в основное приложение `FastAPI`.
@ -517,7 +516,7 @@ from .routers.users import router
///
## Проверка автоматической документации API
## Проверка автоматической документации API { #check-the-automatic-api-docs }
Теперь запустите приложение:
@ -537,7 +536,7 @@ $ fastapi dev app/main.py
<img src="/img/tutorial/bigger-applications/image01.png">
## Подключение существующего маршрута через новый префикс (`prefix`)
## Подключение существующего маршрута через новый префикс (`prefix`) { #include-the-same-router-multiple-times-with-different-prefix }
Вы можете использовать `.include_router()` несколько раз с одним и тем же маршрутом, применив различные префиксы.
@ -545,7 +544,7 @@ $ fastapi dev app/main.py
Это продвинутый способ, который вам может и не пригодится. Мы приводим его на случай, если вдруг вам это понадобится.
## Включение одного маршрутизатора (`APIRouter`) в другой
## Включение одного маршрутизатора (`APIRouter`) в другой { #include-an-apirouter-in-another }
Точно так же, как вы включаете `APIRouter` в приложение `FastAPI`, вы можете включить `APIRouter` в другой `APIRouter`:

21
docs/ru/docs/tutorial/body-fields.md

@ -1,12 +1,12 @@
# Body - Поля
# Body - Поля { #body-fields }
Таким же способом, как вы объявляете дополнительную валидацию и метаданные в параметрах *функции обработки пути* с помощью функций `Query`, `Path` и `Body`, вы можете объявлять валидацию и метаданные внутри Pydantic моделей, используя функцию `Field` из Pydantic.
## Импорт `Field`
## Импорт `Field` { #import-field }
Сначала вы должны импортировать его:
{* ../../docs_src/body_fields/tutorial001_py310.py hl[2] *}
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *}
/// warning | Внимание
@ -14,11 +14,11 @@
///
## Объявление атрибутов модели
## Объявление атрибутов модели { #declare-model-attributes }
Вы можете использовать функцию `Field` с атрибутами модели:
{* ../../docs_src/body_fields/tutorial001_py310.py hl[9:12] *}
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *}
Функция `Field` работает так же, как `Query`, `Path` и `Body`, у неё такие же параметры и т.д.
@ -26,9 +26,9 @@
На самом деле, `Query`, `Path` и другие функции, которые вы увидите в дальнейшем, создают объекты подклассов общего класса `Param`, который сам по себе является подклассом `FieldInfo` из Pydantic.
И `Field` (из Pydantic), и `Body`, оба возвращают объекты подкласса `FieldInfo`.
И `Field` (из Pydantic) также возвращает экземпляр `FieldInfo`.
У класса `Body` есть и другие подклассы, с которыми вы ознакомитесь позже.
`Body` также напрямую возвращает объекты подкласса `FieldInfo`. И есть и другие, с которыми вы познакомитесь позже, которые являются подклассами класса `Body`.
Помните, что когда вы импортируете `Query`, `Path` и другое из `fastapi`, это фактически функции, которые возвращают специальные классы.
@ -40,12 +40,11 @@
///
## Добавление дополнительной информации
## Добавление дополнительной информации { #add-extra-information }
Вы можете объявлять дополнительную информацию в `Field`, `Query`, `Body` и т.п. Она будет включена в сгенерированную JSON схему.
Вы узнаете больше о добавлении дополнительной информации позже в документации, когда будете изучать, как задавать примеры принимаемых данных.
Вы узнаете больше о добавлении дополнительной информации позже в документации, когда будете изучать, как задавать примеры.
/// warning | Внимание
@ -54,7 +53,7 @@
///
## Резюме
## Резюме { #recap }
Вы можете использовать функцию `Field` из Pydantic, чтобы задавать дополнительную валидацию и метаданные для атрибутов модели.

32
docs/ru/docs/tutorial/body-multiple-params.md

@ -1,8 +1,8 @@
# Body - Множество параметров
# Body - Множество параметров { #body-multiple-parameters }
Теперь, когда мы увидели, как использовать `Path` и `Query` параметры, давайте рассмотрим более продвинутые примеры обьявления тела запроса.
Теперь, когда мы увидели, как использовать `Path` и `Query` параметры, давайте рассмотрим более продвинутые примеры объявления тела запроса.
## Обьединение `Path`, `Query` и параметров тела запроса
## Объединение `Path`, `Query` и параметров тела запроса { #mix-path-query-and-body-parameters }
Во-первых, конечно, вы можете объединять параметры `Path`, `Query` и объявления тела запроса в своих функциях обработки, **FastAPI** автоматически определит, что с ними нужно делать.
@ -16,9 +16,9 @@
///
## Несколько параметров тела запроса
## Несколько параметров тела запроса { #multiple-body-parameters }
В предыдущем примере, *операции пути* ожидали тело запроса в формате JSON-тело с параметрами, соответствующими атрибутам `Item`, например:
В предыдущем примере, *операции пути* ожидали тело запроса в формате JSON, с параметрами, соответствующими атрибутам `Item`, например:
```JSON
{
@ -33,7 +33,7 @@
{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
В этом случае **FastAPI** заметит, что в функции есть более одного параметра тела (два параметра, которые являются моделями Pydantic).
В этом случае **FastAPI** заметит, что в функции есть более одного параметра тела (два параметра, которые являются Pydantic-моделями).
Таким образом, имена параметров будут использоваться в качестве ключей (имён полей) в теле запроса, и будет ожидаться запрос следующего формата:
@ -54,21 +54,21 @@
/// note | Внимание
Обратите внимание, что хотя параметр `item` был объявлен таким же способом, как и раньше, теперь предпологается, что он находится внутри тела с ключом `item`.
Обратите внимание, что хотя параметр `item` был объявлен таким же способом, как и раньше, теперь предполагается, что он находится внутри тела с ключом `item`.
///
**FastAPI** сделает автоматические преобразование из запроса, так что параметр `item` получит своё конкретное содержимое, и то же самое происходит с пользователем `user`.
**FastAPI** сделает автоматическое преобразование из запроса, так что параметр `item` получит своё конкретное содержимое, и то же самое происходит с пользователем `user`.
Произойдёт проверка составных данных, и создание документации в схеме OpenAPI и автоматических документах.
## Отдельные значения в теле запроса
## Отдельные значения в теле запроса { #singular-values-in-body }
Точно так же, как `Query` и `Path` используются для определения дополнительных данных для query и path параметров, **FastAPI** предоставляет аналогичный инструмент - `Body`.
Например, расширяя предыдущую модель, вы можете решить, что вам нужен еще один ключ `importance` в том же теле запроса, помимо параметров `item` и `user`.
Если вы объявите его без указания, какой именно объект (Path, Query, Body и .т.п.) ожидаете, то, поскольку это является простым типом данных, **FastAPI** будет считать, что это query-параметр.
Если вы объявите его без указания, какой именно объект (Path, Query, Body и т.п.) ожидаете, то, поскольку это является простым типом данных, **FastAPI** будет считать, что это query-параметр.
Но вы можете указать **FastAPI** обрабатывать его, как ещё один ключ тела запроса, используя `Body`:
@ -94,7 +94,7 @@
И всё будет работать так же - преобразование типов данных, валидация, документирование и т.д.
## Множество body и query параметров
## Множество body и query параметров { #multiple-body-params-and-query }
Конечно, вы также можете объявлять query-параметры в любое время, дополнительно к любым body-параметрам.
@ -112,7 +112,7 @@ q: str | None = None
Например:
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[27] *}
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
/// info | Информация
@ -120,9 +120,9 @@ q: str | None = None
///
## Добавление одного body-параметра
## Вложить один body-параметр { #embed-a-single-body-parameter }
Предположим, у вас есть только один body-параметр `item` из Pydantic модели `Item`.
Предположим, у вас есть только один body-параметр `item` из Pydantic-модели `Item`.
По умолчанию, **FastAPI** ожидает получить тело запроса напрямую.
@ -160,9 +160,9 @@ item: Item = Body(embed=True)
}
```
## Резюме
## Резюме { #recap }
Вы можете добавлять несколько body-параметров вашей *функции операции пути*, несмотря даже на то, что запрос может содержать только одно тело.
Вы можете добавлять несколько body-параметров вашей *функции-обработчика пути*, несмотря даже на то, что запрос может содержать только одно тело.
Но **FastAPI** справится с этим, предоставит правильные данные в вашей функции, а также сделает валидацию и документацию правильной схемы *операции пути*.

100
docs/ru/docs/tutorial/body-nested-models.md

@ -1,41 +1,41 @@
# Body - Вложенные модели
# Body - Вложенные модели { #body-nested-models }
С помощью **FastAPI**, вы можете определять, валидировать, документировать и использовать модели произвольной вложенности (благодаря библиотеке Pydantic).
С помощью **FastAPI** вы можете определять, валидировать, документировать и использовать модели произвольной глубины вложенности (благодаря Pydantic).
## Определение полей содержащих списки
## Поля-списки { #list-fields }
Вы можете определять атрибут как подтип. Например, тип `list` в Python:
Вы можете определить атрибут как подтип. Например, Python-тип `list`:
{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
Это приведёт к тому, что обьект `tags` преобразуется в список, несмотря на то что тип его элементов не объявлен.
Это приведёт к тому, что `tags` будет списком, несмотря на то, что тип его элементов не объявлен.
## Определение полей содержащих список с определением типов его элементов
## Поля-списки с параметром типа { #list-fields-with-type-parameter }
Однако в Python есть способ объявления списков с указанием типов для вложенных элементов:
В Python есть специальный способ объявлять списки с внутренними типами, или «параметрами типа»:
### Импортируйте `List` из модуля typing
### Импортируйте `List` из модуля typing { #import-typings-list }
В Python 3.9 и выше вы можете использовать стандартный тип `list` для объявления аннотаций типов, как мы увидим ниже. 💡
Но в версиях Python до 3.9 (начиная с 3.6) сначала вам необходимо импортировать `List` из стандартного модуля `typing` в Python:
Но в версиях Python до 3.9 (начиная с 3.6) сначала вам необходимо импортировать `List` из стандартного модуля `typing`:
{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
### Объявление `list` с указанием типов для вложенных элементов
### Объявите `list` с параметром типа { #declare-a-list-with-a-type-parameter }
Объявление типов для элементов (внутренних типов) вложенных в такие типы как `list`, `dict`, `tuple`:
Для объявления типов, у которых есть параметры типа (внутренние типы), таких как `list`, `dict`, `tuple`:
* Если у вас Python версии ниже чем 3.9, импортируйте их аналог из модуля `typing`
* Передайте внутренний(ие) тип(ы) как "параметры типа", используя квадратные скобки: `[` и `]`
* Если у вас Python версии ниже 3.9, импортируйте их аналоги из модуля `typing`
* Передайте внутренний(ие) тип(ы) как «параметры типа», используя квадратные скобки: `[` и `]`
В Python версии 3.9 это будет выглядеть так:
В Python 3.9 это будет:
```Python
my_list: list[str]
```
В версиях Python до 3.9 это будет выглядеть так:
В версиях Python до 3.9 это будет:
```Python
from typing import List
@ -47,17 +47,17 @@ my_list: List[str]
Используйте этот же стандартный синтаксис для атрибутов модели с внутренними типами.
Таким образом, в нашем примере мы можем явно указать тип данных для поля `tags` как "список строк":
Таким образом, в нашем примере мы можем явно указать тип данных для поля `tags` как «список строк»:
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
## Типы множеств
## Типы множеств { #set-types }
Но затем мы подумали и поняли, что теги не должны повторяться и, вероятно, они должны быть уникальными строками.
Но затем мы подумали и поняли, что теги не должны повторяться, вероятно, это должны быть уникальные строки.
И в Python есть специальный тип данных для множеств уникальных элементов - `set`.
И в Python есть специальный тип данных для множеств уникальных элементов `set`.
Тогда мы можем обьявить поле `tags` как множество строк:
Тогда мы можем объявить поле `tags` как множество строк:
{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *}
@ -67,23 +67,23 @@ my_list: List[str]
И они также будут соответствующим образом аннотированы / задокументированы.
## Вложенные Модели
## Вложенные модели { #nested-models }
У каждого атрибута Pydantic-модели есть тип.
Но этот тип может сам быть другой моделью Pydantic.
Но этот тип сам может быть другой моделью Pydantic.
Таким образом вы можете объявлять глубоко вложенные JSON "объекты" с определёнными именами атрибутов, типами и валидацией.
Таким образом, вы можете объявлять глубоко вложенные JSON «объекты» с определёнными именами атрибутов, типами и валидацией.
Всё это может быть произвольно вложенным.
### Определение подмодели
### Определение подмодели { #define-a-submodel }
Например, мы можем определить модель `Image`:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
### Использование вложенной модели в качестве типа
### Использование подмодели как типа { #use-the-submodel-as-a-type }
Также мы можем использовать эту модель как тип атрибута:
@ -107,30 +107,30 @@ my_list: List[str]
Ещё раз: сделав такое объявление, с помощью **FastAPI** вы получите:
* Поддержку редакторов IDE (автодополнение и т.д), даже для вложенных моделей
* Поддержку редактора кода (автозавершение и т. д.), даже для вложенных моделей
* Преобразование данных
* Валидацию данных
* Автоматическую документацию
## Особые типы и валидация
## Особые типы и валидация { #special-types-and-validation }
Помимо обычных простых типов, таких как `str`, `int`, `float`, и т.д. Вы можете использовать более сложные базовые типы, которые наследуются от типа `str`.
Помимо обычных простых типов, таких как `str`, `int`, `float` и т.д., вы можете использовать более сложные простые типы, которые наследуются от `str`.
Чтобы увидеть все варианты, которые у вас есть, ознакомьтесь с документацией <a href="https://docs.pydantic.dev/latest/concepts/types/" class="external-link" target="_blank">по необычным типам Pydantic</a>. Вы увидите некоторые примеры в следующей главе.
Чтобы увидеть все варианты, которые у вас есть, ознакомьтесь с <a href="https://docs.pydantic.dev/latest/concepts/types/" class="external-link" target="_blank">обзором типов Pydantic</a>. Вы увидите некоторые примеры в следующей главе.
Например, так как в модели `Image` у нас есть поле `url`, то мы можем объявить его как тип `HttpUrl` из модуля Pydantic вместо типа `str`:
Например, так как в модели `Image` у нас есть поле `url`, то мы можем объявить его как тип `HttpUrl` из Pydantic вместо типа `str`:
{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
Строка будет проверена на соответствие допустимому URL-адресу и задокументирована в JSON схему / OpenAPI.
Строка будет проверена на соответствие допустимому URL-адресу и задокументирована в JSON Schema / OpenAPI как таковая.
## Атрибуты, содержащие списки подмоделей
## Атрибуты, содержащие списки подмоделей { #attributes-with-lists-of-submodels }
Вы также можете использовать модели Pydantic в качестве типов вложенных в `list`, `set` и т.д:
Вы также можете использовать модели Pydantic в качестве подтипов для `list`, `set` и т.д.:
{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
Такая реализация будет ожидать (конвертировать, валидировать, документировать и т.д) JSON-содержимое в следующем формате:
Такая реализация будет ожидать (конвертировать, валидировать, документировать и т.д.) JSON-содержимое в следующем формате:
```JSON hl_lines="11"
{
@ -162,7 +162,7 @@ my_list: List[str]
///
## Глубоко вложенные модели
## Глубоко вложенные модели { #deeply-nested-models }
Вы можете определять модели с произвольным уровнем вложенности:
@ -174,15 +174,15 @@ my_list: List[str]
///
## Тела с чистыми списками элементов
## Тела с чистыми списками элементов { #bodies-of-pure-lists }
Если верхний уровень значения тела JSON-объекта представляет собой JSON `array` (в Python - `list`), вы можете объявить тип в параметре функции, так же, как в моделях Pydantic:
Если верхний уровень значения тела JSON-объекта представляет собой JSON `array` (в Python `list`), вы можете объявить тип в параметре функции, так же как в моделях Pydantic:
```Python
images: List[Image]
```
в Python 3.9 и выше:
или в Python 3.9 и выше:
```Python
images: list[Image]
@ -192,33 +192,33 @@ images: list[Image]
{* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *}
## Универсальная поддержка редактора
## Поддержка редактора кода везде { #editor-support-everywhere }
И вы получаете поддержку редактора везде.
И вы получаете поддержку редактора кода везде.
Даже для элементов внутри списков:
<img src="/img/tutorial/body-nested-models/image01.png">
Вы не могли бы получить такую поддержку редактора, если бы работали напрямую с `dict`, а не с моделями Pydantic.
Вы не могли бы получить такую поддержку редактора кода, если бы работали напрямую с `dict`, а не с моделями Pydantic.
Но вы также не должны беспокоиться об этом, входящие словари автоматически конвертируются, а ваш вывод также автоматически преобразуется в формат JSON.
## Тела запросов с произвольными словарями (`dict` )
## Тела запросов с произвольными словарями (`dict`) { #bodies-of-arbitrary-dicts }
Вы также можете объявить тело запроса как `dict` с ключами определенного типа и значениями другого типа данных.
Вы также можете объявить тело запроса как `dict` с ключами определённого типа и значениями другого типа.
Без необходимости знать заранее, какие значения являются допустимыми для имён полей/атрибутов (как это было бы в случае с моделями Pydantic).
Это было бы полезно, если вы хотите получить ключи, которые вы еще не знаете.
Это было бы полезно, если вы хотите получить ключи, которые вы ещё не знаете.
---
Другой полезный случай - когда вы хотите чтобы ключи были другого типа данных, например, `int`.
Другой полезный случай — когда вы хотите, чтобы ключи были другого типа данных, например, `int`.
Именно это мы сейчас и увидим здесь.
В этом случае вы принимаете `dict`, пока у него есть ключи типа `int` со значениями типа `float`:
В этом случае вы принимаете любой `dict`, пока у него есть ключи типа `int` со значениями типа `float`:
{* ../../docs_src/body_nested_models/tutorial009_py39.py hl[7] *}
@ -228,19 +228,19 @@ images: list[Image]
Но Pydantic обеспечивает автоматическое преобразование данных.
Это значит, что даже если пользователи вашего API могут отправлять только строки в качестве ключей, при условии, что эти строки содержат целые числа, Pydantic автоматический преобразует и валидирует эти данные.
Это значит, что даже если клиенты вашего API могут отправлять только строки в качестве ключей, при условии, что эти строки содержат целые числа, Pydantic автоматически преобразует и валидирует эти данные.
А `dict`, с именем `weights`, который вы получите в качестве ответа Pydantic, действительно будет иметь ключи типа `int` и значения типа `float`.
А `dict`, который вы получите как `weights`, действительно будет иметь ключи типа `int` и значения типа `float`.
///
## Резюме
## Резюме { #recap }
С помощью **FastAPI** вы получаете максимальную гибкость, предоставляемую моделями Pydantic, сохраняя при этом простоту, краткость и элегантность вашего кода.
И дополнительно вы получаете:
* Поддержку редактора (автодополнение доступно везде!)
* Поддержку редактора кода (автозавершение доступно везде!)
* Преобразование данных (также известно как парсинг / сериализация)
* Валидацию данных
* Документацию схемы данных

42
docs/ru/docs/tutorial/body-updates.md

@ -1,16 +1,16 @@
# Body - Обновления
# Body - Обновления { #body-updates }
## Полное обновление с помощью `PUT`
## Обновление с заменой при помощи `PUT` { #update-replacing-with-put }
Для полного обновления элемента можно воспользоваться операцией <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT" class="external-link" target="_blank">HTTP `PUT`</a>.
Вы можете использовать функцию `jsonable_encoder` для преобразования входных данных в JSON, так как нередки случаи, когда работать можно только с простыми типами данных (например, для хранения в NoSQL-базе данных).
Вы можете использовать `jsonable_encoder`, чтобы преобразовать входные данные в данные, которые можно сохранить как JSON (например, в NoSQL-базе данных). Например, преобразование `datetime` в `str`.
{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *}
`PUT` используется для получения данных, которые должны полностью заменить существующие данные.
### Предупреждение о замене
### Предупреждение о замене { #warning-about-replacing }
Это означает, что если вы хотите обновить элемент `bar`, используя `PUT` с телом, содержащим:
@ -26,7 +26,7 @@
И данные будут сохранены с этим "новым" `tax`, равным `10,5`.
## Частичное обновление с помощью `PATCH`
## Частичное обновление с помощью `PATCH` { #partial-updates-with-patch }
Также можно использовать <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH" class="external-link" target="_blank">HTTP `PATCH`</a> операцию для *частичного* обновления данных.
@ -44,25 +44,41 @@
///
### Использование параметра `exclude_unset` в Pydantic
### Использование параметра `exclude_unset` в Pydantic { #using-pydantics-exclude-unset-parameter }
Если необходимо выполнить частичное обновление, то очень полезно использовать параметр `exclude_unset` в методе `.dict()` модели Pydantic.
Если необходимо выполнить частичное обновление, то очень полезно использовать параметр `exclude_unset` в методе `.model_dump()` модели Pydantic.
Например, `item.dict(exclude_unset=True)`.
Например, `item.model_dump(exclude_unset=True)`.
/// info | Информация
В Pydantic v1 метод назывался `.dict()`, в Pydantic v2 он помечен как устаревший (но все еще поддерживается) и переименован в `.model_dump()`.
Примеры здесь используют `.dict()` для совместимости с Pydantic v1, но если вы можете использовать Pydantic v2, лучше используйте `.model_dump()`.
///
В результате будет сгенерирован словарь, содержащий только те данные, которые были заданы при создании модели `item`, без учета значений по умолчанию. Затем вы можете использовать это для создания словаря только с теми данными, которые были установлены (отправлены в запросе), опуская значения по умолчанию:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
### Использование параметра `update` в Pydantic
### Использование параметра `update` в Pydantic { #using-pydantics-update-parameter }
Теперь можно создать копию существующей модели, используя `.copy()`, и передать параметр `update` с `dict`, содержащим данные для обновления.
Теперь можно создать копию существующей модели, используя `.model_copy()`, и передать параметр `update` с `dict`, содержащим данные для обновления.
/// info | Информация
В Pydantic v1 метод назывался `.copy()`, в Pydantic v2 он помечен как устаревший (но все еще поддерживается) и переименован в `.model_copy()`.
Примеры здесь используют `.copy()` для совместимости с Pydantic v1, но если вы можете использовать Pydantic v2, лучше используйте `.model_copy()`.
///
Например, `stored_item_model.copy(update=update_data)`:
Например, `stored_item_model.model_copy(update=update_data)`:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
### Кратко о частичном обновлении
### Кратко о частичном обновлении { #partial-updates-recap }
В целом, для применения частичных обновлений необходимо:
@ -73,7 +89,7 @@
* Таким образом, можно обновлять только те значения, которые действительно установлены пользователем, вместо того чтобы переопределять значения, уже сохраненные в модели по умолчанию.
* Создать копию хранимой модели, обновив ее атрибуты полученными частичными обновлениями (с помощью параметра `update`).
* Преобразовать скопированную модель в то, что может быть сохранено в вашей БД (например, с помощью `jsonable_encoder`).
* Это сравнимо с повторным использованием метода модели `.dict()`, но при этом происходит проверка (и преобразование) значений в типы данных, которые могут быть преобразованы в JSON, например, `datetime` в `str`.
* Это сравнимо с повторным использованием метода модели `.model_dump()`, но при этом происходит проверка (и преобразование) значений в типы данных, которые могут быть преобразованы в JSON, например, `datetime` в `str`.
* Сохранить данные в своей БД.
* Вернуть обновленную модель.

148
docs/ru/docs/tutorial/body.md

@ -1,40 +1,40 @@
# Тело запроса
# Тело запроса { #request-body }
Когда вам необходимо отправить данные из клиента (допустим, браузера) в ваш API, вы отправляете их как **тело запроса**.
Когда вам необходимо отправить данные из клиента (например, браузера) в ваш API, вы отправляете их как **тело запроса**.
Тело **запроса** --- это данные, отправляемые клиентом в ваш API. Тело **ответа** --- это данные, которые ваш API отправляет клиенту.
Тело **запроса** это данные, отправляемые клиентом в ваш API. Тело **ответа** это данные, которые ваш API отправляет клиенту.
Ваш API почти всегда отправляет тело **ответа**. Но клиентам не обязательно всегда отправлять тело **запроса**.
Ваш API почти всегда должен отправлять тело **ответа**. Но клиентам не обязательно всегда отправлять **тело запроса**: иногда они запрашивают только путь, возможно с некоторыми параметрами запроса, но без тела.
Чтобы объявить тело **запроса**, необходимо использовать модели <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>, со всей их мощью и преимуществами.
Чтобы объявить тело **запроса**, используйте модели <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>, со всей их мощью и преимуществами.
/// info | Информация
Чтобы отправить данные, необходимо использовать один из методов: `POST` (обычно), `PUT`, `DELETE` или `PATCH`.
Чтобы отправить данные, используйте один из методов: `POST` (чаще всего), `PUT`, `DELETE` или `PATCH`.
Отправка тела с запросом `GET` имеет неопределенное поведение в спецификациях, тем не менее, оно поддерживается FastAPI только для очень сложных/экстремальных случаев использования.
Отправка тела с запросом `GET` имеет неопределённое поведение в спецификациях, тем не менее это поддерживается FastAPI, но только для очень сложных/крайних случаев использования.
Поскольку это не рекомендуется, интерактивная документация со Swagger UI не будет отображать информацию для тела при использовании метода GET, а промежуточные прокси-серверы могут не поддерживать такой вариант запроса.
Поскольку это не рекомендуется, интерактивная документация со Swagger UI не будет отображать информацию для тела при использовании `GET`, а промежуточные прокси-серверы могут не поддерживать такой вариант запроса.
///
## Импортирование `BaseModel` из Pydantic
## Импортируйте `BaseModel` из Pydantic { #import-pydantics-basemodel }
Первое, что вам необходимо сделать, это импортировать `BaseModel` из пакета `pydantic`:
Первое, что нужно сделать, — импортировать `BaseModel` из пакета `pydantic`:
{* ../../docs_src/body/tutorial001.py hl[4] *}
{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
## Создание вашей собственной модели
## Создайте модель данных { #create-your-data-model }
После этого вы описываете вашу модель данных как класс, наследующий от `BaseModel`.
Затем опишите свою модель данных как класс, наследующийся от `BaseModel`.
Используйте аннотации типов Python для всех атрибутов:
Используйте стандартные типы Python для всех атрибутов:
{* ../../docs_src/body/tutorial001.py hl[7:11] *}
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
Также как и при описании параметров запроса, когда атрибут модели имеет значение по умолчанию, он является необязательным. Иначе он обязателен. Используйте `None`, чтобы сделать его необязательным без использования конкретных значений по умолчанию.
Так же, как при объявлении параметров запроса: когда атрибут модели имеет значение по умолчанию, он не обязателен. Иначе он обязателен. Используйте `None`, чтобы сделать его просто необязательным.
Например, модель выше описывает вот такой JSON "объект" (или словарь Python):
Например, модель выше описывает такой JSON "объект" (или Python `dict`):
```JSON
{
@ -45,7 +45,7 @@
}
```
...поскольку `description` и `tax` являются необязательными (с `None` в качестве значения по умолчанию), вот такой JSON "объект" также подходит:
...так как `description` и `tax` являются необязательными (со значением по умолчанию `None`), такой JSON "объект" тоже будет корректным:
```JSON
{
@ -54,109 +54,119 @@
}
```
## Объявление как параметра функции
## Объявите её как параметр { #declare-it-as-a-parameter }
Чтобы добавить параметр к вашему *обработчику*, объявите его также, как вы объявляли параметры пути или параметры запроса:
Чтобы добавить её в вашу *операцию пути*, объявите её так же, как вы объявляли параметры пути и параметры запроса:
{* ../../docs_src/body/tutorial001.py hl[18] *}
{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
...и укажите созданную модель в качестве типа параметра, `Item`.
...и укажите тип параметра как созданную вами модель, `Item`.
## Результаты
## Результаты { #results }
Всего лишь с помощью аннотации типов Python, **FastAPI**:
Всего лишь с этой аннотацией типов Python **FastAPI**:
* Читает тело запроса как JSON.
* Приводит к соответствующим типам (если есть необходимость).
* Проверяет корректность данных.
* Если данные некорректны, будет возращена читаемая и понятная ошибка, показывающая что именно и в каком месте некорректно в данных.
* Складывает полученные данные в параметр `item`.
* Поскольку внутри функции вы объявили его с типом `Item`, то теперь у вас есть поддержка со стороны редактора (автодополнение и т.п.) для всех атрибутов и их типов.
* Генерирует декларативное описание модели в виде <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a>, так что вы можете его использовать где угодно, если это имеет значение для вашего проекта.
* Эти схемы являются частью сгенерированной схемы OpenAPI и используются для автоматического документирования <abbr title="Пользовательских интерфейсов (User Interfaces)">UI</abbr>.
* Считает тело запроса как JSON.
* Приведёт данные к соответствующим типам (если потребуется).
* Проведёт валидацию данных.
* Если данные некорректны, вернёт понятную и наглядную ошибку, указывающую, где именно и что было некорректно.
* Передаст полученные данные в параметр `item`.
* Поскольку внутри функции вы объявили его с типом `Item`, у вас будет поддержка со стороны редактора кода (автозавершение и т. п.) для всех атрибутов и их типов.
* Сгенерирует определения <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> для вашей модели; вы можете использовать их и в других местах, если это имеет смысл для вашего проекта.
* Эти схемы будут частью сгенерированной схемы OpenAPI и будут использоваться автоматической документацией <abbr title="User Interfaces – Пользовательские интерфейсы">UIs</abbr>.
## Автоматическое документирование
## Автоматическая документация { #automatic-docs }
Схема JSON ваших моделей будет частью сгенерированной схемы OpenAPI и будет отображена в интерактивной документации API:
JSON Schema ваших моделей будет частью сгенерированной схемы OpenAPI и будет отображаться в интерактивной документации API:
<img src="/img/tutorial/body/image01.png">
Также она будет указана в документации по API внутри каждой *операции пути*, в которой используются:
А также они будут использоваться в документации API внутри каждой *операции пути*, где это требуется:
<img src="/img/tutorial/body/image02.png">
## Поддержка редактора
## Поддержка редактора кода { #editor-support }
В вашем редакторе внутри вашей функции у вас будут подсказки по типам и автодополнение (это не будет работать, если вы получаете словарь вместо модели Pydantic):
В вашем редакторе кода внутри функции вы получите подсказки по типам и автозавершение повсюду (этого бы не было, если бы вы получали `dict` вместо модели Pydantic):
<img src="/img/tutorial/body/image03.png">
Также вы будете получать ошибки в случае несоответствия типов:
Также вы получите проверку ошибок при некорректных операциях с типами:
<img src="/img/tutorial/body/image04.png">
Это не случайно, весь фреймворк построен вокруг такого дизайна.
Это не случайность — весь фреймворк построен вокруг такого дизайна.
И это все тщательно протестировано еще на этапе разработки дизайна, до реализации, чтобы это работало со всеми редакторами.
И это было тщательно протестировано ещё на этапе проектирования, до реализации, чтобы убедиться, что всё будет работать со всеми редакторами.
Для поддержки этого даже были внесены некоторые изменения в сам Pydantic.
В сам Pydantic даже были внесены некоторые изменения для поддержки этого.
На всех предыдущих скриншотах используется <a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>.
Предыдущие скриншоты сделаны в <a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>.
Но у вас будет такая же поддержка и с <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>, и вообще с любым редактором Python:
Но вы получите такую же поддержку редактора кода в <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> и большинстве других редакторов Python:
<img src="/img/tutorial/body/image05.png">
/// tip | Подсказка
/// tip | Совет
Если вы используете <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> в качестве редактора, то вам стоит попробовать плагин <a href="https://github.com/koxudaxi/pydantic-pycharm-plugin/" class="external-link" target="_blank">Pydantic PyCharm Plugin</a>.
Если вы используете <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> в качестве редактора кода, вы можете использовать плагин <a href="https://github.com/koxudaxi/pydantic-pycharm-plugin/" class="external-link" target="_blank">Pydantic PyCharm Plugin</a>.
Он улучшает поддержку редактором моделей Pydantic в части:
Он улучшает поддержку моделей Pydantic в редакторе кода, включая:
* автодополнения,
* проверки типов,
* рефакторинга,
* поиска,
* инспектирования.
* автозавершение
* проверки типов
* рефакторинг
* поиск
* инспекции
///
## Использование модели
## Использование модели { #use-the-model }
Внутри функции вам доступны все атрибуты объекта модели напрямую:
{* ../../docs_src/body/tutorial002.py hl[21] *}
{* ../../docs_src/body/tutorial002_py310.py *}
## Тело запроса + параметры пути
/// info | Информация
В Pydantic v1 метод назывался `.dict()`, в Pydantic v2 он был помечен как устаревший (но всё ещё поддерживается) и переименован в `.model_dump()`.
Вы можете одновременно объявлять параметры пути и тело запроса.
Примеры здесь используют `.dict()` для совместимости с Pydantic v1, но если вы можете использовать Pydantic v2, используйте `.model_dump()`.
///
**FastAPI** распознает, какие параметры функции соответствуют параметрам пути и должны быть **получены из пути**, а какие параметры функции, объявленные как модели Pydantic, должны быть **получены из тела запроса**.
## Тело запроса + параметры пути { #request-body-path-parameters }
{* ../../docs_src/body/tutorial003.py hl[17:18] *}
Вы можете одновременно объявить параметры пути и тело запроса.
## Тело запроса + параметры пути + параметры запроса
**FastAPI** распознает, что параметры функции, соответствующие параметрам пути, должны быть **получены из пути**, а параметры функции, объявленные как модели Pydantic, должны быть **получены из тела запроса**.
Вы также можете одновременно объявить параметры для **пути**, **запроса** и **тела запроса**.
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
**FastAPI** распознает каждый из них и возьмет данные из правильного источника.
## Тело запроса + параметры пути + параметры запроса { #request-body-path-query-parameters }
{* ../../docs_src/body/tutorial004.py hl[18] *}
Вы также можете одновременно объявить параметры **тела**, **пути** и **запроса**.
Параметры функции распознаются следующим образом:
**FastAPI** распознает каждый из них и возьмёт данные из правильного источника.
* Если параметр также указан в **пути**, то он будет использоваться как параметр пути.
* Если аннотация типа параметра содержит **примитивный тип** (`int`, `float`, `str`, `bool` и т.п.), он будет интерпретирован как параметр **запроса**.
* Если аннотация типа параметра представляет собой **модель Pydantic**, он будет интерпретирован как параметр **тела запроса**.
{* ../../docs_src/body/tutorial004_py310.py hl[16] *}
Параметры функции будут распознаны следующим образом:
* Если параметр также объявлен в **пути**, он будет использоваться как параметр пути.
* Если параметр имеет **скалярный тип** (например, `int`, `float`, `str`, `bool` и т. п.), он будет интерпретирован как параметр **запроса**.
* Если параметр объявлен как тип **модели Pydantic**, он будет интерпретирован как **тело** запроса.
/// note | Заметка
FastAPI понимает, что значение параметра `q` не является обязательным, потому что имеет значение по умолчанию `= None`.
FastAPI понимает, что значение `q` не является обязательным из-за значения по умолчанию `= None`.
Аннотации типов `str | None` (Python 3.10+) или `Union[str, None]` (Python 3.8+) не используются FastAPI для определения обязательности; он узнает, что параметр не обязателен, потому что у него есть значение по умолчанию `= None`.
Аннотация `Optional` в `Optional[str]` не используется FastAPI, но помогает вашему редактору лучше понимать ваш код и обнаруживать ошибки.
Но добавление аннотаций типов позволит вашему редактору кода лучше вас поддерживать и обнаруживать ошибки.
///
## Без Pydantic
## Без Pydantic { #without-pydantic }
Если вы не хотите использовать модели Pydantic, вы все еще можете использовать параметры **тела запроса**. Читайте в документации раздел [Тело - Несколько параметров: Единичные значения в теле](body-multiple-params.md#_2){.internal-link target=_blank}.
Если вы не хотите использовать модели Pydantic, вы также можете использовать параметры **Body**. См. раздел документации [Тело — Несколько параметров: Единичные значения в теле](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}.

12
docs/ru/docs/tutorial/cookie-param-models.md

@ -1,4 +1,4 @@
# Модели параметров cookie
# Модели параметров cookie { #cookie-parameter-models }
Если у вас есть группа **cookies**, которые связаны между собой, вы можете создать **Pydantic-модель** для их объявления. 🍪
@ -16,7 +16,7 @@
///
## Pydantic-модель для cookies
## Pydantic-модель для cookies { #cookies-with-a-pydantic-model }
Объявите параметры **cookie**, которые вам нужны, в **Pydantic-модели**, а затем объявите параметр как `Cookie`:
@ -24,7 +24,7 @@
**FastAPI** **извлечёт** данные для **каждого поля** из **cookies**, полученных в запросе, и выдаст вам объявленную Pydantic-модель.
## Проверка сгенерированной документации
## Проверка сгенерированной документации { #check-the-docs }
Вы можете посмотреть объявленные cookies в графическом интерфейсе Документации по пути `/docs`:
@ -42,7 +42,7 @@
///
## Запрет дополнительных cookies
## Запрет дополнительных cookies { #forbid-extra-cookies }
В некоторых случаях (не особо часто встречающихся) вам может понадобиться **ограничить** cookies, которые вы хотите получать.
@ -65,12 +65,12 @@
"type": "extra_forbidden",
"loc": ["cookie", "santa_tracker"],
"msg": "Extra inputs are not permitted",
"input": "good-list-please"
"input": "good-list-please",
}
]
}
```
## Заключение
## Заключение { #summary }
Вы можете использовать **Pydantic-модели** для объявления <abbr title="Съешьте последнюю печеньку, прежде чем уйти. 🍪">**cookies**</abbr> в **FastAPI**. 😎

24
docs/ru/docs/tutorial/cookie-params.md

@ -1,20 +1,20 @@
# Параметры Cookie
# Параметры Cookie { #cookie-parameters }
Вы можете задать параметры Cookie таким же способом, как `Query` и `Path` параметры.
## Импорт `Cookie`
## Импорт `Cookie` { #import-cookie }
Сначала импортируйте `Cookie`:
{* ../../docs_src/cookie_params/tutorial001_py310.py hl[1] *}
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *}
## Объявление параметров `Cookie`
## Объявление параметров `Cookie` { #declare-cookie-parameters }
Затем объявляйте параметры cookie, используя ту же структуру, что и с `Path` и `Query`.
Первое значение - это значение по умолчанию, вы можете передать все дополнительные параметры проверки или аннотации:
Вы можете задать значение по умолчанию, а также все дополнительные параметры валидации или аннотации:
{* ../../docs_src/cookie_params/tutorial001_py310.py hl[7] *}
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[9] *}
/// note | Технические детали
@ -30,6 +30,16 @@
///
## Резюме
/// info | Дополнительная информация
Имейте в виду, что, поскольку браузеры обрабатывают cookies особым образом и «за кулисами», они не позволяют JavaScript просто так получать к ним доступ.
Если вы откроете интерфейс документации API на `/docs`, вы сможете увидеть документацию по cookies для ваших операций пути.
Но даже если вы заполните данные и нажмёте «Execute», поскольку UI документации работает с JavaScript, cookies отправлены не будут, и вы увидите сообщение об ошибке, как будто вы не указали никаких значений.
///
## Резюме { #recap }
Объявляйте cookies с помощью `Cookie`, используя тот же общий шаблон, что и `Query`, и `Path`.

57
docs/ru/docs/tutorial/cors.md

@ -1,10 +1,10 @@
# CORS (Cross-Origin Resource Sharing)
# CORS (Cross-Origin Resource Sharing) { #cors-cross-origin-resource-sharing }
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">Понятие CORS или "Cross-Origin Resource Sharing"</a> относится к ситуациям, при которых запущенный в браузере фронтенд содержит JavaScript-код, который взаимодействует с бэкендом, находящимся на другом "источнике" ("origin").
## Источник
## Источник { #origin }
Источник - это совокупность протокола (`http`, `https`), домена (`myapp.com`, `localhost`, `localhost.tiangolo.com`) и порта (`80`, `443`, `8080`).
Источник это совокупность протокола (`http`, `https`), домена (`myapp.com`, `localhost`, `localhost.tiangolo.com`) и порта (`80`, `443`, `8080`).
Поэтому это три разных источника:
@ -12,69 +12,72 @@
* `https://localhost`
* `http://localhost:8080`
Даже если они все расположены в `localhost`, они используют разные протоколы и порты, а значит, являются разными источниками.
Даже если они все расположены в `localhost`, они используют разные протоколы или порты, а значит, являются разными источниками.
## Шаги
## Шаги { #steps }
Допустим, у вас есть фронтенд, запущенный в браузере по адресу `http://localhost:8080`, и его JavaScript-код пытается взаимодействовать с бэкендом, запущенным по адресу `http://localhost` (поскольку мы не указали порт, браузер по умолчанию будет использовать порт `80`).
Допустим, у вас есть фронтенд, запущенный в браузере по адресу `http://localhost:8080`, и его JavaScript-код пытается взаимодействовать с бэкендом, запущенным по адресу `http://localhost` (поскольку мы не указали порт, браузер по умолчанию будет использовать порт `80`).
Затем браузер отправит бэкенду HTTP-запрос `OPTIONS`, и если бэкенд вернёт соответствующие заголовки для авторизации взаимодействия с другим источником (`http://localhost:8080`), то браузер разрешит JavaScript-коду на фронтенде отправить запрос на этот бэкенд.
Затем браузер отправит на бэкенд на `:80` HTTP-запрос `OPTIONS`, и если бэкенд вернёт соответствующие HTTP-заголовки, авторизующие взаимодействие с другим источником (`http://localhost:8080`), то браузер на `:8080` разрешит JavaScript на фронтенде отправить свой запрос на бэкенд на `:80`.
Чтобы это работало, у бэкенда должен быть список "разрешённых источников" ("allowed origins").
Чтобы это работало, у бэкенда на `:80` должен быть список "разрешённых источников" ("allowed origins").
В таком случае этот список должен содержать `http://localhost:8080`, чтобы фронтенд работал корректно.
В таком случае этот список должен содержать `http://localhost:8080`, чтобы фронтенд на `:8080` работал корректно.
## Подстановочный символ `"*"`
## Подстановочный символ "*" { #wildcards }
В качестве списка источников можно указать подстановочный символ `"*"` ("wildcard"), чтобы разрешить любые источники.
Но тогда не будут разрешены некоторые виды взаимодействия, включая всё связанное с учётными данными: куки, заголовки Authorization с Bearer-токенами наподобие тех, которые мы использовали ранее и т.п.
Но тогда будут разрешены только некоторые виды взаимодействия, и всё, что связано с учётными данными, будет исключено: куки, HTTP-заголовки Authorization, как при использовании Bearer-токенов, и т.п.
Поэтому, чтобы всё работало корректно, лучше явно указывать список разрешённых источников.
## Использование `CORSMiddleware`
## Использование `CORSMiddleware` { #use-corsmiddleware }
Вы можете настроить этот механизм в вашем **FastAPI** приложении, используя `CORSMiddleware`.
Вы можете настроить это в вашем **FastAPI**-приложении, используя `CORSMiddleware`.
* Импортируйте `CORSMiddleware`.
* Создайте список разрешённых источников (в виде строк).
* Добавьте его как "middleware" к вашему **FastAPI** приложению.
* Добавьте его как "middleware" (промежуточный слой) к вашему **FastAPI**-приложению.
Вы также можете указать, разрешает ли ваш бэкенд использование:
* Учётных данных (включая заголовки Authorization, куки и т.п.).
* Учётных данных (HTTP-заголовки Authorization, куки и т.п.).
* Отдельных HTTP-методов (`POST`, `PUT`) или всех вместе, используя `"*"`.
* Отдельных HTTP-заголовков или всех вместе, используя `"*"`.
{* ../../docs_src/cors/tutorial001.py hl[2,6:11,13:19] *}
`CORSMiddleware` использует для параметров "запрещающие" значения по умолчанию, поэтому вам нужно явным образом разрешить использование отдельных источников, методов или заголовков, чтобы браузеры могли использовать их в кросс-доменном контексте.
`CORSMiddleware` использует "запрещающие" значения по умолчанию, поэтому вам нужно явным образом разрешить использование отдельных источников, методов или заголовков, чтобы браузеры могли использовать их в кросс-доменном контексте.
Поддерживаются следующие аргументы:
* `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` всегда разрешены для <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests" class="external-link" rel="noopener" target="_blank">простых CORS-запросов</a>.
* `allow_credentials` - указывает, что куки разрешены в кросс-доменных запросах. По умолчанию равно `False`. Также, `allow_origins` нельзя присвоить `['*']`, если разрешено использование учётных данных. В таком случае должен быть указан список источников.
* `expose_headers` - Указывает любые заголовки ответа, которые должны быть доступны браузеру. По умолчанию равно `[]`.
* `max_age` - Устанавливает максимальное время в секундах, в течение которого браузер кэширует CORS-ответы. По умолчанию равно `600`.
* `allow_origin_regex` - Регулярное выражение для определения источников, на которые разрешено выполнять кросс-доменные запросы. Например, `'https://.*\.example\.org'`.
* `allow_methods` - Список HTTP-методов, которые разрешены для кросс-доменных запросов. По умолчанию `['GET']`. Можно использовать `['*']`, чтобы разрешить все стандартные методы.
* `allow_headers` - Список HTTP-заголовков запроса, которые должны поддерживаться при кросс-доменных запросах. По умолчанию `[]`. Можно использовать `['*']`, чтобы разрешить все заголовки. Заголовки `Accept`, `Accept-Language`, `Content-Language` и `Content-Type` всегда разрешены для <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests" class="external-link" rel="noopener" target="_blank">простых CORS-запросов</a>.
* `allow_credentials` - Указывает, что куки разрешены в кросс-доменных запросах. По умолчанию `False`.
Ни один из параметров `allow_origins`, `allow_methods` и `allow_headers` не может быть установлен в `['*']`, если `allow_credentials` имеет значение `True`. Все они должны быть <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards" class="external-link" rel="noopener" target="_blank">указаны явно</a>.
* `expose_headers` - Указывает любые заголовки ответа, которые должны быть доступны браузеру. По умолчанию `[]`.
* `max_age` - Устанавливает максимальное время в секундах, в течение которого браузер кэширует CORS-ответы. По умолчанию `600`.
`CORSMiddleware` отвечает на два типа HTTP-запросов...
### CORS-запросы с предварительной проверкой
### CORS-запросы с предварительной проверкой { #cors-preflight-requests }
Это любые `OPTIONS` запросы с заголовками `Origin` и `Access-Control-Request-Method`.
Это любые `OPTIONS`-запросы с заголовками `Origin` и `Access-Control-Request-Method`.
В этом случае middleware перехватит входящий запрос и отправит соответствующие CORS-заголовки в ответе, а также ответ `200` или `400` в информационных целях.
### Простые запросы
### Простые запросы { #simple-requests }
Любые запросы с заголовком `Origin`. В этом случае middleware передаст запрос дальше как обычно, но добавит соответствующие CORS-заголовки к ответу.
## Больше информации
## Больше информации { #more-info }
Для получения более подробной информации о <abbr title="Cross-Origin Resource Sharing">CORS</abbr>, обратитесь к <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">Документации CORS от Mozilla</a>.
Для получения более подробной информации о <abbr title="Cross-Origin Resource Sharing – совместное использование ресурсов между источниками">CORS</abbr> обратитесь к <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">документации CORS от Mozilla</a>.
/// note | Технические детали

10
docs/ru/docs/tutorial/debugging.md

@ -1,14 +1,14 @@
# Отладка
# Отладка { #debugging }
Вы можете подключить отладчик в своем редакторе, например, в Visual Studio Code или PyCharm.
## Вызов `uvicorn`
## Вызов `uvicorn` { #call-uvicorn }
В вашем FastAPI приложении, импортируйте и вызовите `uvicorn` напрямую:
{* ../../docs_src/debugging/tutorial001.py hl[1,15] *}
### Описание `__name__ == "__main__"`
### Описание `__name__ == "__main__"` { #about-name-main }
Главная цель использования `__name__ == "__main__"` в том, чтобы код выполнялся при запуске файла с помощью:
@ -26,7 +26,7 @@ $ python myapp.py
from myapp import app
```
#### Больше деталей
#### Больше деталей { #more-details }
Давайте назовём ваш файл `myapp.py`.
@ -78,7 +78,7 @@ from myapp import app
///
## Запуск вашего кода с помощью отладчика
## Запуск вашего кода с помощью отладчика { #run-your-code-with-your-debugger }
Так как вы запускаете сервер Uvicorn непосредственно из вашего кода, вы можете вызвать Python программу (ваше FastAPI приложение) напрямую из отладчика.

123
docs/ru/docs/tutorial/dependencies/classes-as-dependencies.md

@ -1,28 +1,30 @@
# Классы как зависимости
# Классы как зависимости { #classes-as-dependencies }
Прежде чем углубиться в систему **Внедрения Зависимостей**, давайте обновим предыдущий пример.
## `Словарь` из предыдущего примера
## `dict` из предыдущего примера { #a-dict-from-the-previous-example }
В предыдущем примере мы возвращали `словарь` из нашей зависимости:
В предыдущем примере мы возвращали `dict` из нашей зависимости:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
Но затем мы получаем `словарь` в параметре `commons` *функции операции пути*. И мы знаем, что редакторы не могут обеспечить достаточную поддержку для `словаря`, поскольку они не могут знать их ключи и типы значений.
Но затем мы получаем `dict` в параметре `commons` *функции-обработчика пути*.
И мы знаем, что редакторы кода не могут обеспечить достаточную поддержку (например, автозавершение) для `dict`, поскольку они не могут знать их ключи и типы значений.
Мы можем сделать лучше...
## Что делает зависимость
## Что делает зависимость { #what-makes-a-dependency }
До сих пор вы видели зависимости, объявленные как функции.
Но это не единственный способ объявления зависимостей (хотя, вероятно, более распространенный).
Но это не единственный способ объявления зависимостей (хотя он, вероятно, более распространенный).
Ключевым фактором является то, что зависимость должна быть "вызываемой".
Ключевым фактором является то, что зависимость должна быть «вызываемой».
В Python "**вызываемый**" - это все, что Python может "вызвать", как функцию.
В Python «**вызываемый**» — это всё, что Python может «вызвать», как функцию.
Так, если у вас есть объект `something` (который может _не_ быть функцией) и вы можете "вызвать" его (выполнить) как:
Так, если у вас есть объект `something` (который может и _не_ быть функцией) и вы можете «вызвать» его (выполнить) так:
```Python
something()
@ -34,9 +36,9 @@ something()
something(some_argument, some_keyword_argument="foo")
```
в таком случае он является "вызываемым".
в таком случае он является «вызываемым».
## Классы как зависимости
## Классы как зависимости { #classes-as-dependencies_1 }
Вы можете заметить, что для создания экземпляра класса в Python используется тот же синтаксис.
@ -53,17 +55,17 @@ fluffy = Cat(name="Mr Fluffy")
В данном случае `fluffy` является экземпляром класса `Cat`.
А чтобы создать `fluffy`, вы "вызываете" `Cat`.
А чтобы создать `fluffy`, вы «вызываете» `Cat`.
Таким образом, класс в Python также является **вызываемым**.
Тогда в **FastAPI** в качестве зависимости можно использовать класс Python.
На самом деле FastAPI проверяет, что переданный объект является "вызываемым" (функция, класс или что-либо еще) и указаны необходимые для его вызова параметры.
На самом деле FastAPI проверяет, что переданный объект является «вызываемым» (функция, класс или что-либо еще) и какие параметры у него определены.
Если вы передаёте что-то, что можно "вызывать" в качестве зависимости в **FastAPI**, то он будет анализировать параметры, необходимые для "вызова" этого объекта и обрабатывать их так же, как параметры *функции операции пути*. Включая подзависимости.
Если вы передаёте «вызываемый» объект в качестве зависимости в **FastAPI**, он проанализирует параметры, необходимые для этого «вызываемого» объекта, и обработает их так же, как параметры *функции-обработчика пути*. Включая подзависимости.
Это относится и к вызываемым объектам без параметров. Работа с ними происходит точно так же, как и для *функций операции пути* без параметров.
Это относится и к вызываемым объектам без параметров. Работа с ними происходит точно так же, как и для *функций-обработчиков пути* без параметров.
Теперь мы можем изменить зависимость `common_parameters`, указанную выше, на класс `CommonQueryParams`:
@ -73,55 +75,55 @@ fluffy = Cat(name="Mr Fluffy")
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
...имеет те же параметры, что и ранее используемая функция `common_parameters`:
...он имеет те же параметры, что и ранее используемая функция `common_parameters`:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
Эти параметры и будут использоваться **FastAPI** для "решения" зависимости.
Эти параметры и будут использоваться **FastAPI** для «решения» зависимости.
В обоих случаях она будет иметь:
* Необязательный параметр запроса `q`, представляющий собой `str`.
* Параметр запроса `skip`, представляющий собой `int`, по умолчанию `0`.
* Параметр запроса `limit`, представляющий собой `int`, по умолчанию равный `100`.
* Параметр запроса `limit`, представляющий собой `int`, по умолчанию `100`.
В обоих случаях данные будут конвертированы, валидированы, документированы по схеме OpenAPI и т.д.
В обоих случаях данные будут конвертированы, валидированы, задокументированы в схеме OpenAPI и т.д.
## Как это использовать
## Как это использовать { #use-it }
Теперь вы можете объявить свою зависимость, используя этот класс.
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
**FastAPI** вызывает класс `CommonQueryParams`. При этом создается "экземпляр" этого класса, который будет передан в качестве параметра `commons` в вашу функцию.
**FastAPI** вызывает класс `CommonQueryParams`. При этом создается «экземпляр» этого класса, который будет передан в качестве параметра `commons` в вашу функцию.
## Аннотация типа или `Depends`
## Аннотация типа и `Depends` { #type-annotation-vs-depends }
Обратите внимание, что в приведенном выше коде мы два раза пишем `CommonQueryParams`:
//// tab | Python 3.6+ без Annotated
/// tip | Подсказка
Рекомендуется использовать версию с `Annotated` если возможно.
///
//// tab | Python 3.8+
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.6+
//// tab | Python 3.8+ non-Annotated
/// tip | Подсказка
Рекомендуется использовать версию с `Annotated`, если возможно.
///
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
Последний параметр `CommonQueryParams`, в:
Последний `CommonQueryParams`, в:
```Python
... Depends(CommonQueryParams)
@ -129,13 +131,13 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
...это то, что **FastAPI** будет использовать, чтобы узнать, что является зависимостью.
Из него FastAPI извлечёт объявленные параметры и именно их будет вызывать.
Из него FastAPI извлечёт объявленные параметры, и именно его FastAPI будет вызывать.
---
В этом случае первый `CommonQueryParams`, в:
//// tab | Python 3.6+
//// tab | Python 3.8+
```Python
commons: Annotated[CommonQueryParams, ...
@ -143,11 +145,11 @@ commons: Annotated[CommonQueryParams, ...
////
//// tab | Python 3.6+ без Annotated
//// tab | Python 3.8+ non-Annotated
/// tip | Подсказка
Рекомендуется использовать версию с `Annotated` если возможно.
Рекомендуется использовать версию с `Annotated`, если возможно.
///
@ -161,7 +163,7 @@ commons: CommonQueryParams ...
На самом деле можно написать просто:
//// tab | Python 3.6+
//// tab | Python 3.8+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
@ -169,11 +171,11 @@ commons: Annotated[Any, Depends(CommonQueryParams)]
////
//// tab | Python 3.6+ без Annotated
//// tab | Python 3.8+ non-Annotated
/// tip | Подсказка
Рекомендуется использовать версию с `Annotated` если возможно.
Рекомендуется использовать версию с `Annotated`, если возможно.
///
@ -187,42 +189,43 @@ commons = Depends(CommonQueryParams)
{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
Но объявление типа приветствуется, так как в этом случае ваш редактор будет знать, что будет передано в качестве параметра `commons`, и тогда он сможет помочь вам с автодополнением, проверкой типов и т.д:
Но объявление типа приветствуется, так как в этом случае ваш редактор кода будет знать, что будет передано в качестве параметра `commons`, и тогда он сможет помочь вам с автозавершением, проверкой типов и т.д.:
<img src="/img/tutorial/dependencies/image02.png">
## Сокращение
## Сокращение { #shortcut }
Но вы видите, что здесь мы имеем некоторое повторение кода, дважды написав `CommonQueryParams`:
//// tab | Python 3.6+ без Annotated
/// tip | Подсказка
Рекомендуется использовать версию с `Annotated` если возможно.
///
//// tab | Python 3.8+
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.6+
//// tab | Python 3.8+ non-Annotated
/// tip | Подсказка
Рекомендуется использовать версию с `Annotated`, если возможно.
///
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
Для случаев, когда зависимостью является *конкретный* класс, который **FastAPI** "вызовет" для создания экземпляра этого класса, можно использовать укороченную запись.
**FastAPI** предоставляет сокращение для таких случаев, когда зависимость — это *конкретный* класс, который **FastAPI** будет «вызывать» для создания экземпляра этого класса.
Для этих конкретных случаев вы можете сделать следующее.
Вместо того чтобы писать:
//// tab | Python 3.6+
//// tab | Python 3.8+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
@ -230,11 +233,11 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
//// tab | Python 3.6+ без Annotated
//// tab | Python 3.8+ non-Annotated
/// tip | Подсказка
Рекомендуется использовать версию с `Annotated` если возможно.
Рекомендуется использовать версию с `Annotated`, если возможно.
///
@ -246,7 +249,7 @@ commons: CommonQueryParams = Depends(CommonQueryParams)
...следует написать:
//// tab | Python 3.6+
//// tab | Python 3.8+
```Python
commons: Annotated[CommonQueryParams, Depends()]
@ -254,11 +257,11 @@ commons: Annotated[CommonQueryParams, Depends()]
////
//// tab | Python 3.6 без Annotated
//// tab | Python 3.8 non-Annotated
/// tip | Подсказка
Рекомендуется использовать версию с `Annotated` если возможно.
Рекомендуется использовать версию с `Annotated`, если возможно.
///
@ -278,7 +281,7 @@ commons: CommonQueryParams = Depends()
/// tip | Подсказка
Если это покажется вам более запутанным, чем полезным, не обращайте внимания, это вам не *нужно*.
Если это покажется вам более запутанным, чем полезным, не обращайте внимания это вам не *нужно*.
Это просто сокращение. Потому что **FastAPI** заботится о том, чтобы помочь вам свести к минимуму повторение кода.

18
docs/ru/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md

@ -1,4 +1,4 @@
# Зависимости в декораторах операции пути
# Зависимости в декораторах операции пути { #dependencies-in-path-operation-decorators }
В некоторых случаях, возвращаемое значение зависимости не используется внутри *функции операции пути*.
@ -8,7 +8,7 @@
Для таких ситуаций, вместо объявления *функции операции пути* с параметром `Depends`, вы можете добавить список зависимостей `dependencies` в *декоратор операции пути*.
## Добавление `dependencies` в *декоратор операции пути*
## Добавление `dependencies` (зависимостей) в *декоратор операции пути* { #add-dependencies-to-the-path-operation-decorator }
*Декоратор операции пути* получает необязательный аргумент `dependencies`.
@ -36,23 +36,23 @@
///
## Исключения в dependencies и возвращаемые значения
## Исключения в Зависимостях и возвращаемые значения { #dependencies-errors-and-return-values }
Вы можете использовать те же *функции* зависимостей, что и обычно.
### Требования к зависимостям
### Требования к зависимостям { #dependency-requirements }
Они могут объявлять требования к запросу (например заголовки) или другие подзависимости:
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[8,13] *}
### Вызов исключений
### Вызов исключений { #raise-exceptions }
Зависимости из dependencies могут вызывать исключения с помощью `raise`, как и обычные зависимости:
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[10,15] *}
### Возвращаемые значения
### Возвращаемые значения { #return-values }
И они могут возвращать значения или нет, эти значения использоваться не будут.
@ -60,10 +60,10 @@
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[11,16] *}
## Dependencies для группы *операций путей*
## Зависимости для группы *операций путей* { #dependencies-for-a-group-of-path-operations }
Позже, читая о том как структурировать большие приложения ([Bigger Applications - Multiple Files](../../tutorial/bigger-applications.md){.internal-link target=_blank}), возможно, многофайловые, вы узнаете как объявить единый параметр `dependencies` для всей группы *операций путей*.
Позже, читая о том как структурировать большие приложения ([Большие приложения — несколько файлов](../../tutorial/bigger-applications.md){.internal-link target=_blank}), возможно, многофайловые, вы узнаете как объявить единый параметр `dependencies` для всей группы *операций путей*.
## Глобальный Dependencies
## Глобальные Зависимости { #global-dependencies }
Далее мы увидим, как можно добавить dependencies для всего `FastAPI` приложения, так чтобы они применялись к каждой *операции пути*.

183
docs/ru/docs/tutorial/dependencies/dependencies-with-yield.md

@ -1,125 +1,139 @@
# Зависимости с yield
# Зависимости с yield { #dependencies-with-yield }
FastAPI поддерживает зависимости, которые выполняют некоторые <abbr title='также известные как "exit", "cleanup", "teardown", "close", "context managers", ...'>дополнительные действия после завершения работы</abbr>.
FastAPI поддерживает зависимости, которые выполняют некоторые <abbr title='иногда также называемые "exit code", "cleanup code", "teardown code", "closing code", "context manager exit code" и т.п.'>дополнительные шаги после завершения</abbr>.
Для этого используйте `yield` вместо `return`, а дополнительный код напишите после него.
Для этого используйте `yield` вместо `return`, а дополнительные шаги (код) напишите после него.
/// tip | Подсказка
Обязательно используйте `yield` один-единственный раз.
Убедитесь, что используете `yield` только один раз на одну зависимость.
///
/// note | Технические детали
Любая функция, с которой может работать:
Любая функция, с которой можно корректно использовать:
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" class="external-link" target="_blank">`@contextlib.contextmanager`</a> или
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" class="external-link" target="_blank">`@contextlib.asynccontextmanager`</a>
будет корректно использоваться в качестве **FastAPI**-зависимости.
будет корректной для использования в качестве зависимости **FastAPI**.
На самом деле, FastAPI использует эту пару декораторов "под капотом".
На самом деле, FastAPI использует эти два декоратора внутренне.
///
## Зависимость базы данных с помощью `yield`
## Зависимость базы данных с помощью `yield` { #a-database-dependency-with-yield }
Например, с его помощью можно создать сессию работы с базой данных и закрыть его после завершения.
Например, с его помощью можно создать сессию работы с базой данных и закрыть её после завершения.
Перед созданием ответа будет выполнен только код до и включая `yield`.
Перед созданием ответа будет выполнен только код до и включая оператор `yield`:
{* ../../docs_src/dependencies/tutorial007.py hl[2:4] *}
Полученное значение и есть то, что будет внедрено в функцию операции пути и другие зависимости:
Значение, полученное из `yield`, внедряется в *операции пути* и другие зависимости:
{* ../../docs_src/dependencies/tutorial007.py hl[4] *}
Код, следующий за оператором `yield`, выполняется после доставки ответа:
Код, следующий за оператором `yield`, выполняется после ответа:
{* ../../docs_src/dependencies/tutorial007.py hl[5:6] *}
/// tip | Подсказка
Можно использовать как `async` так и обычные функции.
Можно использовать как `async`, так и обычные функции.
**FastAPI** это корректно обработает, и в обоих случаях будет делать то же самое, что и с обычными зависимостями.
**FastAPI** корректно обработает каждый вариант, так же как и с обычными зависимостями.
///
## Зависимость с `yield` и `try` одновременно
## Зависимость с `yield` и `try` { #a-dependency-with-yield-and-try }
Если использовать блок `try` в зависимости с `yield`, то будет получено всякое исключение, которое было выброшено при использовании зависимости.
Если использовать блок `try` в зависимости с `yield`, то вы получите любое исключение, которое было выброшено при использовании зависимости.
Например, если какой-то код в какой-то момент в середине, в другой зависимости или в *функции операции пути*, сделал "откат" транзакции базы данных или создал любую другую ошибку, то вы получите исключение в своей зависимости.
Например, если какой-то код в какой-то момент в середине, в другой зависимости или в *операции пути*, сделал "откат" транзакции базы данных или создал любую другую ошибку, то вы получите это исключение в своей зависимости.
Таким образом, можно искать конкретное исключение внутри зависимости с помощью `except SomeException`.
Таким же образом можно использовать `finally`, чтобы убедиться, что обязательные шаги при выходе выполнены, независимо от того, было ли исключение или нет.
Точно так же можно использовать `finally`, чтобы убедиться, что обязательные шаги при выходе выполнены независимо от того, было ли исключение или нет.
{* ../../docs_src/dependencies/tutorial007.py hl[3,5] *}
## Подзависимости с `yield`
## Подзависимости с `yield` { #sub-dependencies-with-yield }
Вы можете иметь подзависимости и "деревья" подзависимостей любого размера и формы, и любая из них или все они могут использовать `yield`.
**FastAPI** будет следить за тем, чтобы "код по выходу" в каждой зависимости с `yield` выполнялся в правильном порядке.
**FastAPI** проследит за тем, чтобы «код выхода» в каждой зависимости с `yield` выполнялся в правильном порядке.
Например, `dependency_c` может иметь зависимость от `dependency_b`, а `dependency_b` от `dependency_a`:
Например, `dependency_c` может зависеть от `dependency_b`, а `dependency_b` от `dependency_a`:
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[6,14,22] *}
И все они могут использовать `yield`.
В этом случае `dependency_c` для выполнения своего кода выхода нуждается в том, чтобы значение из `dependency_b` (здесь `dep_b`) было еще доступно.
В этом случае `dependency_c` для выполнения своего кода выхода нуждается в том, чтобы значение из `dependency_b` (здесь `dep_b`) всё ещё было доступно.
И, в свою очередь, `dependency_b` нуждается в том, чтобы значение из `dependency_a` (здесь `dep_a`) было доступно для ее завершающего кода.
И, в свою очередь, `dependency_b` нуждается в том, чтобы значение из `dependency_a` (здесь `dep_a`) было доступно для её кода выхода.
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[18:19,26:27] *}
Точно так же можно иметь часть зависимостей с `yield`, часть с `return`, и какие-то из них могут зависеть друг от друга.
Точно так же можно иметь часть зависимостей с `yield`, часть с `return`, и какие-то из них могут зависеть друг от друга.
Либо у вас может быть одна зависимость, которая требует несколько других зависимостей с `yield` и т.д.
Комбинации зависимостей могут быть какими вам угодно.
Комбинации зависимостей могут быть какими угодно.
**FastAPI** проследит за тем, чтобы все выполнялось в правильном порядке.
**FastAPI** проследит за тем, чтобы всё выполнялось в правильном порядке.
/// note | Технические детали
Это работает благодаря <a href="https://docs.python.org/3/library/contextlib.html" class="external-link" target="_blank">Контекстным менеджерам</a> в Python.
Это работает благодаря <a href="https://docs.python.org/3/library/contextlib.html" class="external-link" target="_blank">менеджерам контекста</a> в Python.
**FastAPI** использует их внутренне для достижения этого.
///
**FastAPI** использует их "под капотом" с этой целью.
## Зависимости с `yield` и `HTTPException` { #dependencies-with-yield-and-httpexception }
## Зависимости с `yield` и `HTTPException`
Вы видели, что можно использовать зависимости с `yield` и иметь блоки `try`, которые пытаются выполнить некоторый код, а затем запускают код выхода в `finally`.
Вы видели, что можно использовать зависимости с `yield` совместно с блоком `try`, отлавливающие исключения.
Также вы можете использовать `except`, чтобы поймать вызванное исключение и что-то с ним сделать.
Таким же образом вы можете поднять исключение `HTTPException` или что-то подобное в завершающем коде, после `yield`.
Например, вы можете <abbr title="«raise» дословно - «поднять», но «вызвать», «сгенерировать» или «выбросить» употребляется чаще">вызвать</abbr> другое исключение, например `HTTPException`.
Код выхода в зависимостях с `yield` выполняется *после* отправки ответа, поэтому [Обработчик исключений](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} уже будет запущен. В коде выхода (после `yield`) нет ничего, перехватывающего исключения, брошенные вашими зависимостями.
/// tip | Подсказка
Таким образом, если после `yield` возникает `HTTPException`, то стандартный (или любой пользовательский) обработчик исключений, который перехватывает `HTTPException` и возвращает ответ HTTP 400, уже не сможет перехватить это исключение.
Это довольно продвинутая техника, и в большинстве случаев она вам не понадобится, так как вы можете вызывать исключения (включая `HTTPException`) в остальном коде вашего приложения, например, в *функции-обработчике пути*.
Благодаря этому все, что установлено в зависимости (например, сеанс работы с БД), может быть использовано, например, фоновыми задачами.
Но если понадобится — возможность есть. 🤓
Фоновые задачи выполняются *после* отправки ответа. Поэтому нет возможности поднять `HTTPException`, так как нет даже возможности изменить уже отправленный ответ.
///
Но если фоновая задача создает ошибку в БД, то, по крайней мере, можно сделать откат или чисто закрыть сессию в зависимости с помощью `yield`, а также, возможно, занести ошибку в журнал или сообщить о ней в удаленную систему отслеживания.
{* ../../docs_src/dependencies/tutorial008b_an_py39.py hl[18:22,31] *}
Если у вас есть код, который, как вы знаете, может вызвать исключение, сделайте самую обычную/"питонячью" вещь и добавьте блок `try` в этот участок кода.
Если вы хотите перехватывать исключения и формировать на их основе пользовательский ответ, создайте [Пользовательский обработчик исключений](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
Если у вас есть пользовательские исключения, которые вы хотите обрабатывать *до* возврата ответа и, возможно, модифицировать ответ, даже вызывая `HTTPException`, создайте [Cобственный обработчик исключений](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
## Зависимости с `yield` и `except` { #dependencies-with-yield-and-except }
/// tip | Подсказка
Если вы ловите исключение с помощью `except` в зависимости с `yield` и не вызываете его снова (или не вызываете новое исключение), FastAPI не сможет заметить, что было исключение — так же, как это происходит в обычном Python:
Вы все еще можете вызывать исключения, включая `HTTPException`, *до* `yield`. Но не после.
{* ../../docs_src/dependencies/tutorial008c_an_py39.py hl[15:16] *}
///
В этом случае клиент получит *HTTP 500 Internal Server Error*, как и должно быть, поскольку мы не вызываем `HTTPException` или что-то подобное, но на сервере **не будет никаких логов** или других указаний на то, какая была ошибка. 😱
### Всегда делайте `raise` в зависимостях с `yield` и `except` { #always-raise-in-dependencies-with-yield-and-except }
Если вы ловите исключение в зависимости с `yield`, то, если вы не вызываете другой `HTTPException` или что-то подобное, вам следует повторно вызвать исходное исключение.
Вы можете повторно вызвать то же самое исключение с помощью `raise`:
Последовательность выполнения примерно такая, как на этой схеме. Время течет сверху вниз. А каждый столбец - это одна из частей, взаимодействующих с кодом или выполняющих код.
{* ../../docs_src/dependencies/tutorial008d_an_py39.py hl[17] *}
Теперь клиент получит тот же *HTTP 500 Internal Server Error*, но на сервере в логах будет наше пользовательское `InternalError`. 😎
## Выполнение зависимостей с `yield` { #execution-of-dependencies-with-yield }
Последовательность выполнения примерно такая, как на этой схеме. Время течёт сверху вниз. А каждый столбец — это одна из частей, взаимодействующих с кодом или выполняющих код.
```mermaid
sequenceDiagram
@ -130,85 +144,56 @@ 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
Note over client,operation: Can raise exceptions, including HTTPException
client ->> dep: Start request
Note over dep: Run code up to yield
opt raise
dep -->> handler: Raise HTTPException
opt raise Exception
dep -->> handler: Raise Exception
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
operation -->> dep: Raise Exception (e.g. HTTPException)
opt handle
dep -->> dep: Can catch exception, raise a new HTTPException, raise other exception
end
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.
tasks -->> tasks: Handle exceptions in the background task code
end
```
/// info | Дополнительная информация
Клиенту будет отправлен только **один ответ**. Это может быть один из ответов об ошибке или это будет ответ от *операции пути*.
После отправки одного из этих ответов никакой другой ответ не может быть отправлен.
Клиенту будет отправлен только **один ответ**. Это может быть один из ответов об ошибке или ответ от *операции пути*.
///
/// tip | Подсказка
На этой диаграмме показано "HttpException", но вы также можете вызвать любое другое исключение, для которого вы создаете [Пользовательский обработчик исключений](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
Если вы создадите какое-либо исключение, оно будет передано зависимостям с yield, включая `HttpException`, а затем **снова** обработчикам исключений. Если для этого исключения нет обработчика исключений, то оно будет обработано внутренним "ServerErrorMiddleware" по умолчанию, возвращающим код состояния HTTP 500, чтобы уведомить клиента, что на сервере произошла ошибка.
После отправки одного из этих ответов никакой другой ответ отправить нельзя.
///
## Зависимости с `yield`, `HTTPException` и фоновыми задачами
/// warning | Внимание
Скорее всего, вам не нужны эти технические подробности, вы можете пропустить этот раздел и продолжить ниже.
Эти подробности полезны, главным образом, если вы использовали версию FastAPI до 0.106.0 и использовали ресурсы из зависимостей с `yield` в фоновых задачах.
///
До версии FastAPI 0.106.0 вызывать исключения после `yield` было невозможно, код выхода в зависимостях с `yield` выполнялся *после* отправки ответа, поэтому [Обработчик Ошибок](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} уже был бы запущен.
Это было сделано главным образом для того, чтобы позволить использовать те же объекты, "отданные" зависимостями, внутри фоновых задач, поскольку код выхода будет выполняться после завершения фоновых задач.
Тем не менее, поскольку это означало бы ожидание ответа в сети, а также ненужное удержание ресурса в зависимости от доходности (например, соединение с базой данных), это было изменено в FastAPI 0.106.0.
/// tip | Подсказка
Кроме того, фоновая задача обычно представляет собой независимый набор логики, который должен обрабатываться отдельно, со своими собственными ресурсами (например, собственным подключением к базе данных).
Таким образом, вы, вероятно, получите более чистый код.
Если вы вызовете какое-либо исключение в коде из *функции-обработчика пути*, оно будет передано зависимостям с `yield`, включая `HTTPException`. В большинстве случаев вы захотите повторно вызвать то же самое исключение или новое из зависимости с `yield`, чтобы убедиться, что оно корректно обработано.
///
Если вы полагались на это поведение, то теперь вам следует создавать ресурсы для фоновых задач внутри самой фоновой задачи, а внутри использовать только те данные, которые не зависят от ресурсов зависимостей с `yield`.
## Зависимости с `yield`, `HTTPException`, `except` и фоновыми задачами { #dependencies-with-yield-httpexception-except-and-background-tasks }
Например, вместо того чтобы использовать ту же сессию базы данных, вы создадите новую сессию базы данных внутри фоновой задачи и будете получать объекты из базы данных с помощью этой новой сессии. А затем, вместо того чтобы передавать объект из базы данных в качестве параметра в функцию фоновой задачи, вы передадите идентификатор этого объекта, а затем снова получите объект в функции фоновой задачи.
Зависимости с `yield` со временем эволюционировали, чтобы покрыть разные сценарии и исправить некоторые проблемы.
## Контекстные менеджеры
Если вы хотите посмотреть, что менялось в разных версиях FastAPI, вы можете прочитать об этом подробнее в продвинутом руководстве: [Продвинутые зависимости — зависимости с `yield`, `HTTPException`, `except` и фоновыми задачами](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks){.internal-link target=_blank}.
## Контекстные менеджеры { #context-managers }
### Что такое "контекстные менеджеры"
### Что такое «контекстные менеджеры» { #what-are-context-managers }
"Контекстные менеджеры" - это любые объекты Python, которые можно использовать в операторе `with`.
«Контекстные менеджеры» — это любые объекты Python, которые можно использовать в операторе `with`.
Например, <a href="https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files" class="external-link" target="_blank">можно использовать `with` для чтения файла</a>:
@ -218,41 +203,41 @@ with open("./somefile.txt") as f:
print(contents)
```
Под капотом" open("./somefile.txt") создаёт объект называемый "контекстным менеджером".
Под капотом вызов `open("./somefile.txt")` создаёт объект, называемый «контекстным менеджером».
Когда блок `with` завершается, он обязательно закрывает файл, даже если были исключения.
Когда вы создаете зависимость с помощью `yield`, **FastAPI** внутренне преобразует ее в контекстный менеджер и объединяет с некоторыми другими связанными инструментами.
Когда вы создаёте зависимость с `yield`, **FastAPI** внутренне создаёт для неё менеджер контекста и сочетает его с некоторыми другими связанными инструментами.
### Использование менеджеров контекста в зависимостях с помощью `yield`
### Использование менеджеров контекста в зависимостях с `yield` { #using-context-managers-in-dependencies-with-yield }
/// warning | Внимание
Это более или менее "продвинутая" идея.
Это, более или менее, «продвинутая» идея.
Если вы только начинаете работать с **FastAPI**, то лучше пока пропустить этот пункт.
///
В Python для создания менеджеров контекста можно <a href="https://docs.python.org/3/reference/datamodel.html#context-managers" class="external-link" target="_blank">создать класс с двумя методами: `__enter__()` и `__exit__()`</a>.
В Python можно создавать менеджеры контекста, <a href="https://docs.python.org/3/reference/datamodel.html#context-managers" class="external-link" target="_blank">создав класс с двумя методами: `__enter__()` и `__exit__()`</a>.
Вы также можете использовать их внутри зависимостей **FastAPI** с `yield`, используя операторы
Их также можно использовать внутри зависимостей **FastAPI** с `yield`, применяя операторы
`with` или `async with` внутри функции зависимости:
{* ../../docs_src/dependencies/tutorial010.py hl[1:9,13] *}
/// tip | Подсказка
Другой способ создания контекстного менеджера - с помощью:
Другой способ создания менеджера контекста — с помощью:
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" class="external-link" target="_blank">`@contextlib.contextmanager`</a> или
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" class="external-link" target="_blank">`@contextlib.asynccontextmanager`</a>
используйте их для оформления функции с одним `yield`.
оформив ими функцию с одним `yield`.
Это то, что **FastAPI** использует внутри себя для зависимостей с `yield`.
Именно это **FastAPI** использует внутренне для зависимостей с `yield`.
Но использовать декораторы для зависимостей FastAPI не обязательно (да и не стоит).
Но использовать эти декораторы для зависимостей FastAPI не обязательно (и не стоит).
FastAPI сделает это за вас на внутреннем уровне.

10
docs/ru/docs/tutorial/dependencies/global-dependencies.md

@ -1,15 +1,15 @@
# Глобальные зависимости
# Глобальные зависимости { #global-dependencies }
Для некоторых типов приложений может потребоваться добавить зависимости ко всему приложению.
Подобно тому, как вы можете [добавлять зависимости через параметр `dependencies` в *декораторах операций пути*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, вы можете добавлять зависимости сразу ко всему `FastAPI` приложению.
Подобно тому, как вы можете [добавлять `dependencies` (зависимости) в *декораторах операций пути*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, вы можете добавлять зависимости сразу ко всему `FastAPI` приложению.
В этом случае они будут применяться ко всем *операциям пути* в приложении:
{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[16] *}
Все способы [добавления зависимостей в *декораторах операций пути*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} по-прежнему применимы, но в данном случае зависимости применяются ко всем *операциям пути* приложения.
Все способы [добавления `dependencies` (зависимостей) в *декораторах операций пути*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} по-прежнему применимы, но в данном случае зависимости применяются ко всем *операциям пути* приложения.
## Зависимости для групп *операций пути*
## Зависимости для групп *операций пути* { #dependencies-for-groups-of-path-operations }
Позднее, читая о том, как структурировать более крупные [приложения, содержащие много файлов](../../tutorial/bigger-applications.md){.internal-link target=_blank}, вы узнаете, как объявить один параметр dependencies для целой группы *операций пути*.
Позднее, читая о том, как структурировать более крупные [приложения, содержащие много файлов](../../tutorial/bigger-applications.md){.internal-link target=_blank}, вы узнаете, как объявить один параметр `dependencies` для целой группы *операций пути*.

194
docs/ru/docs/tutorial/dependencies/index.md

@ -1,91 +1,97 @@
# Зависимости
# Зависимости { #dependencies }
**FastAPI** имеет очень мощную и интуитивную систему **<abbr title="also known as components, resources, providers, services, injectables">Dependency Injection</abbr>**.
**FastAPI** имеет очень мощную, но интуитивную систему **<abbr title="также известно как: компоненты, ресурсы, провайдеры, сервисы, внедряемые зависимости">Инъекция зависимостей</abbr>**.
Она проектировалась таким образом, чтобы быть простой в использовании и облегчить любому разработчику интеграцию других компонентов с **FastAPI**.
Она спроектирована так, чтобы быть очень простой в использовании и облегчать любому разработчику интеграцию других компонентов с **FastAPI**.
## Что такое "Dependency Injection" (инъекция зависимости)
## Что такое инъекция зависимостей («Dependency Injection») { #what-is-dependency-injection }
**"Dependency Injection"** в программировании означает, что у вашего кода (в данном случае, вашей *функции обработки пути*) есть способы объявить вещи, которые запрашиваются для работы и использования: "зависимости".
В программировании **«Dependency Injection»** означает, что у вашего кода (в данном случае у ваших *функций обработки пути*) есть способ объявить вещи, которые требуются для его работы и использования: «зависимости».
И потом эта система (в нашем случае **FastAPI**) организует всё, что требуется, чтобы обеспечить ваш код этой зависимостью (сделать "инъекцию" зависимости).
И затем эта система (в нашем случае **FastAPI**) позаботится о том, чтобы сделать всё необходимое для предоставления вашему коду этих зависимостей (сделать «инъекцию» зависимостей).
Это очень полезно, когда вам нужно:
* Обеспечить общую логику (один и тот же алгоритм снова и снова).
* Общее соединение с базой данных.
* Обеспечение безопасности, аутентификации, запроса роли и т.п.
* И многое другое.
* Разделять соединения с базой данных.
* Обеспечить безопасность, аутентификацию, требования к ролям и т. п.
* И многое другое...
Всё это минимизирует повторение кода.
Всё это при минимизации повторения кода.
## Первые шаги
## Первые шаги { #first-steps }
Давайте рассмотрим очень простой пример. Он настолько простой, что на данный момент почти бесполезный.
Давайте рассмотрим очень простой пример. Он настолько простой, что пока не очень полезен.
Но таким способом мы можем сфокусироваться на том, как же всё таки работает система **Dependency Injection**.
Но так мы сможем сосредоточиться на том, как работает система **Dependency Injection**.
### Создание зависимости или "зависимого"
Давайте для начала сфокусируемся на зависимостях.
### Создайте зависимость, или «dependable» (от чего что-то зависит) { #create-a-dependency-or-dependable }
Сначала сосредоточимся на зависимости.
Это просто функция, которая может принимать те же параметры, что и *функция обработки пути*:
Это просто функция, которая может принимать все те же параметры, что и *функции обработки пути*:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *}
**И всё.**
И всё.
**2 строки.**
И теперь она той же формы и структуры, что и все ваши *функции обработки пути*.
И она имеет ту же форму и структуру, что и все ваши *функции обработки пути*.
Вы можете думать об *функции обработки пути* как о функции без "декоратора" (без `@app.get("/some-path")`).
Можно думать о ней как о *функции обработки пути* без «декоратора» (без `@app.get("/some-path")`).
И она может возвращать всё, что требуется.
И она может возвращать что угодно.
В этом случае, эта зависимость ожидает:
В этом случае эта зависимость ожидает:
* Необязательный query-параметр `q` с типом `str`
* Необязательный query-параметр `skip` с типом `int`, и значением по умолчанию `0`
* Необязательный query-параметр `limit` с типом `int`, и значением по умолчанию `100`
* Необязательный query-параметр `q` типа `str`.
* Необязательный query-параметр `skip` типа `int`, по умолчанию `0`.
* Необязательный query-параметр `limit` типа `int`, по умолчанию `100`.
И в конце она возвращает `dict`, содержащий эти значения.
А затем просто возвращает `dict`, содержащий эти значения.
/// info | Информация
**FastAPI** добавил поддержку для `Annotated` (и начал её рекомендовать) в версии 0.95.0.
FastAPI добавил поддержку `Annotated` (и начал рекомендовать его использование) в версии 0.95.0.
Если у вас более старая версия, будут ошибки при попытке использовать `Annotated`.
Если у вас более старая версия, вы получите ошибки при попытке использовать `Annotated`.
Убедитесь, что вы [Обновили FastAPI версию](../../deployment/versions.md#fastapi_2){.internal-link target=_blank} до, как минимум 0.95.1, перед тем как использовать `Annotated`.
Убедитесь, что вы [обновили версию FastAPI](../../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} как минимум до 0.95.1, прежде чем использовать `Annotated`.
///
### Import `Depends`
### Импорт `Depends` { #import-depends }
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *}
### Объявите зависимость в "зависимом"
### Объявите зависимость в «зависимом» { #declare-the-dependency-in-the-dependant }
Точно так же, как вы использовали `Body`, `Query` и т.д. с вашей *функцией обработки пути* для параметров, используйте `Depends` с новым параметром:
Точно так же, как вы используете `Body`, `Query` и т. д. с параметрами вашей *функции обработки пути*, используйте `Depends` с новым параметром:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *}
`Depends` работает немного иначе. Вы передаёте в `Depends` одиночный параметр, который будет похож на функцию.
Хотя вы используете `Depends` в параметрах вашей функции так же, как `Body`, `Query` и т. д., `Depends` работает немного иначе.
В `Depends` вы передаёте только один параметр.
Этот параметр должен быть чем-то вроде функции.
Вы **не вызываете его** на месте (не добавляете скобочки в конце: 👎 *your_best_func()*👎), просто передаёте как параметр в `Depends()`.
Вы **не вызываете её** напрямую (не добавляйте круглые скобки в конце), просто передаёте её как параметр в `Depends()`.
И потом функция берёт параметры так же, как *функция обработки пути*.
И эта функция принимает параметры так же, как *функции обработки пути*.
/// tip | Подсказка
В следующей главе вы увидите, какие другие вещи, помимо функций, можно использовать в качестве зависимостей.
В следующей главе вы увидите, какие ещё «вещи», помимо функций, можно использовать в качестве зависимостей.
///
Каждый раз, когда новый запрос приходит, **FastAPI** позаботится о:
Каждый раз, когда приходит новый запрос, **FastAPI** позаботится о:
* Вызове вашей зависимости ("зависимого") функции с корректными параметрами.
* Вызове вашей зависимости («dependable») с корректными параметрами.
* Получении результата из вашей функции.
* Назначении результата в параметр в вашей *функции обработки пути*.
* Присваивании этого результата параметру в вашей *функции обработки пути*.
```mermaid
graph TB
@ -98,121 +104,121 @@ common_parameters --> read_items
common_parameters --> read_users
```
Таким образом, вы пишете общий код один раз, и **FastAPI** позаботится о его вызове для ваших *операций с путями*.
Таким образом, вы пишете общий код один раз, а **FastAPI** позаботится о его вызове для ваших *операций пути*.
/// check | Проверка
Обратите внимание, что вы не создаёте специальный класс и не передаёте его куда-то в **FastAPI** для регистрации, или что-то в этом роде.
Обратите внимание, что вам не нужно создавать специальный класс и передавать его куда-то в **FastAPI**, чтобы «зарегистрировать» его или что-то подобное.
Вы просто передаёте это в `Depends`, и **FastAPI** знает, что делать дальше.
Вы просто передаёте его в `Depends`, и **FastAPI** знает, что делать дальше.
///
## Объединяем с `Annotated` зависимостями
## Использование зависимости с `Annotated` в нескольких местах { #share-annotated-dependencies }
В приведенном выше примере есть небольшое **повторение кода**.
В приведённых выше примерах есть небольшое **повторение кода**.
Когда вам нужно использовать `common_parameters()` зависимость, вы должны написать весь параметр с аннотацией типов и `Depends()`:
Когда вам нужно использовать зависимость `common_parameters()`, вы должны написать весь параметр с аннотацией типа и `Depends()`:
```Python
commons: Annotated[dict, Depends(common_parameters)]
```
Но потому что мы используем `Annotated`, мы можем хранить `Annotated` значение в переменной и использовать его в нескольких местах:
Но поскольку мы используем `Annotated`, мы можем сохранить это значение `Annotated` в переменную и использовать его в нескольких местах:
{* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *}
/// tip | Подсказка
Это стандартный синтаксис python и называется "type alias", это не особенность **FastAPI**.
Это стандартный Python, это называется «type alias», и это не особенность **FastAPI**.
Но потому что **FastAPI** базируется на стандартах Python, включая `Annotated`, вы можете использовать этот трюк в вашем коде. 😎
Но поскольку **FastAPI** основан на стандартах Python, включая `Annotated`, вы можете использовать этот трюк в своём коде. 😎
///
Зависимости продолжат работу как ожидалось, и **лучшая часть** в том, что **информация о типе будет сохранена**. Это означает, что ваш редактор кода будет корректно обрабатывать **автодополнения**, **встроенные ошибки** и так далее. То же самое относится и к инструментам, таким как `mypy`.
Зависимости продолжат работать как ожидалось, и **лучшая часть** в том, что **информация о типах будет сохранена**, а значит, ваш редактор кода продолжит предоставлять **автозавершение**, **встроенные ошибки** и т.д. То же относится и к другим инструментам, таким как `mypy`.
Это очень полезно, когда вы интегрируете это в **большую кодовую базу**, используя **одинаковые зависимости** снова и снова во **многих** ***операциях пути***.
Это особенно полезно, когда вы используете это в **большой кодовой базе**, где вы используете **одни и те же зависимости** снова и снова во **многих *операциях пути***.
## Использовать `async` или не `async`
## Использовать `async` или не `async` { #to-async-or-not-to-async }
Для зависимостей, вызванных **FastAPI** (то же самое, что и ваши *функции обработки пути*), те же правила, что приняты для определения ваших функций.
Поскольку зависимости также вызываются **FastAPI** (как и ваши *функции обработки пути*), применяются те же правила при определении ваших функций.
Вы можете использовать `async def` или обычное `def`.
Вы также можете объявить зависимости с `async def` внутри обычной `def` *функции обработки пути*, или `def` зависимости внутри `async def` *функции обработки пути*, и так далее.
И вы можете объявлять зависимости с `async def` внутри обычных *функций обработки пути* `def`, или зависимости `def` внутри *функций обработки пути* `async def` и т. д.
Это всё не важно. **FastAPI** знает, что нужно сделать. 😎
Это не важно. **FastAPI** знает, что делать.
/// note | Информация
/// note | Примечание
Если вам эта тема не знакома, прочтите [Async: *"In a hurry?"*](../../async.md){.internal-link target=_blank} раздел о `async` и `await` в документации.
Если вы не уверены, посмотрите раздел [Async: *"In a hurry?"*](../../async.md#in-a-hurry){.internal-link target=_blank} о `async` и `await` в документации.
///
## Интеграция с OpenAPI
## Интеграция с OpenAPI { #integrated-with-openapi }
Все заявления о запросах, валидаторы, требования ваших зависимостей (и подзависимостей) будут интегрированы в соответствующую OpenAPI-схему.
Все объявления запросов, проверки и требования ваших зависимостей (и подзависимостей) будут интегрированы в ту же схему OpenAPI.
В интерактивной документации будет вся информация по этим зависимостям тоже:
Поэтому в интерактивной документации будет вся информация и из этих зависимостей:
<img src="/img/tutorial/dependencies/image01.png">
## Простое использование
## Простое использование { #simple-usage }
Если вы посмотрите на фото, *функция обработки пути* объявляется каждый раз, когда вычисляется путь, и тогда **FastAPI** позаботится о вызове функции с корректными параметрами, извлекая информацию из запроса.
Если посмотреть, *функции обработки пути* объявляются для использования всякий раз, когда *путь* и *операция* совпадают, и тогда **FastAPI** заботится о вызове функции с корректными параметрами, извлекая данные из запроса.
На самом деле, все (или большинство) веб-фреймворков работают по схожему сценарию.
На самом деле все (или большинство) веб-фреймворков работают таким же образом.
Вы никогда не вызываете эти функции на месте. Их вызовет ваш фреймворк (в нашем случае, **FastAPI**).
Вы никогда не вызываете эти функции напрямую. Их вызывает ваш фреймворк (в нашем случае **FastAPI**).
С системой Dependency Injection, вы можете сообщить **FastAPI**, что ваша *функция обработки пути* "зависит" от чего-то ещё, что должно быть извлечено перед вашей *функцией обработки пути*, и **FastAPI** позаботится об извлечении и инъекции результата.
С системой **Dependency Injection** вы также можете сообщить **FastAPI**, что ваша *функция обработки пути* «зависит» от чего-то, что должно быть выполнено перед вашей *функцией обработки пути*, и **FastAPI** позаботится о его выполнении и «инъекции» результатов.
Другие распространённые термины для описания схожей идеи "dependency injection" являются:
Другие распространённые термины для описания той же идеи «dependency injection»:
- ресурсность
- доставка
- сервисность
- инъекция
- компонентность
* ресурсы
* провайдеры
* сервисы
* внедряемые зависимости
* компоненты
## **FastAPI** подключаемые модули
## Плагины **FastAPI** { #fastapi-plug-ins }
Инъекции и модули могут быть построены с использованием системы **Dependency Injection**. Но на самом деле, **нет необходимости создавать новые модули**, просто используя зависимости, можно объявить бесконечное количество интеграций и взаимодействий, которые доступны вашей *функции обработки пути*.
Интеграции и «плагины» могут быть построены с использованием системы **Dependency Injection**. Но на самом деле **нет необходимости создавать «плагины»**, так как, используя зависимости, можно объявить бесконечное количество интеграций и взаимодействий, которые становятся доступными вашим *функциям обработки пути*.
И зависимости могут быть созданы очень простым и интуитивным способом, что позволяет вам просто импортировать нужные пакеты Python и интегрировать их в API функции за пару строк.
И зависимости можно создавать очень простым и интуитивным способом, который позволяет просто импортировать нужные пакеты Python и интегрировать их с вашими API-функциями в пару строк кода, *буквально*.
Вы увидите примеры этого в следующих главах о реляционных и NoSQL базах данных, безопасности и т.д.
## Совместимость с **FastAPI**
## Совместимость с **FastAPI** { #fastapi-compatibility }
Простота Dependency Injection делает **FastAPI** совместимым с:
Простота системы **Dependency Injection** делает **FastAPI** совместимым с:
- всеми реляционными базами данных
- NoSQL базами данных
- внешними пакетами
- внешними API
- системами авторизации, аутентификации
- системами мониторинга использования API
- системами ввода данных ответов
- и так далее.
* всеми реляционными базами данных
* NoSQL базами данных
* внешними пакетами
* внешними API
* системами аутентификации и авторизации
* системами мониторинга использования API
* системами инъекции данных в ответы
* и т.д.
## Просто и сильно
## Просто и мощно { #simple-and-powerful }
Хотя иерархическая система Dependency Injection очень проста для описания и использования, она по-прежнему очень мощная.
Хотя иерархическая система dependency injection очень проста для определения и использования, она по-прежнему очень мощная.
Вы можете описывать зависимости в очередь, и они уже будут вызываться друг за другом.
Вы можете определять зависимости, которые, в свою очередь, могут иметь собственные зависимости.
Когда иерархическое дерево построено, система **Dependency Injection** берет на себя решение всех зависимостей для вас (и их подзависимостей) и обеспечивает (инъектирует) результат на каждом шаге.
В итоге строится иерархическое дерево зависимостей, и система **Dependency Injection** берёт на себя решение всех этих зависимостей (и их подзависимостей) и предоставляет (инъектирует) результаты на каждом шаге.
Например, у вас есть 4 API-эндпоинта (*операции пути*):
- `/items/public/`
- `/items/private/`
- `/users/{user_id}/activate`
- `/items/pro/`
* `/items/public/`
* `/items/private/`
* `/users/{user_id}/activate`
* `/items/pro/`
Тогда вы можете требовать разные права для каждого из них, используя зависимости и подзависимости:
тогда вы можете добавить разные требования к правам для каждого из них только с помощью зависимостей и подзависимостей:
```mermaid
graph TB
@ -237,8 +243,8 @@ admin_user --> activate_user
paying_user --> pro_items
```
## Интегрировано с **OpenAPI**
## Интегрировано с **OpenAPI** { #integrated-with-openapi_1 }
Все эти зависимости, объявляя свои требования, также добавляют параметры, проверки и т.д. к вашим операциям *path*.
Все эти зависимости, объявляя свои требования, также добавляют параметры, проверки и т.д. к вашим *операциям пути*.
**FastAPI** позаботится о добавлении всего этого в схему открытого API, чтобы это отображалось в системах интерактивной документации.
**FastAPI** позаботится о добавлении всего этого в схему OpenAPI, чтобы это отображалось в системах интерактивной документации.

18
docs/ru/docs/tutorial/dependencies/sub-dependencies.md

@ -1,4 +1,4 @@
# Подзависимости
# Подзависимости { #sub-dependencies }
Вы можете создавать зависимости, которые имеют **подзависимости**.
@ -6,7 +6,7 @@
**FastAPI** сам займётся их управлением.
## Провайдер зависимости
## Первая зависимость { #first-dependency-dependable }
Можно создать первую зависимость следующим образом:
@ -16,9 +16,9 @@
Это довольно просто (хотя и не очень полезно), но поможет нам сосредоточиться на том, как работают подзависимости.
## Вторая зависимость
## Вторая зависимость, «зависимость» и «зависимая» { #second-dependency-dependable-and-dependant }
Затем можно создать еще одну функцию зависимости, которая в то же время содержит внутри себя первую зависимость (таким образом, она тоже является "зависимой"):
Затем можно создать еще одну функцию зависимости, которая одновременно объявляет свою собственную зависимость (таким образом, она тоже является «зависимой»):
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *}
@ -29,7 +29,7 @@
* Она также объявляет необязательный куки-параметр `last_query` в виде строки.
* Если пользователь не указал параметр `q` в запросе, то мы используем последний использованный запрос, который мы ранее сохранили в куки-параметре `last_query`.
## Использование зависимости
## Использование зависимости { #use-the-dependency }
Затем мы можем использовать зависимость вместе с:
@ -54,7 +54,7 @@ read_query["/items/"]
query_extractor --> query_or_cookie_extractor --> read_query
```
## Использование одной и той же зависимости несколько раз
## Использование одной и той же зависимости несколько раз { #using-the-same-dependency-multiple-times }
Если одна из ваших зависимостей объявлена несколько раз для одной и той же *функции операции пути*, например, несколько зависимостей имеют общую подзависимость, **FastAPI** будет знать, что вызывать эту подзависимость нужно только один раз за запрос.
@ -62,7 +62,7 @@ query_extractor --> query_or_cookie_extractor --> read_query
В расширенном сценарии, когда вы знаете, что вам нужно, чтобы зависимость вызывалась на каждом шаге (возможно, несколько раз) в одном и том же запросе, вместо использования "кэшированного" значения, вы можете установить параметр `use_cache=False` при использовании `Depends`:
//// tab | Python 3.6+
//// tab | Python 3.8+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
@ -71,7 +71,7 @@ async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_ca
////
//// tab | Python 3.6+ без Annotated
//// tab | Python 3.8+ без Annotated
/// tip | Подсказка
@ -86,7 +86,7 @@ async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False
////
## Резюме
## Резюме { #recap }
Помимо всех этих умных слов, используемых здесь, система внедрения зависимостей довольно проста.

12
docs/ru/docs/tutorial/encoder.md

@ -1,4 +1,4 @@
# JSON кодировщик
# JSON-совместимый кодировщик { #json-compatible-encoder }
В некоторых случаях может потребоваться преобразование типа данных (например, Pydantic-модели) в тип, совместимый с JSON (например, `dict`, `list` и т.д.).
@ -6,7 +6,7 @@
Для этого **FastAPI** предоставляет функцию `jsonable_encoder()`.
## Использование `jsonable_encoder`
## Использование `jsonable_encoder` { #using-the-jsonable-encoder }
Представим, что у вас есть база данных `fake_db`, которая принимает только JSON-совместимые данные.
@ -14,21 +14,21 @@
В таком случае объект `datetime` следует преобразовать в строку соответствующую <a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">формату ISO</a>.
Точно так же эта база данных не может принять Pydantic модель (объект с атрибутами), а только `dict`.
Точно так же эта база данных не может принять Pydantic-модель (объект с атрибутами), а только `dict`.
Для этого можно использовать функцию `jsonable_encoder`.
Она принимает объект, например, модель Pydantic, и возвращает его версию, совместимую с JSON:
Она принимает объект, например, Pydantic-модель, и возвращает его версию, совместимую с JSON:
{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *}
В данном примере она преобразует Pydantic модель в `dict`, а `datetime` - в `str`.
В данном примере она преобразует Pydantic-модель в `dict`, а `datetime` - в `str`.
Результатом её вызова является объект, который может быть закодирован с помощью функции из стандартной библиотеки Python – <a href="https://docs.python.org/3/library/json.html#json.dumps" class="external-link" target="_blank">`json.dumps()`</a>.
Функция не возвращает большой `str`, содержащий данные в формате JSON (в виде строки). Она возвращает стандартную структуру данных Python (например, `dict`) со значениями и подзначениями, которые совместимы с JSON.
/// note | Технические детали
/// note | Примечание
`jsonable_encoder` фактически используется **FastAPI** внутри системы для преобразования данных. Однако он полезен и во многих других сценариях.

18
docs/ru/docs/tutorial/extra-data-types.md

@ -1,4 +1,4 @@
# Дополнительные типы данных
# Дополнительные типы данных { #extra-data-types }
До сих пор вы использовали простые типы данных, такие как:
@ -9,15 +9,15 @@
Но вы также можете использовать и более сложные типы.
При этом у вас останутся те же возможности , что и до сих пор:
При этом у вас останутся те же возможности, что и до сих пор:
* Отличная поддержка редактора.
* Отличная поддержка редактора кода.
* Преобразование данных из входящих запросов.
* Преобразование данных для ответа.
* Валидация данных.
* Автоматическая аннотация и документация.
## Другие типы данных
## Другие типы данных { #other-data-types }
Ниже перечислены некоторые из дополнительных типов данных, которые вы можете использовать:
@ -36,7 +36,7 @@
* `datetime.timedelta`:
* Встроенный в Python `datetime.timedelta`.
* В запросах и ответах будет представлен в виде общего количества секунд типа `float`.
* Pydantic также позволяет представить его как "Кодировку разницы во времени ISO 8601", <a href="https://docs.pydantic.dev/latest/concepts/serialization/#json_encoders" class="external-link" target="_blank">см. документацию для получения дополнительной информации</a>.
* Pydantic также позволяет представить его как "Кодировку разницы во времени ISO 8601", <a href="https://docs.pydantic.dev/latest/concepts/serialization/#custom-serializers" class="external-link" target="_blank">см. документацию для получения дополнительной информации</a>.
* `frozenset`:
* В запросах и ответах обрабатывается так же, как и `set`:
* В запросах будет прочитан список, исключены дубликаты и преобразован в `set`.
@ -49,14 +49,14 @@
* `Decimal`:
* Встроенный в Python `Decimal`.
* В запросах и ответах обрабатывается так же, как и `float`.
* Вы можете проверить все допустимые типы данных pydantic здесь: <a href="https://docs.pydantic.dev/latest/concepts/types/" class="external-link" target="_blank">Типы данных Pydantic</a>.
* Вы можете проверить все допустимые типы данных Pydantic здесь: <a href="https://docs.pydantic.dev/latest/usage/types/types/" class="external-link" target="_blank">Типы данных Pydantic</a>.
## Пример
## Пример { #example }
Вот пример *операции пути* с параметрами, который демонстрирует некоторые из вышеперечисленных типов.
{* ../../docs_src/extra_data_types/tutorial001.py hl[1,3,12:16] *}
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *}
Обратите внимание, что параметры внутри функции имеют свой естественный тип данных, и вы, например, можете выполнять обычные манипуляции с датами, такие как:
{* ../../docs_src/extra_data_types/tutorial001.py hl[18:19] *}
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[18:19] *}

40
docs/ru/docs/tutorial/extra-models.md

@ -1,4 +1,4 @@
# Дополнительные модели
# Дополнительные модели { #extra-models }
В продолжение прошлого примера будет уже обычным делом иметь несколько связанных между собой моделей.
@ -16,15 +16,23 @@
///
## Множественные модели
## Множественные модели { #multiple-models }
Ниже изложена основная идея того, как могут выглядеть эти модели с полями для паролей, а также описаны места, где они используются:
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
### Про `**user_in.dict()`
/// info | Информация
#### `.dict()` из Pydantic
В Pydantic v1 метод назывался `.dict()`, в Pydantic v2 он помечен как устаревший (но всё ещё поддерживается) и переименован в `.model_dump()`.
В примерах здесь используется `.dict()` для совместимости с Pydantic v1, но если вы используете Pydantic v2, следует использовать `.model_dump()`.
///
### Про `**user_in.dict()` { #about-user-in-dict }
#### `.dict()` из Pydantic { #pydantics-dict }
`user_in` - это Pydantic-модель класса `UserIn`.
@ -61,7 +69,7 @@ print(user_dict)
}
```
#### Распаковка `dict`
#### Распаковка `dict` { #unpacking-a-dict }
Если мы возьмём `dict` наподобие `user_dict` и передадим его в функцию (или класс), используя `**user_dict`, Python распакует его. Он передаст ключи и значения `user_dict` напрямую как аргументы типа ключ-значение.
@ -93,7 +101,7 @@ UserInDB(
)
```
#### Pydantic-модель из содержимого другой модели
#### Pydantic-модель из содержимого другой модели { #a-pydantic-model-from-the-contents-of-another }
Как в примере выше мы получили `user_dict` из `user_in.dict()`, этот код:
@ -112,7 +120,7 @@ UserInDB(**user_in.dict())
Таким образом мы получаем Pydantic-модель на основе данных из другой Pydantic-модели.
#### Распаковка `dict` и дополнительные именованные аргументы
#### Распаковка `dict` и дополнительные именованные аргументы { #unpacking-a-dict-and-extra-keywords }
И затем, если мы добавим дополнительный именованный аргумент `hashed_password=hashed_password` как здесь:
@ -134,11 +142,11 @@ UserInDB(
/// warning | Предупреждение
Цель использованных в примере вспомогательных функций - не более чем демонстрация возможных операций с данными, но, конечно, они не обеспечивают настоящую безопасность.
Вспомогательные функции `fake_password_hasher` и `fake_save_user` используются только для демонстрации возможного потока данных и, конечно, не обеспечивают настоящую безопасность.
///
## Сократите дублирование
## Сократите дублирование { #reduce-duplication }
Сокращение дублирования кода - это одна из главных идей **FastAPI**.
@ -156,13 +164,13 @@ UserInDB(
{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *}
## `Union` или `anyOf`
## `Union` или `anyOf` { #union-or-anyof }
Вы можете определить ответ как `Union` из двух типов. Это означает, что ответ должен соответствовать одному из них.
Вы можете определить ответ как `Union` из двух или более типов. Это означает, что ответ должен соответствовать одному из них.
Он будет определён в OpenAPI как `anyOf`.
Для этого используйте стандартные аннотации типов в Python <a href="https://docs.python.org/3/library/typing.html#typing.Union" class="external-link" target="_blank">`typing.Union`</a>:
Для этого используйте стандартную аннотацию типов в Python <a href="https://docs.python.org/3/library/typing.html#typing.Union" class="external-link" target="_blank">`typing.Union`</a>:
/// note | Примечание
@ -172,7 +180,7 @@ UserInDB(
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *}
### `Union` в Python 3.10
### `Union` в Python 3.10 { #union-in-python-3-10 }
В этом примере мы передаём `Union[PlaneItem, CarItem]` в качестве значения аргумента `response_model`.
@ -186,7 +194,7 @@ some_variable: PlaneItem | CarItem
Но если мы помещаем его в `response_model=PlaneItem | CarItem` мы получим ошибку, потому что Python попытается произвести **некорректную операцию** между `PlaneItem` и `CarItem` вместо того, чтобы интерпретировать это как аннотацию типа.
## Список моделей
## Список моделей { #list-of-models }
Таким же образом вы можете определять ответы как списки объектов.
@ -194,7 +202,7 @@ some_variable: PlaneItem | CarItem
{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *}
## Ответ с произвольным `dict`
## Ответ с произвольным `dict` { #response-with-arbitrary-dict }
Вы также можете определить ответ, используя произвольный одноуровневый `dict` и определяя только типы ключей и значений без использования Pydantic-моделей.
@ -204,7 +212,7 @@ some_variable: PlaneItem | CarItem
{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *}
## Резюме
## Резюме { #recap }
Используйте несколько Pydantic-моделей и свободно применяйте наследование для каждой из них.

260
docs/ru/docs/tutorial/first-steps.md

@ -1,106 +1,122 @@
# Первые шаги
# Первые шаги { #first-steps }
Самый простой FastAPI файл может выглядеть так:
Самый простой файл FastAPI может выглядеть так:
{* ../../docs_src/first_steps/tutorial001.py *}
Скопируйте в файл `main.py`.
Скопируйте это в файл `main.py`.
Запустите сервер в режиме реального времени:
<div class="termy">
```console
$ uvicorn main:app --reload
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid">main.py</u>
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
<span style="color: green;">INFO</span>: Started reloader process [28720]
<span style="color: green;">INFO</span>: Started server process [28722]
<span style="color: green;">INFO</span>: Waiting for application startup.
<span style="color: green;">INFO</span>: Application startup complete.
```
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting development server 🚀
</div>
Searching for package file structure from directories
with <font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
/// note | Технические детали
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
Команда `uvicorn main:app` обращается к:
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with
the following code:
* `main`: файл `main.py` (модуль Python).
* `app`: объект, созданный внутри файла `main.py` в строке `app = FastAPI()`.
* `--reload`: перезапускает сервер после изменения кода. Используйте только для разработки.
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
///
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> tip </font></span> Running in development mode, for production use:
<b>fastapi run</b>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Will watch for changes in these directories:
<b>[</b><font color="#4E9A06">&apos;/home/user/code/awesomeapp&apos;</font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font> <b>(</b>Press CTRL+C
to quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started reloader process <b>[</b><font color="#34E2E2"><b>383138</b></font><b>]</b> using WatchFiles
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>383153</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
```
</div>
В окне вывода появится следующая строка:
В выводе будет строка примерно такого вида:
```hl_lines="4"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Эта строка показывает URL-адрес, по которому приложение доступно на локальной машине.
Эта строка показывает URL, по которому ваше приложение доступно на локальной машине.
### Проверьте
### Проверьте { #check-it }
Откройте браузер по адресу: <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
Вы увидите JSON-ответ следующего вида:
Вы увидите JSON-ответ вида:
```JSON
{"message": "Hello World"}
```
### Интерактивная документация API
### Интерактивная документация API { #interactive-api-docs }
Перейдите по адресу: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Теперь перейдите по адресу: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Вы увидите автоматически сгенерированную, интерактивную документацию по API (предоставленную <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
Вы увидите автоматически сгенерированную интерактивную документацию по API (предоставлено <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### Альтернативная документация API
### Альтернативная документация API { #alternative-api-docs }
Теперь перейдите по адресу <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
И теперь перейдите по адресу <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
Вы увидите альтернативную автоматически сгенерированную документацию (предоставленную <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
Вы увидите альтернативную автоматически сгенерированную документацию (предоставлено <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
### OpenAPI
### OpenAPI { #openapi }
**FastAPI** генерирует "схему" всего API, используя стандарт **OpenAPI**.
**FastAPI** генерирует «схему» всего вашего API, используя стандарт **OpenAPI** для описания API.
#### "Схема"
#### «Схема» { #schema }
"Схема" - это определение или описание чего-либо. Не код, реализующий это, а только абстрактное описание.
«Схема» — это определение или описание чего-либо. Не код, который это реализует, а только абстрактное описание.
#### API "схема"
#### «Схема» API { #api-schema }
<a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> - это спецификация, которая определяет, как описывать схему API.
В данном случае <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> это спецификация, которая определяет, как описывать схему вашего API.
Определение схемы содержит пути (paths) API, их параметры и т.п.
Это определение схемы включает пути вашего API, возможные параметры, которые они принимают, и т. п.
#### "Схема" данных
#### «Схема» данных { #data-schema }
Термин "схема" также может относиться к формату или структуре некоторых данных, например, JSON.
Термин «схема» также может относиться к форме некоторых данных, например, к содержимому JSON.
Тогда, подразумеваются атрибуты JSON, их типы данных и т.п.
В таком случае это будут атрибуты JSON, их типы данных и т. п.
#### OpenAPI и JSON Schema
#### OpenAPI и JSON Schema { #openapi-and-json-schema }
OpenAPI описывает схему API. Эта схема содержит определения (или "схемы") данных, отправляемых и получаемых API. Для описания структуры данных в JSON используется стандарт **JSON Schema**.
OpenAPI определяет схему API для вашего API. И эта схема включает определения (или «схемы») данных, отправляемых и получаемых вашим API, с использованием стандарта **JSON Schema** для схем данных JSON.
#### Рассмотрим `openapi.json`
#### Посмотрите `openapi.json` { #check-the-openapi-json }
Если Вас интересует, как выглядит исходная схема OpenAPI, то FastAPI автоматически генерирует JSON-схему со всеми описаниями API.
Если вам интересно, как выглядит исходная схема OpenAPI, FastAPI автоматически генерирует JSON (схему) с описанием всего вашего API.
Можете посмотреть здесь: <a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a>.
Вы можете посмотреть её напрямую по адресу: <a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a>.
Вы увидите примерно такой JSON:
Вы увидите JSON, начинающийся примерно так:
```JSON
{
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
@ -119,151 +135,123 @@ OpenAPI описывает схему API. Эта схема содержит о
...
```
#### Для чего нужен OpenAPI
#### Для чего нужен OpenAPI { #what-is-openapi-for }
Схема OpenAPI является основой для обеих систем интерактивной документации.
Схема OpenAPI является основой для обеих включённых систем интерактивной документации.
Существуют десятки альтернативных инструментов, основанных на OpenAPI. Вы можете легко добавить любой из них к **FastAPI** приложению.
Есть десятки альтернатив, все основаны на OpenAPI. Вы можете легко добавить любую из них в ваше приложение, созданное с **FastAPI**.
Вы также можете использовать OpenAPI для автоматической генерации кода для клиентов, которые взаимодействуют с API. Например, для фронтенд-, мобильных или IoT-приложений.
Вы также можете использовать её для автоматической генерации кода для клиентов, которые взаимодействуют с вашим API. Например, для фронтенд-, мобильных или IoT-приложений.
## Рассмотрим поэтапно
## Рассмотрим поэтапно { #recap-step-by-step }
### Шаг 1: импортируйте `FastAPI`
### Шаг 1: импортируйте `FastAPI` { #step-1-import-fastapi }
{* ../../docs_src/first_steps/tutorial001.py hl[1] *}
`FastAPI` это класс в Python, который предоставляет всю функциональность для API.
`FastAPI` — это класс на Python, который предоставляет всю функциональность для вашего API.
/// note | Технические детали
`FastAPI` это класс, который наследуется непосредственно от `Starlette`.
`FastAPI` это класс, который напрямую наследуется от `Starlette`.
Вы можете использовать всю функциональность <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> в `FastAPI`.
Вы можете использовать весь функционал <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> и в `FastAPI`.
///
### Шаг 2: создайте экземпляр `FastAPI`
### Шаг 2: создайте экземпляр `FastAPI` { #step-2-create-a-fastapi-instance }
{* ../../docs_src/first_steps/tutorial001.py hl[3] *}
Переменная `app` является экземпляром класса `FastAPI`.
Это единая точка входа для создания и взаимодействия с API.
Именно к этой переменной `app` обращается `uvicorn` в команде:
<div class="termy">
```console
$ uvicorn main:app --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Если создать такое приложение:
{* ../../docs_src/first_steps/tutorial002.py hl[3] *}
И поместить его в `main.py`, тогда вызов `uvicorn` будет таким:
Здесь переменная `app` будет экземпляром класса `FastAPI`.
<div class="termy">
```console
$ uvicorn main:my_awesome_api --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Это будет основная точка взаимодействия для создания всего вашего API.
### Шаг 3: определите *операцию пути (path operation)*
### Шаг 3: создайте *операцию пути (path operation)* { #step-3-create-a-path-operation }
#### Путь (path)
#### Путь (path) { #path }
"Путь" это часть URL, после первого символа `/`, следующего за именем домена.
Здесь «путь» — это последняя часть URL, начиная с первого символа `/`.
Для URL:
Итак, в таком URL:
```
https://example.com/items/foo
```
...путь выглядит так:
...путь будет:
```
/items/foo
```
/// info | Дополнительная иформация
/// info | Информация
Термин "path" также часто называется "endpoint" или "route".
«Путь» также часто называют «эндпоинт» или «маршрут».
///
При создании API, "путь" является основным способом разделения "задач" и "ресурсов".
При создании API «путь» — это основной способ разделения «задач» и «ресурсов».
#### Операция (operation)
#### Операция (operation) { #operation }
"Операция" это один из "методов" HTTP.
«Операция» здесь — это один из HTTP-«методов».
Таких, как:
Один из:
* `POST`
* `GET`
* `PUT`
* `DELETE`
...и более экзотических:
...и более экзотические:
* `OPTIONS`
* `HEAD`
* `PATCH`
* `TRACE`
По протоколу HTTP можно обращаться к каждому пути, используя один (или несколько) из этих "методов".
В протоколе HTTP можно обращаться к каждому пути, используя один (или несколько) из этих «методов».
---
При создании API принято использовать конкретные HTTP-методы для выполнения определенных действий.
При создании API обычно используют конкретные HTTP-методы для выполнения конкретных действий.
Обычно используют:
* `POST`: создать данные.
* `GET`: прочитать.
* `PUT`: изменить (обновить).
* `DELETE`: удалить.
* `GET`: прочитать данные.
* `PUT`: обновить данные.
* `DELETE`: удалить данные.
В OpenAPI каждый HTTP метод называется "**операция**".
Таким образом, в OpenAPI каждый HTTP-метод называется «операцией».
Мы также будем придерживаться этого термина.
Мы тоже будем называть их «операциями».
#### Определите *декоратор операции пути (path operation decorator)*
#### Определите *декоратор операции пути (path operation decorator)* { #define-a-path-operation-decorator }
{* ../../docs_src/first_steps/tutorial001.py hl[6] *}
Декоратор `@app.get("/")` указывает **FastAPI**, что функция, прямо под ним, отвечает за обработку запросов, поступающих по адресу:
`@app.get("/")` сообщает **FastAPI**, что функция прямо под ним отвечает за обработку запросов, поступающих:
* путь `/`
* использующих <abbr title="HTTP GET метод"><code>get</code> операцию</abbr>
* по пути `/`
* с использованием <abbr title="метод HTTP GET"><code>get</code> операции</abbr>
/// info | `@decorator` Дополнительная информация
/// info | Информация о `@decorator`
Синтаксис `@something` в Python называется "декоратор".
Синтаксис `@something` в Python называется «декоратор».
Вы помещаете его над функцией. Как красивую декоративную шляпу (думаю, что оттуда и происходит этот термин).
Его размещают над функцией. Как красивая декоративная шляпа (кажется, отсюда и пошёл термин).
"Декоратор" принимает функцию ниже и выполняет с ней какое-то действие.
«Декоратор» берёт функцию ниже и делает с ней что-то.
В нашем случае, этот декоратор сообщает **FastAPI**, что функция ниже соответствует **пути** `/` и **операции** `get`.
В нашем случае этот декоратор сообщает **FastAPI**, что функция ниже соответствует **пути** `/` с **операцией** `get`.
Это и есть "**декоратор операции пути**".
Это и есть «декоратор операции пути».
///
Можно также использовать операции:
Можно также использовать другие операции:
* `@app.post()`
* `@app.put()`
@ -278,58 +266,58 @@ https://example.com/items/foo
/// tip | Подсказка
Вы можете использовать каждую операцию (HTTP-метод) по своему усмотрению.
Вы можете использовать каждый метод (HTTP-операцию) так, как считаете нужным.
**FastAPI** не навязывает определенного значения для каждого метода.
**FastAPI** не навязывает какого-либо конкретного смысла.
Информация здесь представлена как рекомендация, а не требование.
Эта информация дана как рекомендация, а не требование.
Например, при использовании GraphQL обычно все действия выполняются только с помощью POST операций.
Например, при использовании GraphQL обычно все действия выполняются только с помощью POST-операций.
///
### Шаг 4: определите **функцию операции пути**
### Шаг 4: определите **функцию операции пути** { #step-4-define-the-path-operation-function }
Вот "**функция операции пути**":
Вот наша «функция операции пути»:
* **путь**: `/`.
* **операция**: `get`.
* **функция**: функция ниже "декоратора" (ниже `@app.get("/")`).
* **функция**: функция ниже «декоратора» (ниже `@app.get("/")`).
{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
Это обычная Python функция.
Это функция на Python.
**FastAPI** будет вызывать её каждый раз при получении `GET` запроса к URL "`/`".
**FastAPI** будет вызывать её каждый раз, когда получает запрос к URL «`/`» с операцией `GET`.
В данном случае это асинхронная функция.
В данном случае это асинхронная (`async`) функция.
---
Вы также можете определить ее как обычную функцию вместо `async def`:
Вы также можете определить её как обычную функцию вместо `async def`:
{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
/// note | Технические детали
/// note | Примечание
Если не знаете в чём разница, посмотрите [Конкурентность: *"Нет времени?"*](../async.md#_1){.internal-link target=_blank}.
Если вы не знаете, в чём разница, посмотрите [Асинхронность: *"Нет времени?"*](../async.md#in-a-hurry){.internal-link target=_blank}.
///
### Шаг 5: верните результат
### Шаг 5: верните содержимое { #step-5-return-the-content }
{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
Вы можете вернуть `dict`, `list`, отдельные значения `str`, `int` и т.д.
Также можно вернуть модели Pydantic (рассмотрим это позже).
Также можно вернуть модели Pydantic (подробнее об этом позже).
Многие объекты и модели будут автоматически преобразованы в JSON (включая ORM). Пробуйте использовать другие объекты, которые предпочтительней для Вас, вероятно, они уже поддерживаются.
Многие другие объекты и модели будут автоматически преобразованы в JSON (включая ORM и т. п.). Попробуйте использовать те, что вам привычнее, с высокой вероятностью они уже поддерживаются.
## Резюме
## Резюме { #recap }
* Импортируем `FastAPI`.
* Создаём экземпляр `app`.
* Пишем **декоратор операции пути** (такой как `@app.get("/")`).
* Пишем **функцию операции пути** (`def root(): ...`).
* Запускаем сервер в режиме разработки (`uvicorn main:app --reload`).
* Импортируйте `FastAPI`.
* Создайте экземпляр `app`.
* Напишите **декоратор операции пути**, например `@app.get("/")`.
* Определите **функцию операции пути**; например, `def root(): ...`.
* Запустите сервер разработки командой `fastapi dev`.

34
docs/ru/docs/tutorial/handling-errors.md

@ -1,4 +1,4 @@
# Обработка ошибок
# Обработка ошибок { #handling-errors }
Существует множество ситуаций, когда необходимо сообщить об ошибке клиенту, использующему ваш API.
@ -19,15 +19,15 @@
Помните ли ошибки **"404 Not Found "** (и шутки) ?
## Использование `HTTPException`
## Использование `HTTPException` { #use-httpexception }
Для возврата клиенту HTTP-ответов с ошибками используется `HTTPException`.
### Импортируйте `HTTPException`
### Импортируйте `HTTPException` { #import-httpexception }
{* ../../docs_src/handling_errors/tutorial001.py hl[1] *}
### Вызовите `HTTPException` в своем коде
### Вызовите `HTTPException` в своем коде { #raise-an-httpexception-in-your-code }
`HTTPException` - это обычное исключение Python с дополнительными данными, актуальными для API.
@ -41,7 +41,7 @@
{* ../../docs_src/handling_errors/tutorial001.py hl[11] *}
### Возвращаемый ответ
### Возвращаемый ответ { #the-resulting-response }
Если клиент запросит `http://example.com/items/foo` (`item_id` `"foo"`), то он получит статус-код 200 и ответ в формате JSON:
@ -69,7 +69,7 @@
///
## Добавление пользовательских заголовков
## Добавление пользовательских заголовков { #add-custom-headers }
В некоторых ситуациях полезно иметь возможность добавлять пользовательские заголовки к ошибке HTTP. Например, для некоторых типов безопасности.
@ -79,7 +79,7 @@
{* ../../docs_src/handling_errors/tutorial002.py hl[14] *}
## Установка пользовательских обработчиков исключений
## Установка пользовательских обработчиков исключений { #install-custom-exception-handlers }
Вы можете добавить пользовательские обработчики исключений с помощью <a href="https://www.starlette.io/exceptions/" class="external-link" target="_blank">то же самое исключение - утилиты от Starlette</a>.
@ -109,7 +109,7 @@
///
## Переопределение стандартных обработчиков исключений
## Переопределение стандартных обработчиков исключений { #override-the-default-exception-handlers }
**FastAPI** имеет некоторые обработчики исключений по умолчанию.
@ -117,7 +117,7 @@
Вы можете переопределить эти обработчики исключений на свои собственные.
### Переопределение исключений проверки запроса
### Переопределение исключений проверки запроса { #override-request-validation-exceptions }
Когда запрос содержит недопустимые данные, **FastAPI** внутренне вызывает ошибку `RequestValidationError`.
@ -154,7 +154,7 @@ path -> item_id
value is not a valid integer (type=type_error.integer)
```
#### `RequestValidationError` или `ValidationError`
#### `RequestValidationError` или `ValidationError` { #requestvalidationerror-vs-validationerror }
/// warning | Внимание
@ -172,7 +172,7 @@ path -> item_id
И пока вы не устраните ошибку, ваши клиенты/пользователи не должны иметь доступа к внутренней информации о ней, так как это может привести к уязвимости в системе безопасности.
### Переопределите обработчик ошибок `HTTPException`
### Переопределите обработчик ошибок `HTTPException` { #override-the-httpexception-error-handler }
Аналогичным образом можно переопределить обработчик `HTTPException`.
@ -188,7 +188,7 @@ path -> item_id
///
### Используйте тело `RequestValidationError`
### Используйте тело `RequestValidationError` { #use-the-requestvalidationerror-body }
Ошибка `RequestValidationError` содержит полученное `тело` с недопустимыми данными.
@ -226,21 +226,19 @@ path -> item_id
}
```
#### `HTTPException` в FastAPI или в Starlette
#### `HTTPException` в FastAPI или в Starlette { #fastapis-httpexception-vs-starlettes-httpexception }
**FastAPI** имеет собственный `HTTPException`.
Класс ошибок **FastAPI** `HTTPException` наследует от класса ошибок Starlette `HTTPException`.
Единственное отличие заключается в том, что `HTTPException` от **FastAPI** позволяет добавлять заголовки, которые будут включены в ответ.
Он необходим/используется внутри системы для OAuth 2.0 и некоторых утилит безопасности.
Единственное отличие состоит в том, что `HTTPException` в **FastAPI** принимает любые данные, пригодные для преобразования в JSON, в поле `detail`, тогда как `HTTPException` в Starlette принимает для него только строки.
Таким образом, вы можете продолжать вызывать `HTTPException` от **FastAPI** как обычно в своем коде.
Но когда вы регистрируете обработчик исключений, вы должны зарегистрировать его для `HTTPException` от Starlette.
Таким образом, если какая-либо часть внутреннего кода Starlette, расширение или плагин Starlette вызовет исключение Starlette `HTTPException`, ваш обработчик сможет перехватить и обработать его.
Таким образом, если какая-либо часть внутреннего кодa Starlette, расширение или плагин Starlette вызовет исключение Starlette `HTTPException`, ваш обработчик сможет перехватить и обработать его.
В данном примере, чтобы иметь возможность использовать оба `HTTPException` в одном коде, исключения Starlette переименованы в `StarletteHTTPException`:
@ -248,7 +246,7 @@ path -> item_id
from starlette.exceptions import HTTPException as StarletteHTTPException
```
### Переиспользование обработчиков исключений **FastAPI**
### Переиспользование обработчиков исключений **FastAPI** { #reuse-fastapis-exception-handlers }
Если вы хотите использовать исключение вместе с теми же обработчиками исключений по умолчанию из **FastAPI**, вы можете импортировать и повторно использовать обработчики исключений по умолчанию из `fastapi.exception_handlers`:

12
docs/ru/docs/tutorial/header-param-models.md

@ -1,4 +1,4 @@
# Модели Header-параметров
# Модели Header-параметров { #header-parameter-models }
Если у вас есть группа связанных **header-параметров**, то вы можете объединить их в одну **Pydantic-модель**.
@ -10,7 +10,7 @@
///
## Header-параметры в виде Pydantic-модели
## Header-параметры в виде Pydantic-модели { #header-parameters-with-a-pydantic-model }
Объявите нужные **header-параметры** в **Pydantic-модели** и затем аннотируйте параметр как `Header`:
@ -18,7 +18,7 @@
**FastAPI** **извлечёт** данные для **каждого поля** из **заголовков** запроса и выдаст заданную вами Pydantic-модель.
## Проверьте документацию
## Проверьте документацию { #check-the-docs }
Вы можете посмотреть нужные header-параметры в графическом интерфейсе сгенерированной документации по пути `/docs`:
@ -26,7 +26,7 @@
<img src="/img/tutorial/header-param-models/image01.png">
</div>
## Как запретить дополнительные заголовки
## Как запретить дополнительные заголовки { #forbid-extra-headers }
В некоторых случаях (не особо часто встречающихся) вам может понадобиться **ограничить** заголовки, которые вы хотите получать.
@ -51,7 +51,7 @@
}
```
## Как отключить автоматическое преобразование подчеркиваний
## Как отключить автоматическое преобразование подчеркиваний { #disable-convert-underscores }
Как и в случае с обычными заголовками, если у вас в именах параметров имеются символы подчеркивания, они **автоматически преобразовываются в дефис**.
@ -67,6 +67,6 @@
///
## Резюме
## Резюме { #summary }
Вы можете использовать **Pydantic-модели** для объявления **header-параметров** в **FastAPI**. 😎

14
docs/ru/docs/tutorial/header-params.md

@ -1,14 +1,14 @@
# Header-параметры
# Header-параметры { #header-parameters }
Вы можете определить параметры заголовка таким же образом, как вы определяете параметры `Query`, `Path` и `Cookie`.
## Импорт `Header`
## Импорт `Header` { #import-header }
Сперва импортируйте `Header`:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *}
## Объявление параметров `Header`
## Объявление параметров `Header` { #declare-header-parameters }
Затем объявите параметры заголовка, используя ту же структуру, что и с `Path`, `Query` и `Cookie`.
@ -24,13 +24,13 @@
///
/// info | Дополнительная информация
/// info | Информация
Чтобы объявить заголовки, важно использовать `Header`, иначе параметры интерпретируются как query-параметры.
///
## Автоматическое преобразование
## Автоматическое преобразование { #automatic-conversion }
`Header` обладает небольшой дополнительной функциональностью в дополнение к тому, что предоставляют `Path`, `Query` и `Cookie`.
@ -54,7 +54,7 @@
///
## Повторяющиеся заголовки
## Повторяющиеся заголовки { #duplicate-headers }
Есть возможность получать несколько заголовков с одним и тем же именем, но разными значениями.
@ -84,7 +84,7 @@ X-Token: bar
}
```
## Резюме
## Резюме { #recap }
Объявляйте заголовки с помощью `Header`, используя тот же общий шаблон, как при `Query`, `Path` и `Cookie`.

94
docs/ru/docs/tutorial/index.md

@ -1,83 +1,95 @@
# Учебник - Руководство пользователя
# Учебник - Руководство пользователя { #tutorial-user-guide }
В этом руководстве шаг за шагом показано, как использовать **FastApi** с большинством его функций.
В этом руководстве шаг за шагом показано, как использовать **FastAPI** с большинством его функций.
Каждый раздел постепенно основывается на предыдущих, но он структурирован по отдельным темам, так что вы можете перейти непосредственно к конкретной теме для решения ваших конкретных потребностей в API.
Каждый раздел постепенно основывается на предыдущих, но структура разделяет темы, так что вы можете сразу перейти к нужной теме для решения ваших конкретных задач по API.
Он также создан для использования в качестве будущего справочника.
Он также создан как справочник на будущее, чтобы вы могли вернуться и посмотреть именно то, что вам нужно.
Так что вы можете вернуться и посмотреть именно то, что вам нужно.
## Запустите код { #run-the-code }
## Запустите код
Все блоки кода можно копировать и использовать напрямую (это действительно протестированные файлы Python).
Все блоки кода можно копировать и использовать напрямую (на самом деле это проверенные файлы Python).
Чтобы запустить любой из примеров, скопируйте код в файл `main.py` и запустите `uvicorn` с параметрами:
Чтобы запустить любой из примеров, скопируйте код в файл `main.py` и запустите `fastapi dev` с:
<div class="termy">
```console
$ uvicorn main:app --reload
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid">main.py</u>
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting development server 🚀
Searching for package file structure from directories
with <font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with
the following code:
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> tip </font></span> Running in development mode, for production use:
<b>fastapi run</b>
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
<span style="color: green;">INFO</span>: Started reloader process [28720]
<span style="color: green;">INFO</span>: Started server process [28722]
<span style="color: green;">INFO</span>: Waiting for application startup.
<span style="color: green;">INFO</span>: Application startup complete.
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Will watch for changes in these directories:
<b>[</b><font color="#4E9A06">&apos;/home/user/code/awesomeapp&apos;</font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font> <b>(</b>Press CTRL+C
to quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started reloader process <b>[</b><font color="#34E2E2"><b>383138</b></font><b>]</b> using WatchFiles
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>383153</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
```
</div>
**НАСТОЯТЕЛЬНО рекомендуется**, чтобы вы написали или скопировали код, отредактировали его и запустили локально.
**НАСТОЯТЕЛЬНО рекомендуется** написать или скопировать код, отредактировать его и запустить локально.
Использование кода в вашем редакторе — это то, что действительно показывает вам преимущества FastAPI, видя, как мало кода вам нужно написать, все проверки типов, автодополнение и т.д.
Использование кода в вашем редакторе кода — это то, что действительно показывает преимущества FastAPI: вы увидите, как мало кода нужно написать, все проверки типов, автозавершение и т.д.
---
## Установка FastAPI
## Установка FastAPI { #install-fastapi }
Первый шаг — установить FastAPI.
Для руководства вы, возможно, захотите установить его со всеми дополнительными зависимостями и функциями:
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его, и затем **установите FastAPI**:
<div class="termy">
```console
$ pip install "fastapi[all]"
$ pip install "fastapi[standard]"
---> 100%
```
</div>
...это также включает `uvicorn`, который вы можете использовать в качестве сервера, который запускает ваш код.
/// note | Технические детали
Вы также можете установить его по частям.
/// note | Примечание
Это то, что вы, вероятно, сделаете, когда захотите развернуть свое приложение в рабочей среде:
При установке с помощью `pip install "fastapi[standard]"` добавляются некоторые стандартные необязательные зависимости по умолчанию, включая `fastapi-cloud-cli`, который позволяет развернуть приложение на <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>.
```
pip install fastapi
```
Также установите `uvicorn` для работы в качестве сервера:
```
pip install "uvicorn[standard]"
```
Если вы не хотите иметь эти необязательные зависимости, установите просто `pip install fastapi`.
И то же самое для каждой из необязательных зависимостей, которые вы хотите использовать.
Если вы хотите установить стандартные зависимости, но без `fastapi-cloud-cli`, установите `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
///
## Продвинутое руководство пользователя
## Продвинутое руководство пользователя { #advanced-user-guide }
Существует также **Продвинутое руководство пользователя**, которое вы сможете прочитать после руководства **Учебник - Руководство пользователя**.
Существует также **Продвинутое руководство пользователя**, которое вы сможете прочитать после **Учебник - Руководство пользователя**.
**Продвинутое руководство пользователя** основано на этом, использует те же концепции и учит вас некоторым дополнительным функциям.
**Продвинутое руководство пользователя** основано на этом, использует те же концепции и обучает некоторым дополнительным функциям.
Но вы должны сначала прочитать **Учебник - Руководство пользователя** (то, что вы читаете прямо сейчас).
Но сначала вам следует прочитать **Учебник - Руководство пользователя** (то, что вы читаете прямо сейчас).
Он разработан таким образом, что вы можете создать полноценное приложение, используя только **Учебник - Руководство пользователя**, а затем расширить его различными способами, в зависимости от ваших потребностей, используя некоторые дополнительные идеи из **Продвинутого руководства пользователя**.
Оно спроектировано так, что вы можете создать полноценное приложение, используя только **Учебник - Руководство пользователя**, а затем расширить его различными способами, в зависимости от ваших потребностей, используя дополнительные идеи из **Продвинутого руководства пользователя**.

44
docs/ru/docs/tutorial/metadata.md

@ -1,23 +1,24 @@
# URL-адреса метаданных и документации
# URL-адреса метаданных и документации { #metadata-and-docs-urls }
Вы можете настроить несколько конфигураций метаданных в вашем **FastAPI** приложении.
## Метаданные для API
## Метаданные для API { #metadata-for-api }
Вы можете задать следующие поля, которые используются в спецификации OpenAPI и в UI автоматической документации API:
| Параметр | Тип | Описание |
|------------|--|-------------|
|------------|------|-------------|
| `title` | `str` | Заголовок API. |
| `summary` | `str` | Краткое резюме API. <small>Доступно начиная с OpenAPI 3.1.0, FastAPI 0.99.0.</small> |
| `description` | `str` | Краткое описание API. Может быть использован Markdown. |
| `version` | `string` | Версия API. Версия вашего собственного приложения, а не OpenAPI. К примеру `2.5.0`. |
| `terms_of_service` | `str` | Ссылка к условиям пользования API. Если указано, то это должен быть URL-адрес. |
| `contact` | `dict` | Контактная информация для открытого API. Может содержать несколько полей. <details><summary>поля <code>contact</code></summary><table><thead><tr><th>Параметр</th><th>Тип</th><th>Описание</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td>Идентификационное имя контактного лица/организации.</td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>URL указывающий на контактную информацию. ДОЛЖЕН быть в формате URL.</td></tr><tr><td><code>email</code></td><td><code>str</code></td><td>Email адрес контактного лица/организации. ДОЛЖЕН быть в формате email адреса.</td></tr></tbody></table></details> |
| `license_info` | `dict` | Информация о лицензии открытого API. Может содержать несколько полей. <details><summary>поля <code>license_info</code></summary><table><thead><tr><th>Параметр</th><th>Тип</th><th>Описание</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td><strong>ОБЯЗАТЕЛЬНО</strong> (если установлен параметр <code>license_info</code>). Название лицензии, используемой для API</td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>URL, указывающий на лицензию, используемую для API. ДОЛЖЕН быть в формате URL.</td></tr></tbody></table></details> |
| `license_info` | `dict` | Информация о лицензии открытого API. Может содержать несколько полей. <details><summary>поля <code>license_info</code></summary><table><thead><tr><th>Параметр</th><th>Тип</th><th>Описание</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td><strong>ОБЯЗАТЕЛЬНО</strong> (если установлен параметр <code>license_info</code>). Название лицензии, используемой для API.</td></tr><tr><td><code>identifier</code></td><td><code>str</code></td><td>Выражение лицензии <a href="https://spdx.org/licenses/" class="external-link" target="_blank">SPDX</a> для API. Поле <code>identifier</code> взаимоисключающее с полем <code>url</code>. <small>Доступно начиная с OpenAPI 3.1.0, FastAPI 0.99.0.</small></td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>URL, указывающий на лицензию, используемую для API. ДОЛЖЕН быть в формате URL.</td></tr></tbody></table></details> |
Вы можете задать их следующим образом:
{* ../../docs_src/metadata/tutorial001.py hl[3:16,19:31] *}
{* ../../docs_src/metadata/tutorial001.py hl[3:16, 19:32] *}
/// tip | Подсказка
@ -25,11 +26,19 @@
///
С этой конфигурацией автоматическая документация API будут выглядеть так:
С этой конфигурацией автоматическая документация API будет выглядеть так:
<img src="/img/tutorial/metadata/image01.png">
## Метаданные для тегов
## Идентификатор лицензии { #license-identifier }
Начиная с OpenAPI 3.1.0 и FastAPI 0.99.0, вы также можете задать `license_info` с помощью `identifier` вместо `url`.
К примеру:
{* ../../docs_src/metadata/tutorial001_1.py hl[31] *}
## Метаданные для тегов { #metadata-for-tags }
Вы также можете добавить дополнительные метаданные для различных тегов, используемых для группировки ваших операций пути с помощью параметра `openapi_tags`.
@ -43,7 +52,7 @@
* `description`: `str`-значение с кратким описанием для внешней документации.
* `url` (**обязательно**): `str`-значение с URL-адресом для внешней документации.
### Создание метаданных для тегов
### Создание метаданных для тегов { #create-metadata-for-tags }
Давайте попробуем сделать это на примере с тегами для `users` и `items`.
@ -59,30 +68,31 @@
///
### Используйте собственные теги
### Используйте собственные теги { #use-your-tags }
Используйте параметр `tags` с вашими *операциями пути*`APIRouter`ами), чтобы присвоить им различные теги:
{* ../../docs_src/metadata/tutorial004.py hl[21,26] *}
/// info | Дополнительная информация
Узнайте больше о тегах в [Конфигурации операции пути](path-operation-configuration.md#_3){.internal-link target=_blank}.
Узнайте больше о тегах в [Конфигурации операции пути](path-operation-configuration.md#tags){.internal-link target=_blank}.
///
### Проверьте документацию
### Проверьте документацию { #check-the-docs }
Теперь, если вы проверите документацию, вы увидите всю дополнительную информацию:
<img src="/img/tutorial/metadata/image02.png">
### Порядок расположения тегов
### Порядок расположения тегов { #order-of-tags }
Порядок расположения словарей метаданных для каждого тега определяет также порядок, отображаемый в документах UI
Порядок расположения словарей метаданных для каждого тега определяет также порядок, отображаемый в UI документации.
К примеру, несмотря на то, что `users` будут идти после `items` в алфавитном порядке, они отображаются раньше, потому что мы добавляем свои метаданные в качестве первого словаря в списке.
## URL-адреса OpenAPI
## URL-адрес OpenAPI { #openapi-url }
По умолчанию схема OpenAPI отображена по адресу `/openapi.json`.
@ -92,11 +102,11 @@
{* ../../docs_src/metadata/tutorial002.py hl[3] *}
Если вы хотите отключить схему OpenAPI полностью, вы можете задать `openapi_url=None`, это также отключит пользовательские интерфейсы документации, которые его использует.
Если вы хотите отключить схему OpenAPI полностью, вы можете задать `openapi_url=None`, это также отключит пользовательские интерфейсы документации, которые её используют.
## URL-адреса документации
## URL-адреса документации { #docs-urls }
Вы можете изменить конфигурацию двух пользовательских интерфейсов документации, среди которых
Вы можете изменить конфигурацию двух пользовательских интерфейсов документации, которые включены:
* **Swagger UI**: отображаемый по адресу `/docs`.
* Вы можете задать его URL с помощью параметра `docs_url`.

35
docs/ru/docs/tutorial/middleware.md

@ -1,4 +1,4 @@
# Middleware (Промежуточный слой)
# Middleware (Промежуточный слой) { #middleware }
Вы можете добавить промежуточный слой (middleware) в **FastAPI** приложение.
@ -17,11 +17,11 @@
Если у вас есть зависимости с `yield`, то код выхода (код после `yield`) будет выполняться *после* middleware.
Если у вас имеются некие фоновые задачи (см. документацию), то они будут запущены после middleware.
Если были какие‑либо фоновые задачи (рассматриваются в разделе [Фоновые задачи](background-tasks.md){.internal-link target=_blank}, вы увидите это позже), они будут запущены *после* всех middleware.
///
## Создание middleware
## Создание middleware { #create-a-middleware }
Для создания middleware используйте декоратор `@app.middleware("http")`.
@ -51,7 +51,7 @@
///
### До и после `response`
### До и после `response` { #before-and-after-the-response }
Вы можете добавить код, использующий `request` до передачи его какой-либо *операции пути*.
@ -67,8 +67,31 @@
///
## Другие middleware
## Порядок выполнения нескольких middleware { #multiple-middleware-execution-order }
Когда вы добавляете несколько middleware с помощью декоратора `@app.middleware()` или метода `app.add_middleware()`, каждое новое middleware оборачивает приложение, формируя стек. Последнее добавленное middleware — самое внешнее (*outermost*), а первое — самое внутреннее (*innermost*).
На пути обработки запроса сначала выполняется самое внешнее middleware.
На пути формирования ответа оно выполняется последним.
Например:
```Python
app.add_middleware(MiddlewareA)
app.add_middleware(MiddlewareB)
```
Это приводит к следующему порядку выполнения:
* **Запрос**: MiddlewareB → MiddlewareA → маршрут
* **Ответ**: маршрут → MiddlewareA → MiddlewareB
Такое стековое поведение обеспечивает предсказуемый и управляемый порядок выполнения middleware.
## Другие middleware { #other-middlewares }
О других middleware вы можете узнать больше в разделе [Advanced User Guide: Advanced Middleware](../advanced/middleware.md){.internal-link target=_blank}.
В следующем разделе вы можете прочитать, как настроить <abbr title="Cross-Origin Resource Sharing">CORS</abbr> с помощью middleware.
В следующем разделе вы можете прочитать, как настроить <abbr title="Cross-Origin Resource Sharing – совместное использование ресурсов между источниками">CORS</abbr> с помощью middleware.

22
docs/ru/docs/tutorial/path-operation-configuration.md

@ -1,4 +1,4 @@
# Конфигурация операций пути
# Конфигурация операций пути { #path-operation-configuration }
Существует несколько параметров, которые вы можете передать вашему *декоратору операций пути* для его настройки.
@ -8,7 +8,7 @@
///
## Коды состояния
## Статус-код ответа { #response-status-code }
Вы можете определить (HTTP) `status_code`, который будет использован в ответах вашей *операции пути*.
@ -18,7 +18,7 @@
{* ../../docs_src/path_operation_configuration/tutorial001_py310.py hl[1,15] *}
Этот код состояния будет использован в ответе и будет добавлен в схему OpenAPI.
Этот статус-код будет использован в ответе и будет добавлен в схему OpenAPI.
/// note | Технические детали
@ -28,7 +28,7 @@
///
## Теги
## Теги { #tags }
Вы можете добавлять теги к вашим *операциям пути*, добавив параметр `tags` с `list` заполненным `str`-значениями (обычно в нём только одна строка):
@ -38,7 +38,7 @@
<img src="/img/tutorial/path-operation-configuration/image01.png">
### Теги с перечислениями
### Теги с перечислениями { #tags-with-enums }
Если у вас большое приложение, вы можете прийти к необходимости добавить **несколько тегов**, и возможно, вы захотите убедиться в том, что всегда используете **один и тот же тег** для связанных *операций пути*.
@ -48,13 +48,13 @@
{* ../../docs_src/path_operation_configuration/tutorial002b.py hl[1,8:10,13,18] *}
## Краткое и развёрнутое содержание
## Краткое и развёрнутое содержание { #summary-and-description }
Вы можете добавить параметры `summary` и `description`:
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[18:19] *}
## Описание из строк документации
## Описание из строк документации { #description-from-docstring }
Так как описания обычно длинные и содержат много строк, вы можете объявить описание *операции пути* в функции <abbr title="многострочный текст, первое выражение внутри функции (не присвоенный какой-либо переменной), используемый для документации">строки документации</abbr> и **FastAPI** прочитает её отсюда.
@ -66,7 +66,7 @@
<img src="/img/tutorial/path-operation-configuration/image02.png">
## Описание ответа
## Описание ответа { #response-description }
Вы можете указать описание ответа с помощью параметра `response_description`:
@ -78,7 +78,7 @@
///
/// check | Технические детали
/// check
OpenAPI указывает, что каждой *операции пути* необходимо описание ответа.
@ -88,7 +88,7 @@ OpenAPI указывает, что каждой *операции пути* не
<img src="/img/tutorial/path-operation-configuration/image03.png">
## Обозначение *операции пути* как устаревшей
## Обозначение *операции пути* как устаревшей { #deprecate-a-path-operation }
Если вам необходимо пометить *операцию пути* как <abbr title="устаревшее, не рекомендовано к использованию">устаревшую</abbr>, при этом не удаляя её, передайте параметр `deprecated`:
@ -102,6 +102,6 @@ OpenAPI указывает, что каждой *операции пути* не
<img src="/img/tutorial/path-operation-configuration/image05.png">
## Резюме
## Резюме { #recap }
Вы можете легко конфигурировать и добавлять метаданные в ваши *операции пути*, передавая параметры *декораторам операций пути*.

58
docs/ru/docs/tutorial/path-params-numeric-validations.md

@ -1,8 +1,8 @@
# Path-параметры и валидация числовых данных
# Path-параметры и валидация числовых данных { #path-parameters-and-numeric-validations }
Так же, как с помощью `Query` вы можете добавлять валидацию и метаданные для query-параметров, так и с помощью `Path` вы можете добавлять такую же валидацию и метаданные для path-параметров.
## Импорт Path
## Импорт `Path` { #import-path }
Сначала импортируйте `Path` из `fastapi`, а также импортируйте `Annotated`:
@ -14,11 +14,11 @@
Если вы используете более старую версию, вы столкнётесь с ошибками при попытке использовать `Annotated`.
Убедитесь, что вы [обновили версию FastAPI](../deployment/versions.md#fastapi_2){.internal-link target=_blank} как минимум до 0.95.1 перед тем, как использовать `Annotated`.
Убедитесь, что вы [обновили версию FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} как минимум до 0.95.1 перед тем, как использовать `Annotated`.
///
## Определите метаданные
## Определите метаданные { #declare-metadata }
Вы можете указать все те же параметры, что и для `Query`.
@ -28,15 +28,11 @@
/// note | Примечание
Path-параметр всегда является обязательным, поскольку он составляет часть пути.
Поэтому следует объявить его с помощью `...`, чтобы обозначить, что этот параметр обязательный.
Тем не менее, даже если вы объявите его как `None` или установите для него значение по умолчанию, это ни на что не повлияет и параметр останется обязательным.
Path-параметр всегда является обязательным, поскольку он должен быть частью пути. Даже если вы объявите его как `None` или зададите значение по умолчанию, это ни на что не повлияет — параметр всё равно будет обязательным.
///
## Задайте нужный вам порядок параметров
## Задайте нужный вам порядок параметров { #order-the-parameters-as-you-need }
/// tip | Подсказка
@ -58,25 +54,13 @@ Path-параметр всегда является обязательным, п
Поэтому вы можете определить функцию так:
//// tab | Python 3.8 без Annotated
/// tip | Подсказка
Рекомендуется использовать версию с `Annotated` если возможно.
///
```Python hl_lines="7"
{!> ../../docs_src/path_params_numeric_validations/tutorial002.py!}
```
////
{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[7] *}
Но имейте в виду, что если вы используете `Annotated`, вы не столкнётесь с этой проблемой, так как вы не используете `Query()` или `Path()` в качестве значения по умолчанию для параметра функции.
Но имейте в виду, что если вы используете `Annotated`, вы не столкнётесь с этой проблемой, так как вы не используете значения по умолчанию параметров функции для `Query()` или `Path()`.
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py hl[10] *}
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *}
## Задайте нужный вам порядок параметров, полезные приёмы
## Задайте нужный вам порядок параметров, полезные приёмы { #order-the-parameters-as-you-need-tricks }
/// tip | Подсказка
@ -97,25 +81,25 @@ Path-параметр всегда является обязательным, п
Передайте `*` в качестве первого параметра функции.
Python не будет ничего делать с `*`, но он будет знать, что все следующие параметры являются именованными аргументами (парами ключ-значение), также известными как <abbr title="From: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>, даже если у них нет значений по умолчанию.
Python не будет ничего делать с `*`, но он будет знать, что все следующие параметры являются именованными аргументами (парами ключ-значение), также известными как <abbr title="От: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>, даже если у них нет значений по умолчанию.
{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[7] *}
### Лучше с `Annotated`
### Лучше с `Annotated` { #better-with-annotated }
Имейте в виду, что если вы используете `Annotated`, то, поскольку вы не используете значений по умолчанию для параметров функции, то у вас не возникнет подобной проблемы и вам не придётся использовать `*`.
Имейте в виду, что если вы используете `Annotated`, то, поскольку вы не используете значений по умолчанию для параметров функции, у вас не возникнет подобной проблемы и вам, вероятно, не придётся использовать `*`.
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py hl[10] *}
## Валидация числовых данных: больше или равно
## Валидация числовых данных: больше или равно { #number-validations-greater-than-or-equal }
С помощью `Query` и `Path` (и других классов, которые мы разберём позже) вы можете добавлять ограничения для числовых данных.
В этом примере при указании `ge=1`, параметр `item_id` должен быть больше или равен `1` ("`g`reater than or `e`qual").
В этом примере при указании `ge=1`, параметр `item_id` должен быть целым числом "`g`reater than or `e`qual" — больше или равно `1`.
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *}
## Валидация числовых данных: больше и меньше или равно
## Валидация числовых данных: больше и меньше или равно { #number-validations-greater-than-and-less-than-or-equal }
То же самое применимо к:
@ -124,19 +108,19 @@ Python не будет ничего делать с `*`, но он будет з
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py hl[10] *}
## Валидация числовых данных: числа с плавающей точкой, больше и меньше
## Валидация числовых данных: числа с плавающей точкой, больше и меньше { #number-validations-floats-greater-than-and-less-than }
Валидация также применима к значениям типа `float`.
В этом случае становится важной возможность добавить ограничение <abbr title="greater than"><code>gt</code></abbr>, вместо <abbr title="greater than or equal"><code>ge</code></abbr>, поскольку в таком случае вы можете, например, создать ограничение, чтобы значение было больше `0`, даже если оно меньше `1`.
В этом случае становится важной возможность добавить ограничение <abbr title="greater than – больше чем"><code>gt</code></abbr>, вместо <abbr title="greater than or equal – больше или равно"><code>ge</code></abbr>, поскольку в таком случае вы можете, например, создать ограничение, чтобы значение было больше `0`, даже если оно меньше `1`.
Таким образом, `0.5` будет корректным значением. А `0.0` или `0` — нет.
То же самое справедливо и для <abbr title="less than"><code>lt</code></abbr>.
То же самое справедливо и для <abbr title="less than – меньше чем"><code>lt</code></abbr>.
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py hl[13] *}
## Резюме
## Резюме { #recap }
С помощью `Query`, `Path` (и других классов, которые мы пока не затронули) вы можете добавлять метаданные и строковую валидацию тем же способом, как и в главе [Query-параметры и валидация строк](query-params-str-validations.md){.internal-link target=_blank}.
@ -149,7 +133,7 @@ Python не будет ничего делать с `*`, но он будет з
/// info | Информация
`Query`, `Path` и другие классы, которые мы разберём позже, являются наследниками общего класса `Param`.
`Query`, `Path` и другие классы, которые вы разберёте позже, являются наследниками общего класса `Param`.
Все они используют те же параметры для дополнительной валидации и метаданных, которые вы видели ранее.

87
docs/ru/docs/tutorial/path-params.md

@ -1,4 +1,4 @@
# Path-параметры
# Path-параметры { #path-parameters }
Вы можете определить "параметры" или "переменные" пути, используя синтаксис форматированных строк Python:
@ -12,9 +12,9 @@
{"item_id":"foo"}
```
## Параметры пути с типами
## Параметры пути с типами { #path-parameters-with-types }
Вы можете объявить тип параметра пути в функции, используя стандартные аннотации типов Python.
Вы можете объявить тип параметра пути в функции, используя стандартные аннотации типов Python:
{* ../../docs_src/path_params/tutorial002.py hl[7] *}
@ -22,11 +22,11 @@
/// check | Заметка
Это обеспечит поддержку редактора внутри функции (проверка ошибок, автодополнение и т.п.).
Это обеспечит поддержку редактора кода внутри функции (проверка ошибок, автозавершение и т.п.).
///
## <abbr title="Или сериализация, парсинг">Преобразование</abbr> данных
## <abbr title="также известное как: сериализация, парсинг, маршаллинг">Преобразование</abbr> данных { #data-conversion }
Если запустите этот пример и перейдёте по адресу: <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a>, то увидите ответ:
@ -38,44 +38,45 @@
Обратите внимание на значение `3`, которое получила (и вернула) функция. Это целочисленный Python `int`, а не строка `"3"`.
Используя определения типов, **FastAPI** выполняет автоматический <abbr title="преобразование строк из HTTP-запроса в типы данных Python">"парсинг"</abbr> запросов.
Используя такое объявление типов, **FastAPI** выполняет автоматический <abbr title="преобразование строк из HTTP-запроса в типы данных Python">"парсинг"</abbr> запросов.
///
## <abbr title="Или валидация">Проверка</abbr> данных
## Валидация данных { #data-validation }
Если откроете браузер по адресу <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, то увидите интересную HTTP-ошибку:
```JSON
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo"
}
]
}
```
из-за того, что параметр пути `item_id` имеет значение `"foo"`, которое не является типом `int`.
из-за того, что параметр пути `item_id` имеет значение `"foo"`, которое не является типом `int`.
Та же ошибка возникнет, если вместо `int` передать `float` , например: <a href="http://127.0.0.1:8000/items/4.2" class="external-link" target="_blank">http://127.0.0.1:8000/items/4.2</a>
Та же ошибка возникнет, если вместо `int` передать `float`, например: <a href="http://127.0.0.1:8000/items/4.2" class="external-link" target="_blank">http://127.0.0.1:8000/items/4.2</a>
/// check | Заметка
**FastAPI** обеспечивает проверку типов, используя всё те же определения типов.
**FastAPI** обеспечивает валидацию данных, используя всё те же определения типов.
Обратите внимание, что в тексте ошибки явно указано место не прошедшее проверку.
Обратите внимание, что в тексте ошибки явно указано место, не прошедшее проверку.
Это очень полезно при разработке и отладке кода, который взаимодействует с API.
///
## Документация
## Документация { #documentation }
И теперь, когда откроете браузер по адресу: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, то увидите вот такую автоматически сгенерированную документацию API:
@ -89,17 +90,17 @@
///
## Преимущества стандартизации, альтернативная документация
## Преимущества стандартизации, альтернативная документация { #standards-based-benefits-alternative-documentation }
Поскольку сгенерированная схема соответствует стандарту <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md" class="external-link" target="_blank">OpenAPI</a>, её можно использовать со множеством совместимых инструментов.
Поскольку сгенерированная схема соответствует стандарту <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md" class="external-link" target="_blank">OpenAPI</a>, её можно использовать со множеством совместимых инструментов.
Именно поэтому, FastAPI сам предоставляет альтернативную документацию API (используя ReDoc), которую можно получить по адресу: <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
Именно поэтому, **FastAPI** сам предоставляет альтернативную документацию API (используя ReDoc), которую можно получить по адресу: <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
<img src="/img/tutorial/path-params/image02.png">
По той же причине, есть множество совместимых инструментов, включая инструменты генерации кода для многих языков.
## Pydantic
## Pydantic { #pydantic }
Вся проверка данных выполняется под капотом с помощью <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>. Поэтому вы можете быть уверены в качестве обработки данных.
@ -107,7 +108,7 @@
Некоторые из них рассматриваются в следующих главах данного руководства.
## Порядок имеет значение
## Порядок имеет значение { #order-matters }
При создании *операций пути* можно столкнуться с ситуацией, когда путь является фиксированным.
@ -117,7 +118,6 @@
Поскольку *операции пути* выполняются в порядке их объявления, необходимо, чтобы путь для `/users/me` был объявлен раньше, чем путь для `/users/{user_id}`:
{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
Иначе путь для `/users/{user_id}` также будет соответствовать `/users/me`, "подразумевая", что он получает параметр `user_id` со значением `"me"`.
@ -128,11 +128,11 @@
Первый будет выполняться всегда, так как путь совпадает первым.
## Предопределенные значения
## Предопределенные значения { #predefined-values }
Что если нам нужно заранее определить допустимые *параметры пути*, которые *операция пути* может принимать? В таком случае можно использовать стандартное перечисление <abbr title="Enumeration">`Enum`</abbr> Python.
### Создание класса `Enum`
### Создание класса `Enum` { #create-an-enum-class }
Импортируйте `Enum` и создайте подкласс, который наследуется от `str` и `Enum`.
@ -150,33 +150,33 @@
/// tip | Подсказка
Если интересно, то "AlexNet", "ResNet" и "LeNet" - это названия <abbr title="Технически, это архитектуры моделей глубокого обучения">моделей</abbr> машинного обучения.
Если интересно, то "AlexNet", "ResNet" и "LeNet" - это названия <abbr title="Технически, архитектуры моделей глубокого обучения">моделей</abbr> Машинного обучения.
///
### Определение *параметра пути*
### Определение *параметра пути* { #declare-a-path-parameter }
Определите *параметр пути*, используя в аннотации типа класс перечисления (`ModelName`), созданный ранее:
{* ../../docs_src/path_params/tutorial005.py hl[16] *}
### Проверьте документацию
### Проверьте документацию { #check-the-docs }
Поскольку доступные значения *параметра пути* определены заранее, интерактивная документация может наглядно их отображать:
<img src="/img/tutorial/path-params/image03.png">
### Работа с *перечислениями* в Python
### Работа с *перечислениями* в Python { #working-with-python-enumerations }
Значение *параметра пути* будет *элементом перечисления*.
#### Сравнение *элементов перечисления*
#### Сравнение *элементов перечисления* { #compare-enumeration-members }
Вы можете сравнить это значение с *элементом перечисления* класса `ModelName`:
{* ../../docs_src/path_params/tutorial005.py hl[17] *}
#### Получение *значения перечисления*
#### Получение *значения перечисления* { #get-the-enumeration-value }
Можно получить фактическое значение (в данном случае - `str`) с помощью `model_name.value` или в общем случае `your_enum_member.value`:
@ -188,7 +188,7 @@
///
#### Возврат *элементов перечисления*
#### Возврат *элементов перечисления* { #return-enumeration-members }
Из *операции пути* можно вернуть *элементы перечисления*, даже вложенные в тело JSON (например в `dict`).
@ -204,7 +204,7 @@
}
```
## Path-параметры, содержащие пути
## Path-параметры, содержащие пути { #path-parameters-containing-paths }
Предположим, что есть *операция пути* с путем `/files/{file_path}`.
@ -212,7 +212,7 @@
Тогда URL для этого файла будет такой: `/files/home/johndoe/myfile.txt`.
### Поддержка OpenAPI
### Поддержка OpenAPI { #openapi-support }
OpenAPI не поддерживает способов объявления *параметра пути*, содержащего внутри *путь*, так как это может привести к сценариям, которые сложно определять и тестировать.
@ -220,7 +220,7 @@ OpenAPI не поддерживает способов объявления *п
Документация по-прежнему будет работать, хотя и не добавит никакой информации о том, что параметр должен содержать путь.
### Конвертер пути
### Конвертер пути { #path-convertor }
Благодаря одной из опций Starlette, можете объявить *параметр пути*, содержащий *путь*, используя URL вроде:
@ -242,13 +242,14 @@ OpenAPI не поддерживает способов объявления *п
///
## Резюме
## Резюме { #recap }
Используя **FastAPI** вместе со стандартными объявлениями типов Python (короткими и интуитивно понятными), вы получаете:
* Поддержку редактора (проверку ошибок, автозаполнение и т.п.)
* Поддержку редактора кода (проверку ошибок, автозавершение и т.п.)
* "<abbr title="преобразование строк из HTTP-запроса в типы данных Python">Парсинг</abbr>" данных
* Валидацию данных
* Автоматическую документацию API с указанием типов параметров.
* Аннотации API и автоматическую документацию
И объявлять типы достаточно один раз.

12
docs/ru/docs/tutorial/query-param-models.md

@ -1,4 +1,4 @@
# Модели Query-Параметров
# Модели Query-Параметров { #query-parameter-models }
Если у вас есть группа связанных **query-параметров**, то вы можете объединить их в одну **Pydantic-модель**.
@ -10,7 +10,7 @@
///
## Pydantic-Модель для Query-Параметров
## Pydantic-Модель для Query-Параметров { #query-parameters-with-a-pydantic-model }
Объявите нужные **query-параметры** в **Pydantic-модели**, а после аннотируйте параметр как `Query`:
@ -18,7 +18,7 @@
**FastAPI извлечёт** данные соответствующие **каждому полю модели** из **query-параметров** запроса и выдаст вам объявленную Pydantic-модель заполненную ими.
## Проверьте Сгенерированную Документацию
## Проверьте Сгенерированную Документацию { #check-the-docs }
Вы можете посмотреть query-параметры в графическом интерфейсе сгенерированной документации по пути `/docs`:
@ -26,7 +26,7 @@
<img src="/img/tutorial/query-param-models/image01.png">
</div>
## Запретить Дополнительные Query-Параметры
## Запретить Дополнительные Query-Параметры { #forbid-extra-query-parameters }
В некоторых случаях (не особо часто встречающихся) вам может понадобиться **ограничить** query-параметры, которые вы хотите получить.
@ -57,12 +57,12 @@ https://example.com/items/?limit=10&tool=plumbus
}
```
## Заключение
## Заключение { #summary }
Вы можете использовать **Pydantic-модели** для объявления **query-параметров** в **FastAPI**. 😎
/// tip | Совет
Спойлер: вы также можете использовать Pydantic-модели для группировки кук (cookies) и заголовков (headers), но об этом вы прочитаете позже. 🤫
Спойлер: вы также можете использовать Pydantic-модели, чтобы объявлять cookies и HTTP-заголовки, но об этом вы прочитаете позже. 🤫
///

393
docs/ru/docs/tutorial/query-params-str-validations.md

@ -1,61 +1,51 @@
# Query-параметры и валидация строк
# Query-параметры и валидация строк { #query-parameters-and-string-validations }
**FastAPI** позволяет определять дополнительную информацию и валидацию для ваших параметров.
**FastAPI** позволяет определять дополнительную информацию и выполнять валидацию для ваших параметров.
Давайте рассмотрим следующий пример:
Рассмотрим это приложение в качестве примера:
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
Query-параметр `q` имеет тип `Union[str, None]` (или `str | None` в Python 3.10). Это означает, что входной параметр будет типа `str`, но может быть и `None`. Ещё параметр имеет значение по умолчанию `None`, из-за чего FastAPI определит параметр как необязательный.
Query-параметр `q` имеет тип `str | None`, это означает, что он имеет тип `str`, но также может быть `None`. Значение по умолчанию действительно `None`, поэтому FastAPI будет знать, что он не обязателен.
/// note | Технические детали
FastAPI определит параметр `q` как необязательный, потому что его значение по умолчанию `= None`.
FastAPI поймёт, что значение `q` не обязательно, из‑за значения по умолчанию `= None`.
`Union` в `Union[str, None]` позволит редактору кода оказать вам лучшую поддержку и найти ошибки.
Аннотация `str | None` позволит вашему редактору кода обеспечить лучшую поддержку и находить ошибки.
///
## Расширенная валидация
## Дополнительная валидация { #additional-validation }
Добавим дополнительное условие валидации параметра `q` - **длина строки не более 50 символов** (условие проверяется всякий раз, когда параметр `q` не является `None`).
Мы собираемся добавить ограничение: хотя `q` и необязателен, когда он передан, **его длина не должна превышать 50 символов**.
### Импорт `Query` и `Annotated`
### Импорт `Query` и `Annotated` { #import-query-and-annotated }
Чтобы достичь этого, первым делом нам нужно импортировать:
Чтобы сделать это, сначала импортируйте:
* `Query` из пакета `fastapi`:
* `Annotated` из пакета `typing` (или из `typing_extensions` для Python ниже 3.9)
* `Query` из `fastapi`
* `Annotated` из `typing`
//// tab | Python 3.10+
В Python 3.9 или выше, `Annotated` является частью стандартной библиотеки, таким образом вы можете импортировать его из `typing`.
```Python hl_lines="1 3"
{!> ../../docs_src/query_params_str_validations/tutorial002_an_py310.py!}
```
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
////
//// tab | Python 3.8+
/// info | Дополнительная информация
В версиях Python ниже Python 3.9 `Annotation` импортируется из `typing_extensions`.
Поддержка `Annotated` (и рекомендация использовать его) появилась в FastAPI версии 0.95.0.
Эта библиотека будет установлена вместе с FastAPI.
Если у вас более старая версия, при попытке использовать `Annotated` вы получите ошибки.
```Python hl_lines="3-4"
{!> ../../docs_src/query_params_str_validations/tutorial002_an.py!}
```
Убедитесь, что вы [обновили версию FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} как минимум до 0.95.1 перед использованием `Annotated`.
////
///
## `Annotated` как тип для query-параметра `q`
## Использовать `Annotated` в типе для параметра `q` { #use-annotated-in-the-type-for-the-q-parameter }
Помните, как ранее я говорил об Annotated? Он может быть использован для добавления метаданных для ваших параметров в разделе [Введение в аннотации типов Python](../python-types.md#type-hints-with-metadata-annotations){.internal-link target=_blank}?
Помните, я уже говорил, что `Annotated` можно использовать для добавления метаданных к параметрам в разделе [Введение в типы Python](../python-types.md#type-hints-with-metadata-annotations){.internal-link target=_blank}?
Пришло время использовать их в FastAPI. 🚀
Пришло время использовать его с FastAPI. 🚀
У нас была аннотация следующего типа:
У нас была такая аннотация типа:
//// tab | Python 3.10+
@ -73,7 +63,7 @@ q: Union[str, None] = None
////
Вот что мы получим, если обернём это в `Annotated`:
Мы «обернём» это в `Annotated`, и получится:
//// tab | Python 3.10+
@ -91,173 +81,161 @@ q: Annotated[Union[str, None]] = None
////
Обе эти версии означают одно и тоже. `q` - это параметр, который может быть `str` или `None`, и по умолчанию он будет принимать `None`.
Обе версии означают одно и то же: `q` параметр, который может быть `str` или `None`, и по умолчанию равен `None`.
Давайте повеселимся. 🎉
А теперь к самому интересному. 🎉
## Добавим `Query` в `Annotated` для query-параметра `q`
## Добавим `Query` в `Annotated` для параметра `q` { #add-query-to-annotated-in-the-q-parameter }
Теперь, когда у нас есть `Annotated`, где мы можем добавить больше метаданных, добавим `Query` со значением параметра `max_length` равным 50:
Теперь, когда у нас есть `Annotated`, куда можно поместить дополнительную информацию (в нашем случае — дополнительные правила валидации), добавим `Query` внутрь `Annotated` и установим параметр `max_length` равным `50`:
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *}
Обратите внимание, что значение по умолчанию всё ещё `None`, так что параметр остаётся необязательным.
Обратите внимание, что значение по умолчанию по‑прежнему `None`, то есть параметр остаётся необязательным.
Однако теперь, имея `Query(max_length=50)` внутри `Annotated`, мы говорим FastAPI, что мы хотим извлечь это значение из параметров query-запроса (что произойдёт в любом случае 🤷), и что мы хотим иметь **дополнительные условия валидации** для этого значения (для чего мы и делаем это - чтобы получить дополнительную валидацию). 😎
Но теперь, добавив `Query(max_length=50)` внутрь `Annotated`, мы говорим FastAPI, что этому значению нужна **дополнительная валидация** — максимум 50 символов. 😎
Теперь FastAPI:
/// tip | Совет
* **Валидирует** (проверяет), что полученные данные состоят максимум из 50 символов
* Показывает **исчерпывающую ошибку** (будет описание местонахождения ошибки и её причины) для клиента в случаях, когда данные не валидны
* **Задокументирует** параметр в схему OpenAPI *операции пути* (что будет отображено в **UI автоматической документации**)
Здесь мы используем `Query()`, потому что это **query-параметр**. Позже мы увидим другие — `Path()`, `Body()`, `Header()` и `Cookie()`, — они также принимают те же аргументы, что и `Query()`.
## Альтернативный (устаревший) способ задать `Query` как значение по умолчанию
///
В предыдущих версиях FastAPI (ниже <abbr title="ранее 2023-03">0.95.0</abbr>) необходимо было использовать `Query` как значение по умолчанию для query-параметра. Так было вместо размещения его в `Annotated`, так что велика вероятность, что вам встретится такой код. Сейчас объясню.
Теперь FastAPI будет:
/// tip | Подсказка
* **валидировать** данные, удостоверяясь, что максимальная длина — 50 символов;
* показывать **понятную ошибку** клиенту, если данные невалидны;
* **документировать** параметр в *операции пути* схемы OpenAPI (он будет показан в **UI автоматической документации**).
При написании нового кода и везде где это возможно, используйте `Annotated`, как было описано ранее. У этого способа есть несколько преимуществ (о них дальше) и никаких недостатков. 🍰
## Альтернатива (устаревшее): `Query` как значение по умолчанию { #alternative-old-query-as-the-default-value }
///
Вот как вы могли бы использовать `Query()` в качестве значения по умолчанию параметра вашей функции, установив для параметра `max_length` значение 50:
В предыдущих версиях FastAPI (до <abbr title="до 2023-03">0.95.0</abbr>) требовалось использовать `Query` как значение по умолчанию для параметра вместо помещения его в `Annotated`. Скорее всего вы ещё встретите такой код, поэтому поясню.
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
/// tip | Подсказка
В таком случае (без использования `Annotated`), мы заменили значение по умолчанию с `None` на `Query()` в функции. Теперь нам нужно установить значение по умолчанию для query-параметра `Query(default=None)`, что необходимо для тех же целей, как когда ранее просто указывалось значение по умолчанию (по крайней мере, для FastAPI).
Для нового кода и везде, где это возможно, используйте `Annotated`, как описано выше. У этого есть несколько преимуществ (см. ниже) и нет недостатков. 🍰
Таким образом:
///
```Python
q: Union[str, None] = Query(default=None)
```
Вот как можно использовать `Query()` как значение по умолчанию для параметра функции, установив `max_length` равным 50:
...делает параметр необязательным со значением по умолчанию `None`, также как это делает:
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
```Python
q: Union[str, None] = None
```
Так как в этом случае (без `Annotated`) мы заменяем в функции значение по умолчанию `None` на `Query()`, теперь нужно указать значение по умолчанию через параметр `Query(default=None)`, это служит той же цели — задать значение по умолчанию (по крайней мере для FastAPI).
И для Python 3.10 и выше:
Итак:
```Python
q: str | None = Query(default=None)
```
...делает параметр необязательным со значением по умолчанию `None`, также как это делает:
...делает параметр необязательным со значением по умолчанию `None`, так же как:
```Python
q: str | None = None
```
Но он явно объявляет его как query-параметр.
/// info | Дополнительная информация
Запомните, важной частью объявления параметра как необязательного является:
```Python
= None
```
или:
```Python
= Query(default=None)
```
так как `None` указан в качестве значения по умолчанию, параметр будет **необязательным**.
`Union[str, None]` позволит редактору кода оказать вам лучшую поддержку. Но это не то, на что обращает внимание FastAPI для определения необязательности параметра.
///
Но вариант с `Query` явно объявляет его как query-параметр.
Теперь, мы можем указать больше параметров для `Query`. В данном случае, параметр `max_length` применяется к строкам:
Затем мы можем передать и другие параметры в `Query`. В данном случае — параметр `max_length`, применимый к строкам:
```Python
q: Union[str, None] = Query(default=None, max_length=50)
q: str | None = Query(default=None, max_length=50)
```
Входные данные будут проверены. Если данные недействительны, тогда будет указано на ошибку в запросе (будет описание местонахождения ошибки и её причины). Кроме того, параметр задокументируется в схеме OpenAPI данной *операции пути*.
Это провалидирует данные, покажет понятную ошибку, если данные невалидны, и задокументирует параметр в *операции пути* схемы OpenAPI.
### Использовать `Query` как значение по умолчанию или добавить в `Annotated`
### `Query` как значение по умолчанию или внутри `Annotated` { #query-as-the-default-value-or-in-annotated }
Когда `Query` используется внутри `Annotated`, вы не можете использовать параметр `default` у `Query`.
Помните, что при использовании `Query` внутри `Annotated` нельзя указывать параметр `default` у `Query`.
Вместо этого, используйте обычное указание значения по умолчанию для параметра функции. Иначе, это будет несовместимо.
Вместо этого используйте обычное значение по умолчанию параметра функции. Иначе это будет неоднозначно.
Следующий пример не рабочий:
Например, так делать нельзя:
```Python
q: Annotated[str, Query(default="rick")] = "morty"
```
...потому что нельзя однозначно определить, что именно должно быть значением по умолчанию: `"rick"` или `"morty"`.
...потому что непонятно, какое значение должно быть по умолчанию: `"rick"` или `"morty"`.
Вам следует использовать (предпочтительно):
Следовательно, используйте (предпочтительно):
```Python
q: Annotated[str, Query()] = "rick"
```
...или как в старом коде, который вам может попасться:
...или в старой кодовой базе вы увидите:
```Python
q: str = Query(default="rick")
```
### Преимущества `Annotated`
### Преимущества `Annotated` { #advantages-of-annotated }
**Рекомендуется использовать `Annotated`** вместо значения по умолчанию в параметрах функции, потому что так **лучше** по нескольким причинам. 🤓
**Рекомендуется использовать `Annotated`** вместо задания значения по умолчанию в параметрах функции так **лучше** по нескольким причинам. 🤓
Значение **по умолчанию** у **параметров функции** - это **действительно значение по умолчанию**, что более интуитивно понятно для пользователей Python. 😌
**Значение по умолчанию** у **параметра функции** — это **настоящее значение по умолчанию**, что более интуитивно для Python. 😌
Вы можете **вызвать** ту же функцию в **иных местах** без FastAPI, и она **сработает как ожидается**. Если это **обязательный** параметр (без значения по умолчанию), ваш **редактор кода** сообщит об ошибке. **Python** также укажет на ошибку, если вы вызовете функцию без передачи ей обязательного параметра.
Вы можете **вызвать** эту же функцию в **других местах** без FastAPI, и она будет **работать как ожидается**. Если есть **обязательный** параметр (без значения по умолчанию), ваш **редактор кода** сообщит об ошибке, **Python** тоже пожалуется, если вы запустите её без передачи обязательного параметра.
Если вы вместо `Annotated` используете **(устаревший) стиль значений по умолчанию**, тогда при вызове этой функции без FastAPI в **другом месте** вам необходимо **помнить** о передаче аргументов функции, чтобы она работала корректно. В противном случае, значения будут отличаться от тех, что вы ожидаете (например, `QueryInfo` или что-то подобное вместо `str`). И ни ваш редактор кода, ни Python не будут жаловаться на работу этой функции, только когда вычисления внутри дадут сбой.
Если вы не используете `Annotated`, а применяете **(устаревший) стиль со значением по умолчанию**, то при вызове этой функции без FastAPI в **других местах** вам нужно **помнить** о том, что надо передать аргументы, чтобы всё работало корректно, иначе значения будут не такими, как вы ожидаете (например, вместо `str` будет `QueryInfo` или что-то подобное). И ни редактор, ни Python не будут ругаться при самом вызове функции — ошибка проявится лишь при операциях внутри.
Так как `Annotated` может принимать более одной аннотации метаданных, то теперь вы можете использовать ту же функцию с другими инструментами, например <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">Typer</a>. 🚀
Так как `Annotated` может содержать больше одной аннотации метаданных, теперь вы можете использовать ту же функцию и с другими инструментами, например с <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">Typer</a>. 🚀
## Больше валидации
## Больше валидаций { #add-more-validations }
Вы также можете добавить параметр `min_length`:
Можно также добавить параметр `min_length`:
{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *}
## Регулярные выражения
## Регулярные выражения { #add-regular-expressions }
Вы можете определить <abbr title="Регулярное выражение, regex или regexp - это последовательность символов, определяющая шаблон для строк.">регулярное выражение</abbr>, которому должен соответствовать параметр:
Вы можете определить <abbr title="Регулярное выражение (regex, regexp) — это последовательность символов, задающая шаблон поиска для строк.">регулярное выражение</abbr> `pattern`, которому должен соответствовать параметр:
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
Данное регулярное выражение проверяет, что полученное значение параметра:
Данный шаблон регулярного выражения проверяет, что полученное значение параметра:
* `^`: начало строки.
* `fixedquery`: в точности содержит строку `fixedquery`.
* `$`: конец строки, не имеет символов после `fixedquery`.
* `^`: начинается с следующих символов, до них нет символов.
* `fixedquery`: имеет точное значение `fixedquery`.
* `$`: заканчивается здесь, после `fixedquery` нет никаких символов.
Не переживайте, если **"регулярное выражение"** вызывает у вас трудности. Это достаточно сложная тема для многих людей. Вы можете сделать множество вещей без использования регулярных выражений.
Если вы теряетесь во всех этих идеях про **«регулярные выражения»**, не переживайте. Это сложная тема для многих. Многое можно сделать и без них.
Но когда они вам понадобятся, и вы закончите их освоение, то не будет проблемой использовать их в **FastAPI**.
Теперь вы знаете, что когда они понадобятся, вы сможете использовать их в **FastAPI**.
## Значения по умолчанию
### `regex` из Pydantic v1 вместо `pattern` { #pydantic-v1-regex-instead-of-pattern }
Вы точно также можете указать любое значение `по умолчанию`, как ранее указывали `None`.
До Pydantic версии 2 и до FastAPI 0.100.0 этот параметр назывался `regex`, а не `pattern`, но сейчас он устарел.
Например, вы хотите для параметра запроса `q` указать, что он должен состоять минимум из 3 символов (`min_length=3`) и иметь значение по умолчанию `"fixedquery"`:
Вы всё ещё можете встретить такой код:
//// tab | Pydantic v1
{* ../../docs_src/query_params_str_validations/tutorial004_regex_an_py310.py hl[11] *}
////
Имейте в виду, что это устарело, и код следует обновить на использование нового параметра `pattern`. 🤓
## Значения по умолчанию { #default-values }
Конечно, можно использовать и другие значения по умолчанию, не только `None`.
Допустим, вы хотите объявить, что query-параметр `q` должен иметь `min_length` равный `3` и значение по умолчанию `"fixedquery"`:
{* ../../docs_src/query_params_str_validations/tutorial005_an_py39.py hl[9] *}
/// note | Технические детали
/// note | Примечание
Наличие значения по умолчанию делает параметр необязательным.
Наличие значения по умолчанию любого типа, включая `None`, делает параметр необязательным.
///
## Обязательный параметр
## Обязательные параметры { #required-parameters }
Когда вам не требуется дополнительная валидация или дополнительные метаданные для параметра запроса, вы можете сделать параметр `q` обязательным просто не указывая значения по умолчанию. Например:
Когда не требуется объявлять дополнительные проверки или метаданные, можно сделать query-параметр `q` обязательным, просто не указывая значение по умолчанию, например:
```Python
q: str
@ -266,60 +244,42 @@ q: str
вместо:
```Python
q: Union[str, None] = None
```
Но у нас query-параметр определён как `Query`. Например:
//// tab | Annotated
```Python
q: Annotated[Union[str, None], Query(min_length=3)] = None
q: str | None = None
```
////
//// tab | без Annotated
Но сейчас мы объявляем его через `Query`, например так:
```Python
q: Union[str, None] = Query(default=None, min_length=3)
q: Annotated[str | None, Query(min_length=3)] = None
```
////
В таком случае, чтобы сделать query-параметр `Query` обязательным, вы можете просто не указывать значение по умолчанию:
Поэтому, когда вам нужно объявить значение как обязательное при использовании `Query`, просто не указывайте значение по умолчанию:
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
### Обязательный параметр с `None`
### Обязательный, но может быть `None` { #required-can-be-none }
Вы можете определить, что параметр может принимать `None`, но всё ещё является обязательным. Это может потребоваться для того, чтобы пользователи явно указали параметр, даже если его значение будет `None`.
Можно объявить, что параметр может принимать `None`, но при этом остаётся обязательным. Это заставит клиентов отправлять значение, даже если это значение — `None`.
Чтобы этого добиться, вам нужно определить `None` как валидный тип для параметра запроса, но также указать `default=...`:
Для этого объявите, что `None` — валидный тип, но просто не задавайте значение по умолчанию:
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
/// tip | Подсказка
Pydantic, мощь которого используется в FastAPI для валидации и сериализации, имеет специальное поведение для `Optional` или `Union[Something, None]` без значения по умолчанию. Вы можете узнать об этом больше в документации Pydantic, раздел <a href="https://docs.pydantic.dev/latest/concepts/models/#required-optional-fields" class="external-link" target="_blank">Обязательные Опциональные поля</a>.
///
## Множество значений для query-параметра
## Query-параметр - список / несколько значений { #query-parameter-list-multiple-values }
Для query-параметра `Query` можно указать, что он принимает список значений (множество значений).
Когда вы явно объявляете query-параметр через `Query`, можно также указать, что он принимает список значений, иначе говоря — несколько значений.
Например, query-параметр `q` может быть указан в URL несколько раз. И если вы ожидаете такой формат запроса, то можете указать это следующим образом:
Например, чтобы объявить query-параметр `q`, который может встречаться в URL несколько раз, можно написать:
{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *}
Затем, получив такой URL:
Тогда при таком URL:
```
http://localhost:8000/items/?q=foo&q=bar
```
вы бы получили несколько значений (`foo` и `bar`), которые относятся к параметру `q`, в виде Python `list` внутри вашей *функции обработки пути*, в *параметре функции* `q`.
вы получите множественные значения query-параметра `q` (`foo` и `bar`) в виде Python-`list` внутри вашей *функции обработки пути*, в *параметре функции* `q`.
Таким образом, ответ на этот URL будет:
@ -332,29 +292,29 @@ http://localhost:8000/items/?q=foo&q=bar
}
```
/// tip | Подсказка
/// tip | Совет
Чтобы объявить query-параметр типом `list`, как в примере выше, вам нужно явно использовать `Query`, иначе он будет интерпретирован как тело запроса.
Чтобы объявить query-параметр типа `list`, как в примере выше, нужно явно использовать `Query`, иначе он будет интерпретирован как тело запроса.
///
Интерактивная документация API будет обновлена соответствующим образом, где будет разрешено множество значений:
Интерактивная документация API обновится соответствующим образом и позволит передавать несколько значений:
<img src="/img/tutorial/query-params-str-validations/image02.png">
### Query-параметр со множеством значений по умолчанию
### Query-параметр - список / несколько значений со значением по умолчанию { #query-parameter-list-multiple-values-with-defaults }
Вы также можете указать тип `list` со списком значений по умолчанию на случай, если вам их не предоставят:
Можно также определить значение по умолчанию как `list`, если ничего не передано:
{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *}
Если вы перейдёте по ссылке:
Если вы перейдёте по адресу:
```
http://localhost:8000/items/
```
значение по умолчанию для `q` будет: `["foo", "bar"]` и ответом для вас будет:
значение по умолчанию для `q` будет: `["foo", "bar"]`, и ответом будет:
```JSON
{
@ -365,45 +325,45 @@ http://localhost:8000/items/
}
```
#### Использование `list`
#### Просто `list` { #using-just-list }
Вы также можете использовать `list` напрямую вместо `List[str]` (или `list[str]` в Python 3.9+):
Можно использовать `list` напрямую вместо `list[str]`:
{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
/// note | Технические детали
Запомните, что в таком случае, FastAPI не будет проверять содержимое списка.
Имейте в виду, что в этом случае FastAPI не будет проверять содержимое списка.
Например, для List[int] список будет провалидирован (и задокументирован) на содержание только целочисленных элементов. Но для простого `list` такой проверки не будет.
Например, `list[int]` проверит (и задокументирует), что элементы списка — целые числа. А просто `list` — нет.
///
## Больше метаданных
## Больше метаданных { #declare-more-metadata }
Вы можете добавить больше информации об query-параметре.
Можно добавить больше информации о параметре.
Указанная информация будет включена в генерируемую OpenAPI документацию и использована в пользовательском интерфейсе и внешних инструментах.
Эта информация будет включена в сгенерированную OpenAPI-схему и использована интерфейсами документации и внешними инструментами.
/// note | Технические детали
Имейте в виду, что разные инструменты могут иметь разные уровни поддержки OpenAPI.
Помните, что разные инструменты могут иметь разный уровень поддержки OpenAPI.
Некоторые из них могут не отображать (на данный момент) всю заявленную дополнительную информацию, хотя в большинстве случаев отсутствующая функция уже запланирована к разработке.
Некоторые из них пока могут не показывать всю дополнительную информацию, хотя в большинстве случаев недостающая возможность уже запланирована к разработке.
///
Вы можете указать название query-параметра, используя параметр `title`:
Можно задать `title`:
{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *}
Добавить описание, используя параметр `description`:
И `description`:
{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
## Псевдонимы параметров
## Псевдонимы параметров { #alias-parameters }
Представьте, что вы хотите использовать query-параметр с названием `item-query`.
Представьте, что вы хотите, чтобы параметр назывался `item-query`.
Например:
@ -411,54 +371,117 @@ http://localhost:8000/items/
http://127.0.0.1:8000/items/?item-query=foobaritems
```
Но `item-query` является невалидным именем переменной в Python.
Но `item-query` — недопустимое имя переменной в Python.
Наиболее похожее валидное имя `item_query`.
Ближайший вариант — `item_query`.
Но вам всё равно необходим `item-query`...
Но вам всё равно нужно именно `item-query`...
Тогда вы можете объявить `псевдоним`, и этот псевдоним будет использоваться для поиска значения параметра запроса:
Тогда можно объявить `alias`, и этот псевдоним будет использован для поиска значения параметра:
{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
## Устаревшие параметры
## Маркировка параметров как устаревших { #deprecating-parameters }
Предположим, вы больше не хотите использовать какой-либо параметр.
Предположим, этот параметр вам больше не нравится.
Вы решили оставить его, потому что клиенты всё ещё им пользуются. Но вы хотите отобразить это в документации как <abbr title="устарело, не рекомендуется использовать">устаревший функционал</abbr>.
Его нужно оставить на какое‑то время, так как клиенты его используют, но вы хотите, чтобы в документации он явно отображался как <abbr title="устаревший, не рекомендуется использовать">устаревший</abbr>.
Тогда для `Query` укажите параметр `deprecated=True`:
Тогда передайте параметр `deprecated=True` в `Query`:
{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *}
В документации это будет отображено следующим образом:
В документации это будет показано так:
<img src="/img/tutorial/query-params-str-validations/image01.png">
## Исключить из OpenAPI
## Исключить параметры из OpenAPI { #exclude-parameters-from-openapi }
Чтобы исключить query-параметр из генерируемой OpenAPI схемы (а также из системы автоматической генерации документации), укажите в `Query` параметр `include_in_schema=False`:
Чтобы исключить query-параметр из генерируемой OpenAPI-схемы (и, следовательно, из систем автоматической документации), укажите у `Query` параметр `include_in_schema=False`:
{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
## Резюме
## Кастомная валидация { #custom-validation }
Бывают случаи, когда нужна **кастомная валидация**, которую нельзя выразить параметрами выше.
В таких случаях можно использовать **кастомную функцию-валидатор**, которая применяется после обычной валидации (например, после проверки, что значение — это `str`).
Этого можно добиться, используя <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator" class="external-link" target="_blank">`AfterValidator` Pydantic</a> внутри `Annotated`.
/// tip | Совет
В Pydantic также есть <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator" class="external-link" target="_blank">`BeforeValidator`</a> и другие. 🤓
///
Например, эта кастомная проверка убеждается, что ID элемента начинается с `isbn-` для номера книги <abbr title="ISBN означает International Standard Book Number – Международный стандартный книжный номер">ISBN</abbr> или с `imdb-` для ID URL фильма на <abbr title="IMDB (Internet Movie Database) — веб‑сайт с информацией о фильмах">IMDB</abbr>:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
/// info | Дополнительная информация
Это доступно в Pydantic версии 2 и выше. 😎
///
/// tip | Совет
Если вам нужна валидация, требующая общения с каким‑либо **внешним компонентом** — базой данных или другим API — вместо этого используйте **Зависимости FastAPI** (FastAPI Dependencies), вы познакомитесь с ними позже.
Эти кастомные валидаторы предназначены для проверок, которые можно выполнить, имея **только** те же **данные**, что пришли в запросе.
///
### Понимание этого кода { #understand-that-code }
Важный момент — это использовать **`AfterValidator` с функцией внутри `Annotated`**. Смело пропускайте эту часть. 🤸
---
Но если вам любопытен именно этот пример и всё ещё интересно, вот немного подробностей.
#### Строка и `value.startswith()` { #string-with-value-startswith }
Вы можете объявлять дополнительные правила валидации и метаданные для ваших параметров запроса.
Заметили? Метод строки `value.startswith()` может принимать кортеж — тогда будет проверено каждое значение из кортежа:
Общие метаданные:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
#### Случайный элемент { #a-random-item }
С помощью `data.items()` мы получаем <abbr title="Объект, по которому можно итерироваться циклом for, например список, множество и т. п.">итерируемый объект</abbr> с кортежами, содержащими ключ и значение для каждого элемента словаря.
Мы превращаем этот итерируемый объект в обычный `list` через `list(data.items())`.
Затем с `random.choice()` можно получить **случайное значение** из списка — то есть кортеж вида `(id, name)`. Это будет что‑то вроде `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`.
После этого мы **распаковываем** эти два значения кортежа в переменные `id` и `name`.
Так что, если пользователь не передал ID элемента, он всё равно получит случайную рекомендацию.
...и всё это в **одной простой строке**. 🤯 Разве не прекрасен Python? 🐍
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
## Резюме { #recap }
Вы можете объявлять дополнительные проверки и метаданные для параметров.
Общие метаданные и настройки:
* `alias`
* `title`
* `description`
* `deprecated`
* `include_in_schema`
Специфичные правила валидации для строк:
Проверки, специфичные для строк:
* `min_length`
* `max_length`
* `regex`
* `pattern`
Кастомные проверки с использованием `AfterValidator`.
В рассмотренных примерах показано объявление правил валидации для строковых значений `str`.
В этих примерах вы видели, как объявлять проверки для значений типа `str`.
В следующих главах вы увидете, как объявлять правила валидации для других типов (например, чисел).
Смотрите следующие главы, чтобы узнать, как объявлять проверки для других типов, например чисел.

46
docs/ru/docs/tutorial/query-params.md

@ -1,6 +1,6 @@
# Query-параметры
# Query-параметры { #query-parameters }
Когда вы объявляете параметры функции, которые не являются параметрами пути, они автоматически интерпретируются как "query"-параметры.
Когда вы объявляете параметры функции, которые не являются параметрами пути, они автоматически интерпретируются как "query"-параметры.
{* ../../docs_src/query_params/tutorial001.py hl[9] *}
@ -19,7 +19,7 @@ http://127.0.0.1:8000/items/?skip=0&limit=10
Будучи частью URL-адреса, они "по умолчанию" являются строками.
Но когда вы объявляете их с использованием аннотаций (в примере выше, как `int`), они конвертируются в указанный тип данных и проходят проверку на соответствие ему.
Но когда вы объявляете их с использованием типов Python (в примере выше, как `int`), они конвертируются в указанный тип данных и проходят проверку на соответствие ему.
Все те же правила, которые применяются к path-параметрам, также применяются и query-параметрам:
@ -28,7 +28,7 @@ http://127.0.0.1:8000/items/?skip=0&limit=10
* Проверка на соответствие данных (Валидация)
* Автоматическая документация
## Значения по умолчанию
## Значения по умолчанию { #defaults }
Поскольку query-параметры не являются фиксированной частью пути, они могут быть не обязательными и иметь значения по умолчанию.
@ -57,7 +57,7 @@ http://127.0.0.1:8000/items/?skip=20
* `skip=20`: потому что вы установили это в URL-адресе
* `limit=10`: т.к это было значение по умолчанию
## Необязательные параметры
## Необязательные параметры { #optional-parameters }
Аналогично, вы можете объявлять необязательные query-параметры, установив их значение по умолчанию, равное `None`:
@ -71,7 +71,7 @@ http://127.0.0.1:8000/items/?skip=20
///
## Преобразование типа параметра запроса
## Преобразование типа параметра запроса { #query-parameter-type-conversion }
Вы также можете объявлять параметры с типом `bool`, которые будут преобразованы соответственно:
@ -109,10 +109,9 @@ http://127.0.0.1:8000/items/foo?short=yes
или в любом другом варианте написания (в верхнем регистре, с заглавной буквой, и т.п), внутри вашей функции параметр `short` будет иметь значение `True` типа данных `bool` . В противном случае - `False`.
## Смешивание query-параметров и path-параметров { #multiple-path-and-query-parameters }
## Смешивание query-параметров и path-параметров
Вы можете объявлять несколько query-параметров и path-параметров одновременно,**FastAPI** сам разберётся, что чем является.
Вы можете объявлять несколько query-параметров и path-параметров одновременно, **FastAPI** сам разберётся, что чем является.
И вы не обязаны объявлять их в каком-либо определенном порядке.
@ -120,9 +119,9 @@ http://127.0.0.1:8000/items/foo?short=yes
{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *}
## Обязательные query-параметры
## Обязательные query-параметры { #required-query-parameters }
Когда вы объявляете значение по умолчанию для параметра, который не является path-параметром (в этом разделе, мы пока что познакомились только с path-параметрами), то это значение не является обязательным.
Когда вы объявляете значение по умолчанию для параметра, который не является path-параметром (в этом разделе, мы пока что познакомились только с path-параметрами), то он не является обязательным.
Если вы не хотите задавать конкретное значение, но хотите сделать параметр необязательным, вы можете установить значение по умолчанию равным `None`.
@ -142,16 +141,17 @@ http://127.0.0.1:8000/items/foo-item
```JSON
{
"detail": [
{
"loc": [
"query",
"needy"
],
"msg": "field required",
"type": "value_error.missing"
}
]
"detail": [
{
"type": "missing",
"loc": [
"query",
"needy"
],
"msg": "Field required",
"input": null
}
]
}
```
@ -170,7 +170,7 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
}
```
Конечно, вы можете определить некоторые параметры как обязательные, некоторые - со значением по умполчанию, а некоторые - полностью необязательные:
Конечно, вы можете определить некоторые параметры как обязательные, некоторые — со значением по умолчанию, а некоторые — полностью необязательные:
{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *}
@ -182,6 +182,6 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
/// tip | Подсказка
Вы можете использовать класс `Enum` также, как ранее применяли его с [Path-параметрами](path-params.md#_7){.internal-link target=_blank}.
Вы можете использовать класс `Enum` также, как ранее применяли его с [Path-параметрами](path-params.md#predefined-values){.internal-link target=_blank}.
///

39
docs/ru/docs/tutorial/request-files.md

@ -1,4 +1,4 @@
# Загрузка файлов
# Загрузка файлов { #request-files }
Используя класс `File`, мы можем позволить клиентам загружать файлы.
@ -6,19 +6,23 @@
Чтобы получать загруженные файлы, сначала установите <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>.
Например: `pip install python-multipart`.
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его, а затем установили пакет, например:
Это связано с тем, что загружаемые файлы передаются как данные формы.
```console
$ pip install python-multipart
```
Это связано с тем, что загружаемые файлы передаются как "данные формы".
///
## Импорт `File`
## Импорт `File` { #import-file }
Импортируйте `File` и `UploadFile` из модуля `fastapi`:
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[3] *}
## Определите параметры `File`
## Определите параметры `File` { #define-file-parameters }
Создайте параметры `File` так же, как вы это делаете для `Body` или `Form`:
@ -46,7 +50,7 @@
Однако возможны случаи, когда использование `UploadFile` может оказаться полезным.
## Загрузка файла с помощью `UploadFile`
## Параметры файла с `UploadFile` { #file-parameters-with-uploadfile }
Определите параметр файла с типом `UploadFile`:
@ -62,7 +66,7 @@
* Он реализует <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">file-like</a> `async` интерфейс.
* Он предоставляет реальный объект Python <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">`SpooledTemporaryFile`</a> который вы можете передать непосредственно другим библиотекам, которые ожидают файл в качестве объекта.
### `UploadFile`
### `UploadFile` { #uploadfile }
`UploadFile` имеет следующие атрибуты:
@ -70,12 +74,12 @@
* `content_type`: Строка `str` с типом содержимого (MIME type / media type) (например, `image/jpeg`).
* `file`: <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">`SpooledTemporaryFile`</a> (a <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">file-like</a> объект). Это фактический файл Python, который можно передавать непосредственно другим функциям или библиотекам, ожидающим файл в качестве объекта.
`UploadFile` имеет следующие методы `async`. Все они вызывают соответствующие файловые методы (используя внутренний SpooledTemporaryFile).
`UploadFile` имеет следующие методы `async`. Все они вызывают соответствующие файловые методы (используя внутренний `SpooledTemporaryFile`).
* `write(data)`: Записать данные `data` (`str` или `bytes`) в файл.
* `read(size)`: Прочитать количество `size` (`int`) байт/символов из файла.
* `seek(offset)`: Перейти к байту на позиции `offset` (`int`) в файле.
* Наример, `await myfile.seek(0)` перейдет к началу файла.
* Например, `await myfile.seek(0)` перейдет к началу файла.
* Это особенно удобно, если вы один раз выполнили команду `await myfile.read()`, а затем вам нужно прочитать содержимое файла еще раз.
* `close()`: Закрыть файл.
@ -93,6 +97,7 @@ contents = await myfile.read()
contents = myfile.file.read()
```
/// note | Технические детали `async`
При использовании методов `async` **FastAPI** запускает файловые методы в пуле потоков и ожидает их.
@ -105,7 +110,7 @@ contents = myfile.file.read()
///
## Про данные формы ("Form Data")
## Что такое «данные формы» { #what-is-form-data }
Способ, которым HTML-формы (`<form></form>`) отправляют данные на сервер, обычно использует "специальную" кодировку для этих данных, отличную от JSON.
@ -117,7 +122,7 @@ contents = myfile.file.read()
Но когда форма включает файлы, она кодируется как multipart/form-data. Если вы используете `File`, **FastAPI** будет знать, что ему нужно получить файлы из нужной части тела.
Если вы хотите узнать больше об этих кодировках и полях форм, перейдите по ссылке <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network">MDN</abbr> web docs for <code>POST</code></a>.
Если вы хотите узнать больше об этих кодировках и полях форм, перейдите по ссылке <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network – Сеть разработчиков Mozilla">MDN</abbr> web docs for <code>POST</code></a>.
///
@ -129,19 +134,19 @@ contents = myfile.file.read()
///
## Необязательная загрузка файлов
## Необязательная загрузка файлов { #optional-file-upload }
Вы можете сделать загрузку файла необязательной, используя стандартные аннотации типов и установив значение по умолчанию `None`:
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
## `UploadFile` с дополнительными метаданными
## `UploadFile` с дополнительными метаданными { #uploadfile-with-additional-metadata }
Вы также можете использовать `File()` вместе с `UploadFile`, например, для установки дополнительных метаданных:
{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}
## Загрузка нескольких файлов
## Загрузка нескольких файлов { #multiple-file-uploads }
Можно одновременно загружать несколько файлов.
@ -153,7 +158,7 @@ contents = myfile.file.read()
Вы получите, как и было объявлено, список `list` из `bytes` или `UploadFile`.
/// note | Technical Details
/// note | Технические детали
Можно также использовать `from starlette.responses import HTMLResponse`.
@ -161,12 +166,12 @@ contents = myfile.file.read()
///
### Загрузка нескольких файлов с дополнительными метаданными
### Загрузка нескольких файлов с дополнительными метаданными { #multiple-file-uploads-with-additional-metadata }
Так же, как и раньше, вы можете использовать `File()` для задания дополнительных параметров, даже для `UploadFile`:
{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}
## Резюме
## Резюме { #recap }
Используйте `File`, `bytes` и `UploadFile` для работы с файлами, которые будут загружаться и передаваться в виде данных формы.

18
docs/ru/docs/tutorial/request-form-models.md

@ -1,6 +1,6 @@
# Модели форм
# Модели форм { #form-models }
Вы можете использовать **Pydantic-модели** для объявления **полей форм** в FastAPI.
Вы можете использовать **Pydantic-модели** для объявления **полей формы** в FastAPI.
/// info | Дополнительная информация
@ -16,11 +16,11 @@ $ pip install python-multipart
/// note | Заметка
Этот функционал доступен с версии `0.113.0`. 🤓
Этот функционал доступен начиная с версии FastAPI `0.113.0`. 🤓
///
## Pydantic-модель для формы
## Pydantic-модели для форм { #pydantic-models-for-forms }
Вам просто нужно объявить **Pydantic-модель** с полями, которые вы хотите получить как **поля формы**, а затем объявить параметр как `Form`:
@ -28,21 +28,21 @@ $ pip install python-multipart
**FastAPI** **извлечёт** данные для **каждого поля** из **данных формы** в запросе и выдаст вам объявленную Pydantic-модель.
## Проверка сгенерированной документации
## Проверьте документацию { #check-the-docs }
Вы можете посмотреть поля формы в графическом интерфейсе Документации по пути `/docs`:
Вы можете проверить это в интерфейсе документации по адресу `/docs`:
<div class="screenshot">
<img src="/img/tutorial/request-form-models/image01.png">
</div>
## Запрет дополнительных полей формы
## Запрет дополнительных полей формы { #forbid-extra-form-fields }
В некоторых случаях (не особо часто встречающихся) вам может понадобиться **ограничить** поля формы только теми, которые объявлены в Pydantic-модели. И **запретить** любые **дополнительные** поля.
/// note | Заметка
Этот функционал доступен с версии `0.114.0`. 🤓
Этот функционал доступен начиная с версии FastAPI `0.114.0`. 🤓
///
@ -73,6 +73,6 @@ $ pip install python-multipart
}
```
## Заключение
## Итоги { #summary }
Вы можете использовать Pydantic-модели для объявления полей форм в FastAPI. 😎

22
docs/ru/docs/tutorial/request-forms-and-files.md

@ -1,20 +1,24 @@
# Файлы и формы в запросе
# Файлы и формы в запросе { #request-forms-and-files }
Вы можете определять файлы и поля формы одновременно, используя `File` и `Form`.
/// info | Дополнительная информация
/// info | Информация
Чтобы получать загруженные файлы и/или данные форм, сначала установите <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>.
Например: `pip install python-multipart`.
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его, а затем установили пакет, например:
```console
$ pip install python-multipart
```
///
## Импортируйте `File` и `Form`
## Импортируйте `File` и `Form` { #import-file-and-form }
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[3] *}
## Определите параметры `File` и `Form`
## Определите параметры `File` и `Form` { #define-file-and-form-parameters }
Создайте параметры файла и формы таким же образом, как для `Body` или `Query`:
@ -22,16 +26,16 @@
Файлы и поля формы будут загружены в виде данных формы, и вы получите файлы и поля формы.
Вы можете объявить некоторые файлы как `bytes`, а некоторые - как `UploadFile`.
Вы можете объявить некоторые файлы как `bytes`, а некоторые как `UploadFile`.
/// warning | Внимание
Вы можете объявить несколько параметров `File` и `Form` в операции *path*, но вы не можете также объявить поля `Body`, которые вы ожидаете получить в виде JSON, так как запрос будет иметь тело, закодированное с помощью `multipart/form-data` вместо `application/json`.
Вы можете объявить несколько параметров `File` и `Form` в операции пути, но вы не можете также объявить поля `Body`, которые вы ожидаете получить в виде JSON, так как запрос будет иметь тело, закодированное с помощью `multipart/form-data` вместо `application/json`.
Это не ограничение **Fast API**, это часть протокола HTTP.
Это не ограничение **FastAPI**, это часть протокола HTTP.
///
## Резюме
## Резюме { #recap }
Используйте `File` и `Form` вместе, когда необходимо получить данные и файлы в одном запросе.

38
docs/ru/docs/tutorial/request-forms.md

@ -1,4 +1,4 @@
# Данные формы
# Данные формы { #form-data }
Когда вам нужно получить поля формы вместо JSON, вы можете использовать `Form`.
@ -6,64 +6,68 @@
Чтобы использовать формы, сначала установите <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>.
Например, выполните команду `pip install python-multipart`.
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его, а затем установили пакет, например:
```console
$ pip install python-multipart
```
///
## Импорт `Form`
## Импорт `Form` { #import-form }
Импортируйте `Form` из `fastapi`:
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[3] *}
## Определение параметров `Form`
## Определение параметров `Form` { #define-form-parameters }
Создайте параметры формы так же, как это делается для `Body` или `Query`:
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[9] *}
Например, в одном из способов использования спецификации OAuth2 (называемом "потоком пароля") требуется отправить `username` и `password` в виде полей формы.
Например, в одном из способов использования спецификации OAuth2 (называемом «потоком пароля») требуется отправить `username` и `password` в виде полей формы.
Данный способ требует отправку данных для авторизации посредством формы (а не JSON) и обязательного наличия в форме строго именованных полей `username` и `password`.
<abbr title="specification – спецификация">spec</abbr> требует, чтобы поля были строго названы `username` и `password` и отправлялись как поля формы, а не JSON.
Вы можете настроить `Form` точно так же, как настраиваете и `Body` ( `Query`, `Path`, `Cookie`), включая валидации, примеры, псевдонимы (например, `user-name` вместо `username`) и т.д.
С помощью `Form` вы можете объявить те же настройки, что и с `Body` `Query`, `Path`, `Cookie`), включая валидацию, примеры, псевдоним (например, `user-name` вместо `username`) и т.д.
/// info | Дополнительная информация
`Form` - это класс, который наследуется непосредственно от `Body`.
`Form` это класс, который наследуется непосредственно от `Body`.
///
/// tip | Подсказка
Вам необходимо явно указывать параметр `Form` при объявлении каждого поля, иначе поля будут интерпретироваться как параметры запроса или параметры тела (JSON).
Чтобы объявлять данные формы, вам нужно явно использовать `Form`, иначе параметры будут интерпретированы как параметры запроса или параметры тела (JSON).
///
## О "полях формы"
## О «полях формы» { #about-form-fields }
Обычно способ, которым HTML-формы (`<form></form>`) отправляют данные на сервер, использует "специальное" кодирование для этих данных, отличное от JSON.
Обычно способ, которым HTML-формы (`<form></form>`) отправляют данные на сервер, использует «специальное» кодирование для этих данных, отличное от JSON.
**FastAPI** гарантирует правильное чтение этих данных из соответствующего места, а не из JSON.
**FastAPI** гарантирует, что эти данные будут прочитаны из нужного места, а не из JSON.
/// note | Технические детали
Данные из форм обычно кодируются с использованием "типа медиа" `application/x-www-form-urlencoded`.
Данные из форм обычно кодируются с использованием «типа содержимого» `application/x-www-form-urlencoded`.
Но когда форма содержит файлы, она кодируется как `multipart/form-data`. Вы узнаете о работе с файлами в следующей главе.
Но когда форма содержит файлы, она кодируется как `multipart/form-data`. О работе с файлами вы прочтёте в следующей главе.
Если вы хотите узнать больше про кодировки и поля формы, ознакомьтесь с <a href="https://developer.mozilla.org/ru/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank">документацией <abbr title="Mozilla Developer Network">MDN</abbr> для `POST` на веб-сайте</a>.
Если вы хотите узнать больше про эти кодировки и поля формы, обратитесь к <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network – Сеть разработчиков Mozilla">MDN</abbr> веб-документации для `POST`</a>.
///
/// warning | Предупреждение
Вы можете объявлять несколько параметров `Form` в *операции пути*, но вы не можете одновременно с этим объявлять поля `Body`, которые вы ожидаете получить в виде JSON, так как запрос будет иметь тело, закодированное с использованием `application/x-www-form-urlencoded`, а не `application/json`.
Вы можете объявлять несколько параметров `Form` в *операции пути*, но вы не можете одновременно объявлять поля `Body`, которые вы ожидаете получить в виде JSON, так как запрос будет иметь тело, закодированное с использованием `application/x-www-form-urlencoded`, а не `application/json`.
Это не ограничение **FastAPI**, это часть протокола HTTP.
///
## Резюме
## Резюме { #recap }
Используйте `Form` для объявления входных параметров данных формы.

270
docs/ru/docs/tutorial/response-model.md

@ -1,237 +1,247 @@
# Модель ответа - Возвращаемый тип
# Модель ответа — Возвращаемый тип { #response-model-return-type }
Вы можете объявить тип ответа, указав аннотацию **возвращаемого значения** для *функции операции пути*.
Вы можете объявить тип, используемый для ответа, указав аннотацию **возвращаемого значения** для *функции-обработчика пути*.
FastAPI позволяет использовать **аннотации типов** таким же способом, как и для ввода данных в **параметры** функции, вы можете использовать модели Pydantic, списки, словари, скалярные типы (такие, как int, bool и т.д.).
Вы можете использовать **аннотации типов** так же, как и для входных данных в **параметрах** функции: Pydantic-модели, списки, словари, скалярные значения (целые числа, булевы и т.д.).
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
FastAPI будет использовать этот возвращаемый тип для:
FastAPI будет использовать этот тип ответа для:
* **Валидации** ответа.
* Если данные невалидны (например, отсутствует одно из полей), это означает, что код *вашего* приложения работает некорректно и функция возвращает не то, что вы ожидаете. В таком случае приложение вернет server error вместо того, чтобы отправить неправильные данные. Таким образом, вы и ваши пользователи можете быть уверены, что получите корректные данные в том виде, в котором они ожидаются.
* Добавьте **JSON схему** для ответа внутри *операции пути* OpenAPI.
* Она будет использована для **автоматически генерируемой документации**.
* А также - для автоматической кодогенерации пользователями.
* **Валидации** возвращаемых данных.
* Если данные невалидны (например, отсутствует поле), это означает, что код *вашего* приложения работает некорректно и возвращает не то, что должен. В таком случае будет возвращена ошибка сервера вместо неправильных данных. Так вы и ваши клиенты можете быть уверены, что получите ожидаемые данные и ожидаемую структуру.
* Добавления **JSON Schema** для ответа в OpenAPI *операции пути*.
* Это будет использовано **автоматической документацией**.
* Это также будет использовано инструментами автоматической генерации клиентского кода.
Но самое важное:
Но самое главное:
* Ответ будет **ограничен и отфильтрован** - т.е. в нем останутся только те данные, которые определены в возвращаемом типе.
* Это особенно важно для **безопасности**, далее мы рассмотрим эту тему подробнее.
* Выходные данные будут **ограничены и отфильтрованы** в соответствии с тем, что определено в возвращаемом типе.
* Это особенно важно для **безопасности**, ниже мы рассмотрим это подробнее.
## Параметр `response_model`
## Параметр `response_model` { #response-model-parameter }
Бывают случаи, когда вам необходимо (или просто хочется) возвращать данные, которые не полностью соответствуют объявленному типу.
Бывают случаи, когда вам нужно или хочется возвращать данные, которые не в точности соответствуют объявленному типу.
Допустим, вы хотите, чтобы ваша функция **возвращала словарь (dict)** или объект из базы данных, но при этом **объявляете выходной тип как модель Pydantic**. Тогда именно указанная модель будет использована для автоматической документации, валидации и т.п. для объекта, который вы вернули (например, словаря или объекта из базы данных).
Например, вы можете хотеть **возвращать словарь (dict)** или объект из базы данных, но **объявить его как Pydantic-модель**. Тогда Pydantic-модель выполнит документирование данных, валидацию и т.п. для объекта, который вы вернули (например, словаря или объекта из базы данных).
Но если указать аннотацию возвращаемого типа, статическая проверка типов будет выдавать ошибку (абсолютно корректную в данном случае). Она будет говорить о том, что ваша функция должна возвращать данные одного типа (например, dict), а в аннотации вы объявили другой тип (например, модель Pydantic).
Если вы добавите аннотацию возвращаемого типа, инструменты и редакторы кода начнут жаловаться (и будут правы), что функция возвращает тип (например, dict), отличный от объявленного (например, Pydantic-модель).
В таком случае можно использовать параметр `response_model` внутри *декоратора операции пути* вместо аннотации возвращаемого значения функции.
В таких случаях вместо аннотации возвращаемого типа можно использовать параметр `response_model` у *декоратора операции пути*.
Параметр `response_model` может быть указан для любой *операции пути*:
Параметр `response_model` можно указать у любой *операции пути*:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* и др.
* и т.д.
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *}
/// note | Технические детали
/// note | Примечание
Помните, что параметр `response_model` является параметром именно декоратора http-методов (`get`, `post`, и т.п.). Не следует его указывать для *функций операций пути*, как вы бы поступили с другими параметрами или с телом запроса.
Обратите внимание, что `response_model` — это параметр метода «декоратора» (`get`, `post` и т.д.), а не вашей *функции-обработчика пути*, в которой указываются параметры и тело запроса.
///
`response_model` принимает те же типы, которые можно указать для какого-либо поля в модели Pydantic. Таким образом, это может быть как одиночная модель Pydantic, так и `список (list)` моделей Pydantic. Например, `List[Item]`.
`response_model` принимает тот же тип, что вы бы объявили для поля Pydantic-модели, то есть это может быть одна Pydantic-модель, а может быть, например, `list` Pydantic-моделей, как `List[Item]`.
FastAPI будет использовать значение `response_model` для того, чтобы автоматически генерировать документацию, производить валидацию и т.п. А также для **конвертации и фильтрации выходных данных** в объявленный тип.
FastAPI будет использовать `response_model` для документации, валидации и т. п., а также для **конвертации и фильтрации выходных данных** к объявленному типу.
/// tip | Подсказка
/// tip | Совет
Если вы используете анализаторы типов со строгой проверкой (например, mypy), можно указать `Any` в качестве типа возвращаемого значения функции.
Если у вас в редакторе кода, mypy и т. п. включены строгие проверки типов, вы можете объявить возвращаемый тип функции как `Any`.
Таким образом вы информируете ваш редактор кода, что намеренно возвращаете данные неопределенного типа. Но возможности FastAPI, такие как автоматическая генерация документации, валидация, фильтрация и т.д. все так же будут работать, просто используя параметр `response_model`.
Так вы сообщите редактору, что намеренно возвращаете что угодно. Но FastAPI всё равно выполнит документацию данных, валидацию, фильтрацию и т.д. с помощью `response_model`.
///
### Приоритет `response_model`
### Приоритет `response_model` { #response-model-priority }
Если одновременно указать аннотацию типа для ответа функции и параметр `response_model` - последний будет иметь больший приоритет и FastAPI будет использовать именно его.
Если вы объявите и возвращаемый тип, и `response_model`, приоритет будет у `response_model`, именно его использует FastAPI.
Таким образом вы можете объявить корректные аннотации типов к вашим функциям, даже если они возвращают тип, отличающийся от указанного в `response_model`. Они будут считаны во время статической проверки типов вашими помощниками, например, mypy. При этом вы все так же используете возможности FastAPI для автоматической документации, валидации и т.д. благодаря `response_model`.
Так вы можете добавить корректные аннотации типов к своим функциям, даже если фактически возвращаете тип, отличный от модели ответа, чтобы ими пользовались редактор и инструменты вроде mypy. И при этом FastAPI продолжит выполнять валидацию данных, документацию и т.д. с использованием `response_model`.
Вы можете указать значение `response_model=None`, чтобы отключить создание модели ответа для данной *операции пути*. Это может понадобиться, если вы добавляете аннотации типов для данных, не являющихся валидными полями Pydantic. Мы увидим пример кода для такого случая в одном из разделов ниже.
Вы также можете указать `response_model=None`, чтобы отключить создание модели ответа для данной *операции пути*. Это может понадобиться, если вы добавляете аннотации типов для вещей, не являющихся валидными полями Pydantic. Пример вы увидите ниже.
## Получить и вернуть один и тот же тип данных
## Вернуть те же входные данные { #return-the-same-input-data }
Здесь мы объявили модель `UserIn`, которая хранит пользовательский пароль в открытом виде:
Здесь мы объявляем модель `UserIn`, она будет содержать пароль в открытом виде:
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *}
/// info | Информация
Чтобы использовать `EmailStr`, прежде необходимо установить <a href="https://github.com/JoshData/python-email-validator" class="external-link" target="_blank">`email-validator`</a>.
Используйте `pip install email-validator`
или `pip install pydantic[email]`.
Чтобы использовать `EmailStr`, сначала установите <a href="https://github.com/JoshData/python-email-validator" class="external-link" target="_blank">`email-validator`</a>.
Создайте [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активируйте его и затем установите пакет, например:
```console
$ pip install email-validator
```
или так:
```console
$ pip install "pydantic[email]"
```
///
Далее мы используем нашу модель в аннотациях типа как для аргумента функции, так и для выходного значения:
И мы используем эту модель для объявления входных данных, и ту же модель — для объявления выходных данных:
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *}
Теперь всякий раз, когда клиент создает пользователя с паролем, API будет возвращать его пароль в ответе.
Теперь, когда браузер создаёт пользователя с паролем, API вернёт тот же пароль в ответе.
В данном случае это не такая уж большая проблема, поскольку ответ получит тот же самый пользователь, который и создал пароль.
В этом случае это может быть не проблемой, так как пароль отправляет тот же пользователь.
Но что если мы захотим использовать эту модель для какой-либо другой *операции пути*? Мы можем, сами того не желая, отправить пароль любому другому пользователю.
Но если мы используем ту же модель в другой *операции пути*, мы можем начать отправлять пароли пользователей каждому клиенту.
/// danger | Осторожно
Никогда не храните пароли пользователей в открытом виде, а также никогда не возвращайте их в ответе, как в примере выше. В противном случае - убедитесь, что вы хорошо продумали и учли все возможные риски такого подхода и вам известно, что вы делаете.
Никогда не храните пароль пользователя в открытом виде и не отправляйте его в ответе подобным образом, если только вы не понимаете всех рисков и точно знаете, что делаете.
///
## Создание модели для ответа
## Добавить модель для ответа { #add-an-output-model }
Вместо этого мы можем создать входную модель, хранящую пароль в открытом виде и выходную модель без пароля:
Вместо этого мы можем создать входную модель с паролем в открытом виде и выходную модель без него:
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *}
В таком случае, даже несмотря на то, что наша *функция операции пути* возвращает тот же самый объект пользователя с паролем, полученным на вход:
Здесь, хотя *функция-обработчик пути* возвращает тот же входной объект пользователя, содержащий пароль:
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *}
...мы указали в `response_model` модель `UserOut`, в которой отсутствует поле, содержащее пароль - и он будет исключен из ответа:
...мы объявили `response_model` как модель `UserOut`, в которой нет пароля:
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *}
Таким образом **FastAPI** позаботится о фильтрации ответа и исключит из него всё, что не указано в выходной модели (при помощи Pydantic).
Таким образом, **FastAPI** позаботится о том, чтобы отфильтровать все данные, не объявленные в выходной модели (используя Pydantic).
### `response_model` или возвращаемый тип данных
### `response_model` или возвращаемый тип { #response-model-or-return-type }
В нашем примере модели входных данных и выходных данных различаются. И если мы укажем аннотацию типа выходного значения функции как `UserOut` - проверка типов выдаст ошибку из-за того, что мы возвращаем некорректный тип. Поскольку это 2 разных класса.
В этом случае, поскольку две модели различаются, если бы мы аннотировали возвращаемый тип функции как `UserOut`, редактор и инструменты пожаловались бы, что мы возвращаем неверный тип, так как это разные классы.
Поэтому в нашем примере мы можем объявить тип ответа только в параметре `response_model`.
Поэтому в этом примере мы должны объявить тип ответа в параметре `response_model`.
...но продолжайте читать дальше, чтобы узнать как можно это обойти.
...но читайте дальше, чтобы узнать, как это обойти.
## Возвращаемый тип и Фильтрация данных
## Возвращаемый тип и фильтрация данных { #return-type-and-data-filtering }
Продолжим рассматривать предыдущий пример. Мы хотели **аннотировать входные данные одним типом**, а выходное значение - **другим типом**.
Продолжим предыдущий пример. Мы хотели **аннотировать функцию одним типом**, но при этом иметь возможность вернуть из функции что-то, что фактически включает **больше данных**.
Мы хотим, чтобы FastAPI продолжал **фильтровать** данные, используя `response_model`.
Мы хотим, чтобы FastAPI продолжал **фильтровать** данные с помощью модели ответа. Так что, даже если функция возвращает больше данных, в ответ будут включены только поля, объявленные в модели ответа.
В прошлом примере, т.к. входной и выходной типы являлись разными классами, мы были вынуждены использовать параметр `response_model`. И как следствие, мы лишались помощи статических анализаторов для проверки ответа функции.
В предыдущем примере, поскольку классы были разными, нам пришлось использовать параметр `response_model`. Но это также означает, что мы теряем поддержку от редактора и инструментов, проверяющих возвращаемый тип функции.
Но в подавляющем большинстве случаев мы будем хотеть, чтобы модель ответа лишь **фильтровала/удаляла** некоторые данные из ответа, как в нашем примере.
Однако в большинстве таких случаев нам нужно лишь **отфильтровать/убрать** некоторые данные, как в этом примере.
И в таких случаях мы можем использовать классы и наследование, чтобы пользоваться преимуществами **аннотаций типов** и получать более полную статическую проверку типов. Но при этом все так же получать **фильтрацию ответа** от FastAPI.
И в этих случаях мы можем использовать классы и наследование, чтобы воспользоваться **аннотациями типов** функций для лучшей поддержки в редакторе и инструментах и при этом получить **фильтрацию данных** от FastAPI.
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *}
Таким образом, мы получаем поддержку редактора кода и mypy в части типов, сохраняя при этом фильтрацию данных от FastAPI.
Так мы получаем поддержку инструментов (редакторы, mypy) — код корректен с точки зрения типов — и одновременно получаем фильтрацию данных от FastAPI.
Как это возможно? Давайте разберемся. 🤓
Как это работает? Давайте разберёмся. 🤓
### Аннотации типов и инструменты для их проверки
### Аннотации типов и инструменты { #type-annotations-and-tooling }
Для начала давайте рассмотрим как наш редактор кода, mypy и другие помощники разработчика видят аннотации типов.
Сначала посмотрим, как это увидят редакторы, mypy и другие инструменты.
У модели `BaseUser` есть некоторые поля. Затем `UserIn` наследуется от `BaseUser` и добавляет новое поле `password`. Таким образом модель будет включать в себя все поля из первой модели (родителя), а также свои собственные.
`BaseUser` содержит базовые поля. Затем `UserIn` наследуется от `BaseUser` и добавляет поле `password`, то есть он включает все поля обеих моделей.
Мы аннотируем возвращаемый тип функции как `BaseUser`, но фактически мы будем возвращать объект типа `UserIn`.
Мы аннотируем возвращаемый тип функции как `BaseUser`, но фактически возвращаем экземпляр `UserIn`.
Редакторы, mypy и другие инструменты не будут иметь возражений против такого подхода, поскольку `UserIn` является подклассом `BaseUser`. Это означает, что такой тип будет *корректным*, т.к. ответ может быть чем угодно, если это будет `BaseUser`.
Редактор, mypy и другие инструменты не будут возражать, потому что с точки зрения типов `UserIn` — подкласс `BaseUser`, что означает, что это *валидный* тип везде, где ожидается что-то, являющееся `BaseUser`.
### Фильтрация Данных FastAPI
### Фильтрация данных FastAPI { #fastapi-data-filtering }
FastAPI знает тип ответа функции, так что вы можете быть уверены, что на выходе будут **только** те поля, которые вы указали.
Теперь, для FastAPI: он увидит возвращаемый тип и убедится, что то, что вы возвращаете, включает **только** поля, объявленные в этом типе.
FastAPI совместно с Pydantic выполнит некоторую магию "под капотом", чтобы убедиться, что те же самые правила наследования классов не используются для фильтрации возвращаемых данных, в противном случае вы могли бы в конечном итоге вернуть гораздо больше данных, чем ожидали.
FastAPI делает несколько вещей внутри вместе с Pydantic, чтобы гарантировать, что те же правила наследования классов не используются для фильтрации возвращаемых данных, иначе вы могли бы вернуть гораздо больше данных, чем ожидали.
Таким образом, вы можете получить все самое лучшее из обоих миров: аннотации типов с **поддержкой инструментов для разработки** и **фильтрацию данных**.
Таким образом вы получаете лучшее из обоих миров: аннотации типов с **поддержкой инструментов** и **фильтрацию данных**.
## Автоматическая документация
## Посмотреть в документации { #see-it-in-the-docs }
Если посмотреть на сгенерированную документацию, вы можете убедиться, что в ней присутствуют обе JSON схемы - как для входной модели, так и для выходной:
В автоматической документации вы увидите, что у входной и выходной моделей есть свои JSON Schema:
<img src="/img/tutorial/response-model/image01.png">
И также обе модели будут использованы в интерактивной документации API:
И обе модели используются в интерактивной документации API:
<img src="/img/tutorial/response-model/image02.png">
## Другие аннотации типов
## Другие аннотации возвращаемых типов { #other-return-type-annotations }
Бывают случаи, когда вы возвращаете что-то, что не является валидным типом для Pydantic и вы указываете аннотацию ответа функции только для того, чтобы работала поддержка различных инструментов (редактор кода, mypy и др.).
Бывают случаи, когда вы возвращаете что-то, что не является валидным полем Pydantic, и аннотируете это в функции только ради поддержки инструментов (редактор, mypy и т. д.).
### Возвращаем Response
### Возврат Response напрямую { #return-a-response-directly }
Самый частый сценарий использования - это [возвращать Response напрямую, как описано в расширенной документации](../advanced/response-directly.md){.internal-link target=_blank}.
Самый распространённый случай — [возвращать Response напрямую, как описано далее в разделах для продвинутых](../advanced/response-directly.md){.internal-link target=_blank}.
{* ../../docs_src/response_model/tutorial003_02.py hl[8,10:11] *}
Это поддерживается FastAPI по-умолчанию, т.к. аннотация проставлена в классе (или подклассе) `Response`.
Этот простой случай обрабатывается FastAPI автоматически, потому что аннотация возвращаемого типа — это класс (или подкласс) `Response`.
И ваши помощники разработки также будут счастливы, т.к. оба класса `RedirectResponse` и `JSONResponse` являются подклассами `Response`. Таким образом мы получаем корректную аннотацию типа.
И инструменты тоже будут довольны, потому что и `RedirectResponse`, и `JSONResponse` являются подклассами `Response`, так что аннотация типа корректна.
### Подкласс Response в аннотации типа
### Аннотировать подкласс Response { #annotate-a-response-subclass }
Вы также можете указать подкласс `Response` в аннотации типа:
Вы также можете использовать подкласс `Response` в аннотации типа:
{* ../../docs_src/response_model/tutorial003_03.py hl[8:9] *}
Это сработает, потому что `RedirectResponse` является подклассом `Response` и FastAPI автоматически обработает этот простейший случай.
Это тоже сработает, так как `RedirectResponse` — подкласс `Response`, и FastAPI автоматически обработает этот случай.
### Некорректные аннотации типов
### Некорректные аннотации возвращаемых типов { #invalid-return-type-annotations }
Но когда вы возвращаете какой-либо другой произвольный объект, который не является допустимым типом Pydantic (например, объект из базы данных), и вы аннотируете его подобным образом для функции, FastAPI попытается создать из этого типа модель Pydantic и потерпит неудачу.
Но когда вы возвращаете произвольный объект, не являющийся валидным типом Pydantic (например, объект базы данных), и аннотируете его таким образом в функции, FastAPI попытается создать модель ответа Pydantic из этой аннотации типа и потерпит неудачу.
То же самое произошло бы, если бы у вас было что-то вроде <abbr title='Union разных типов буквально означает "любой из перечисленных типов".'>Union</abbr> различных типов и один или несколько из них не являлись бы допустимыми типами для Pydantic. Например, такой вариант приведет к ошибке 💥:
То же произойдёт, если у вас будет что-то вроде <abbr title='Объединение нескольких типов означает «любой из этих типов».'>union</abbr> разных типов, где один или несколько не являются валидными типами Pydantic, например, это приведёт к ошибке 💥:
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
...такой код вызовет ошибку, потому что в аннотации указан неподдерживаемый Pydantic тип. А также этот тип не является классом или подклассом `Response`.
...это не сработает, потому что аннотация типа не является типом Pydantic и это не единственный класс `Response` или его подкласс, а объединение (`union`) из `Response` и `dict`.
### Возможно ли отключить генерацию модели ответа?
### Отключить модель ответа { #disable-response-model }
Продолжим рассматривать предыдущий пример. Допустим, что вы хотите отказаться от автоматической валидации ответа, документации, фильтрации и т.д.
Продолжая пример выше, вы можете не хотеть использовать стандартную валидацию данных, документацию, фильтрацию и т.д., выполняемые FastAPI.
Но в то же время, хотите сохранить аннотацию возвращаемого типа для функции, чтобы обеспечить работу помощников и анализаторов типов (например, mypy).
Но при этом вы можете хотеть сохранить аннотацию возвращаемого типа в функции, чтобы пользоваться поддержкой инструментов (редакторы, проверки типов вроде mypy).
В таком случае, вы можете отключить генерацию модели ответа, указав `response_model=None`:
В этом случае вы можете отключить генерацию модели ответа, установив `response_model=None`:
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
Тогда FastAPI не станет генерировать модель ответа и вы сможете сохранить такую аннотацию типа, которая вам требуется, никак не влияя на работу FastAPI. 🤓
Так FastAPI пропустит генерацию модели ответа, и вы сможете использовать любые аннотации возвращаемых типов, не влияя на ваше приложение FastAPI. 🤓
## Параметры модели ответа
## Параметры кодирования модели ответа { #response-model-encoding-parameters }
Модель ответа может иметь значения по умолчанию, например:
У вашей модели ответа могут быть значения по умолчанию, например:
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *}
* `description: Union[str, None] = None` (или `str | None = None` в Python 3.10), где `None` является значением по умолчанию.
* `tax: float = 10.5`, где `10.5` является значением по умолчанию.
* `tags: List[str] = []`, где пустой список `[]` является значением по умолчанию.
* `description: Union[str, None] = None` (или `str | None = None` в Python 3.10) имеет значение по умолчанию `None`.
* `tax: float = 10.5` имеет значение по умолчанию `10.5`.
* `tags: List[str] = []` имеет значение по умолчанию пустого списка: `[]`.
но вы, возможно, хотели бы исключить их из ответа, если данные поля не были заданы явно.
но вы можете захотеть опустить их в результате, если они фактически не были сохранены.
Например, у вас есть модель с множеством необязательных полей в NoSQL базе данных, но вы не хотите отправлять в качестве ответа очень длинный JSON с множеством значений по умолчанию.
Например, если у вас есть модели с множеством необязательных атрибутов в NoSQL-базе данных, но вы не хотите отправлять очень длинные JSON-ответы, заполненные значениями по умолчанию.
### Используйте параметр `response_model_exclude_unset`
### Используйте параметр `response_model_exclude_unset` { #use-the-response-model-exclude-unset-parameter }
Установите для *декоратора операции пути* параметр `response_model_exclude_unset=True`:
Вы можете установить у *декоратора операции пути* параметр `response_model_exclude_unset=True`:
{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *}
и тогда значения по умолчанию не будут включены в ответ. В нем будут только те поля, значения которых фактически были установлены.
и эти значения по умолчанию не будут включены в ответ — только те значения, которые действительно были установлены.
Итак, если вы отправите запрос на данную *операцию пути* для элемента, с ID = `Foo` - ответ (с исключенными значениями по-умолчанию) будет таким:
Итак, если вы отправите запрос к этой *операции пути* для элемента с ID `foo`, ответ (без значений по умолчанию) будет таким:
```JSON
{
@ -242,7 +252,15 @@ FastAPI совместно с Pydantic выполнит некоторую ма
/// info | Информация
"Под капотом" FastAPI использует метод `.dict()` у объектов моделей Pydantic <a href="https://docs.pydantic.dev/latest/concepts/serialization/#modeldict" class="external-link" target="_blank">с параметром `exclude_unset`</a>, чтобы достичь такого эффекта.
В Pydantic v1 метод назывался `.dict()`, в Pydantic v2 он был помечен как устаревший (но всё ещё поддерживается) и переименован в `.model_dump()`.
Примеры здесь используют `.dict()` для совместимости с Pydantic v1, но если вы используете Pydantic v2, применяйте `.model_dump()`.
///
/// info | Информация
FastAPI использует метод `.dict()` у Pydantic-моделей с <a href="https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict" class="external-link" target="_blank">параметром `exclude_unset`</a>, чтобы добиться такого поведения.
///
@ -253,13 +271,13 @@ FastAPI совместно с Pydantic выполнит некоторую ма
* `response_model_exclude_defaults=True`
* `response_model_exclude_none=True`
как описано в <a href="https://docs.pydantic.dev/latest/concepts/serialization/#modeldict" class="external-link" target="_blank">документации Pydantic</a> для параметров `exclude_defaults` и `exclude_none`.
как описано в <a href="https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict" class="external-link" target="_blank">документации Pydantic</a> для `exclude_defaults` и `exclude_none`.
///
#### Если значение поля отличается от значения по-умолчанию
#### Данные со значениями для полей, имеющих значения по умолчанию { #data-with-values-for-fields-with-defaults }
Если для некоторых полей модели, имеющих значения по-умолчанию, значения были явно установлены - как для элемента с ID = `Bar`, ответ будет таким:
Но если в ваших данных есть значения для полей модели, для которых указаны значения по умолчанию, как у элемента с ID `bar`:
```Python hl_lines="3 5"
{
@ -270,11 +288,11 @@ FastAPI совместно с Pydantic выполнит некоторую ма
}
```
они не будут исключены из ответа.
они будут включены в ответ.
#### Если значение поля совпадает с его значением по умолчанию
#### Данные с такими же значениями, как значения по умолчанию { #data-with-the-same-values-as-the-defaults }
Если данные содержат те же значения, которые являются для этих полей по умолчанию, но были установлены явно - как для элемента с ID = `baz`, ответ будет таким:
Если данные имеют те же значения, что и значения по умолчанию, как у элемента с ID `baz`:
```Python hl_lines="3 5-6"
{
@ -286,54 +304,54 @@ FastAPI совместно с Pydantic выполнит некоторую ма
}
```
FastAPI достаточно умен (на самом деле, это заслуга Pydantic), чтобы понять, что, хотя `description`, `tax` и `tags` хранят такие же данные, какие должны быть по умолчанию - для них эти значения были установлены явно (а не получены из значений по умолчанию).
FastAPI достаточно умен (на самом деле, это Pydantic), чтобы понять, что хотя `description`, `tax` и `tags` совпадают со значениями по умолчанию, они были установлены явно (а не взяты из значений по умолчанию).
И поэтому, они также будут включены в JSON ответа.
Поэтому они тоже будут включены в JSON-ответ.
/// tip | Подсказка
/// tip | Совет
Значением по умолчанию может быть что угодно, не только `None`.
Обратите внимание, что значения по умолчанию могут быть любыми, не только `None`.
Им может быть и список (`[]`), значение 10.5 типа `float`, и т.п.
Это может быть список (`[]`), число с плавающей точкой `10.5` и т. д.
///
### `response_model_include` и `response_model_exclude`
### `response_model_include` и `response_model_exclude` { #response-model-include-and-response-model-exclude }
Вы также можете использовать параметры *декоратора операции пути*, такие, как `response_model_include` и `response_model_exclude`.
Вы также можете использовать параметры *декоратора операции пути* `response_model_include` и `response_model_exclude`.
Они принимают аргументы типа `set`, состоящий из строк (`str`) с названиями атрибутов, которые либо требуется включить в ответ (при этом исключив все остальные), либо наоборот исключить (оставив в ответе все остальные поля).
Они принимают `set` из `str` с именами атрибутов, которые нужно включить (исключив остальные) или исключить (оставив остальные).
Это можно использовать как быстрый способ исключить данные из ответа, не создавая отдельную модель Pydantic.
Это можно использовать как быстрый способ, если у вас только одна Pydantic-модель и вы хотите убрать часть данных из ответа.
/// tip | Подсказка
/// tip | Совет
Но по-прежнему рекомендуется следовать изложенным выше советам и использовать несколько моделей вместо данных параметров.
Но всё же рекомендуется использовать подходы выше — несколько классов — вместо этих параметров.
Потому как JSON схема OpenAPI, генерируемая вашим приложением (а также документация) все еще будет содержать все поля, даже если вы использовали `response_model_include` или `response_model_exclude` и исключили некоторые атрибуты.
Потому что JSON Schema, генерируемая в OpenAPI вашего приложения (и документации), всё равно будет соответствовать полной модели, даже если вы используете `response_model_include` или `response_model_exclude`, чтобы опустить некоторые атрибуты.
То же самое применимо к параметру `response_model_by_alias`.
То же относится к `response_model_by_alias`, который работает аналогично.
///
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *}
/// tip | Подсказка
/// tip | Совет
При помощи кода `{"name","description"}` создается объект множества (`set`) с двумя строковыми значениями.
Синтаксис `{"name", "description"}` создаёт `set` с этими двумя значениями.
Того же самого можно достичь используя `set(["name", "description"])`.
Это эквивалентно `set(["name", "description"])`.
///
#### Что если использовать `list` вместо `set`?
#### Использование `list` вместо `set` { #using-lists-instead-of-sets }
Если вы забыли про `set` и использовали структуру `list` или `tuple`, FastAPI автоматически преобразует этот объект в `set`, чтобы обеспечить корректную работу:
Если вы забыли использовать `set` и применили `list` или `tuple`, FastAPI всё равно преобразует это в `set`, и всё будет работать корректно:
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
## Резюме
## Резюме { #recap }
Используйте параметр `response_model` у *декоратора операции пути* для того, чтобы задать модель ответа и в большей степени для того, чтобы быть уверенным, что приватная информация будет отфильтрована.
Используйте параметр `response_model` у *декоратора операции пути*, чтобы задавать модели ответа, и особенно — чтобы приватные данные отфильтровывались.
А также используйте `response_model_exclude_unset`, чтобы возвращать только те значения, которые были заданы явно.
Используйте `response_model_exclude_unset`, чтобы возвращать только те значения, которые были установлены явно.

38
docs/ru/docs/tutorial/response-status-code.md

@ -1,6 +1,6 @@
# HTTP коды статуса ответа
# Статус-код ответа { #response-status-code }
Вы можете задать HTTP код статуса ответа с помощью параметра `status_code` подобно тому, как вы определяете схему ответа в любой из *операций пути*:
Подобно тому, как вы можете задать модель/схему ответа, вы можете объявить HTTP статус-код, используемый для ответа, с помощью параметра `status_code` в любой из *операций пути*:
* `@app.get()`
* `@app.post()`
@ -12,11 +12,11 @@
/// note | Примечание
Обратите внимание, что `status_code` является атрибутом метода-декоратора (`get`, `post` и т.д.), а не *функции-обработчика пути* в отличие от всех остальных параметров и тела запроса.
Обратите внимание, что `status_code` — это параметр метода-декоратора (`get`, `post` и т.д.), а не вашей *функции-обработчика пути*, в отличие от всех остальных параметров и тела запроса.
///
Параметр `status_code` принимает число, обозначающее HTTP код статуса ответа.
Параметр `status_code` принимает число, обозначающее HTTP статус-код.
/// info | Информация
@ -27,7 +27,7 @@
Это позволит:
* Возвращать указанный код статуса в ответе.
* Документировать его как код статуса ответа в OpenAPI схеме (а значит, и в пользовательском интерфейсе):
* Документировать его как код статуса ответа в OpenAPI схеме (а значит, и в пользовательских интерфейсах):
<img src="/img/tutorial/response-status-code/image01.png">
@ -39,11 +39,11 @@ FastAPI знает об этом и создаст документацию Open
///
## Об HTTP кодах статуса ответа
## Об HTTP статус-кодах { #about-http-status-codes }
/// note | Примечание
Если вы уже знаете, что представляют собой HTTP коды статуса ответа, можете перейти к следующему разделу.
Если вы уже знаете, что представляют собой HTTP статус-коды, можете перейти к следующему разделу.
///
@ -51,26 +51,26 @@ FastAPI знает об этом и создаст документацию Open
У кодов статуса есть названия, чтобы упростить их распознавание, но важны именно числовые значения.
Кратко о значениях кодов:
Кратко:
* `1XX` – статус-коды информационного типа. Они редко используются разработчиками напрямую. Ответы с этими кодами не могут иметь тела.
* **`2XX`** – статус-коды, сообщающие об успешной обработке запроса. Они используются чаще всего.
* `100 - 199` – статус-коды информационного типа. Они редко используются разработчиками напрямую. Ответы с этими кодами не могут иметь тела.
* **`200 - 299`** – статус-коды, сообщающие об успешной обработке запроса. Они используются чаще всего.
* `200` – это код статуса ответа по умолчанию, который означает, что все прошло "OK".
* Другим примером может быть статус `201`, "Created". Он обычно используется после создания новой записи в базе данных.
* Особый случай – `204`, "No Content". Этот статус ответа используется, когда нет содержимого для возврата клиенту, и поэтому ответ не должен иметь тела.
* **`3XX`** – статус-коды, сообщающие о перенаправлениях. Ответы с этими кодами статуса могут иметь или не иметь тело, за исключением ответов со статусом `304`, "Not Modified", у которых не должно быть тела.
* **`4XX`** – статус-коды, сообщающие о клиентской ошибке. Это ещё одна наиболее часто используемая категория.
* Особый случай – `204`, "No Content". Этот статус ответа используется, когда нет содержимого для возврата клиенту, и поэтому ответ не должен иметь тела.
* **`300 - 399`** – статус-коды, сообщающие о перенаправлениях. Ответы с этими кодами статуса могут иметь или не иметь тело, за исключением ответов со статусом `304`, "Not Modified", у которых не должно быть тела.
* **`400 - 499`** – статус-коды, сообщающие о клиентской ошибке. Это ещё одна наиболее часто используемая категория.
* Пример – код `404` для статуса "Not Found".
* Для общих ошибок со стороны клиента можно просто использовать код `400`.
* `5XX` – статус-коды, сообщающие о серверной ошибке. Они почти никогда не используются разработчиками напрямую. Когда что-то идет не так в какой-то части кода вашего приложения или на сервере, он автоматически вернёт один из 5XX кодов.
* `500 - 599`статус-коды, сообщающие о серверной ошибке. Они почти никогда не используются разработчиками напрямую. Когда что-то идет не так в какой-то части кода вашего приложения или на сервере, он автоматически вернёт один из этих кодов статуса.
/// tip | Подсказка
Чтобы узнать больше о HTTP кодах статуса и о том, для чего каждый из них предназначен, ознакомьтесь с <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank">документацией <abbr title="Mozilla Developer Network">MDN</abbr> об HTTP кодах статуса ответа</a>.
Чтобы узнать больше о HTTP кодах статуса и о том, для чего каждый из них предназначен, ознакомьтесь с <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank"><abbr title="Mozilla Developer Network – Сеть разработчиков Mozilla">MDN</abbr> документацией об HTTP статус-кодах</a>.
///
## Краткие обозначения для запоминания названий кодов
## Краткие обозначения для запоминания названий кодов { #shortcut-to-remember-the-names }
Рассмотрим предыдущий пример еще раз:
@ -84,7 +84,7 @@ FastAPI знает об этом и создаст документацию Open
{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *}
Они содержат те же числовые значения, но позволяют использовать подсказки редактора для выбора кода статуса:
Они содержат те же числовые значения, но позволяют использовать автозавершение редактора кода для выбора кода статуса:
<img src="/img/tutorial/response-status-code/image02.png">
@ -96,6 +96,6 @@ FastAPI знает об этом и создаст документацию Open
///
## Изменение кода статуса по умолчанию
## Изменение кода статуса по умолчанию { #changing-the-default }
Позже, в [Руководстве для продвинутых пользователей](../advanced/response-change-status-code.md){.internal-link target=_blank}, вы узнаете, как возвращать HTTP коды статуса, отличные от используемого здесь кода статуса по умолчанию.
Позже, в [Руководстве для продвинутых пользователей](../advanced/response-change-status-code.md){.internal-link target=_blank}, вы узнаете, как возвращать HTTP статус-код, отличный от значения по умолчанию, которое вы объявляете здесь.

204
docs/ru/docs/tutorial/schema-extra-example.md

@ -1,42 +1,70 @@
# Объявление примера запроса данных
# Объявление примеров данных запроса { #declare-request-example-data }
Вы можете объявлять примеры данных, которые ваше приложение может получать.
Вот несколько способов, как это можно сделать.
Вот несколько способов, как это сделать.
## Pydantic `schema_extra`
## Дополнительные данные JSON Schema в моделях Pydantic { #extra-json-schema-data-in-pydantic-models }
Вы можете объявить ключ `example` для модели Pydantic, используя класс `Config` и переменную `schema_extra`, как описано в <a href="https://docs.pydantic.dev/latest/concepts/json_schema/#schema-customization" class="external-link" target="_blank">Pydantic документации: Настройка схемы</a>:
Вы можете объявить `examples` для модели Pydantic, которые будут добавлены в сгенерированную JSON Schema.
{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:21] *}
//// tab | Pydantic v2
Эта дополнительная информация будет включена в **JSON Schema** выходных данных для этой модели, и она будет использоваться в документации к API.
{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *}
////
//// tab | Pydantic v1
{* ../../docs_src/schema_extra_example/tutorial001_pv1_py310.py hl[13:23] *}
////
Эта дополнительная информация будет добавлена как есть в выходную **JSON Schema** этой модели и будет использоваться в документации API.
//// tab | Pydantic v2
В Pydantic версии 2 вы будете использовать атрибут `model_config`, который принимает `dict`, как описано в <a href="https://docs.pydantic.dev/latest/api/config/" class="external-link" target="_blank">Документации Pydantic: Конфигурация</a>.
Вы можете задать `"json_schema_extra"` с `dict`, содержащим любые дополнительные данные, которые вы хотите видеть в сгенерированной JSON Schema, включая `examples`.
////
//// tab | Pydantic v1
В Pydantic версии 1 вы будете использовать внутренний класс `Config` и `schema_extra`, как описано в <a href="https://docs.pydantic.dev/1.10/usage/schema/#schema-customization" class="external-link" target="_blank">Документации Pydantic: Настройка схемы</a>.
Вы можете задать `schema_extra` со `dict`, содержащим любые дополнительные данные, которые вы хотите видеть в сгенерированной JSON Schema, включая `examples`.
////
/// tip | Подсказка
Вы можете использовать тот же метод для расширения JSON-схемы и добавления своей собственной дополнительной информации.
Вы можете использовать тот же приём, чтобы расширить JSON Schema и добавить свою собственную дополнительную информацию.
Например, вы можете использовать это для добавления дополнительной информации для пользовательского интерфейса в вашем веб-приложении и т.д.
Например, вы можете использовать это, чтобы добавить метаданные для фронтенд‑пользовательского интерфейса и т.д.
///
## Дополнительные аргументы поля `Field`
/// info | Информация
При использовании `Field()` с моделями Pydantic, вы также можете объявлять дополнительную информацию для **JSON Schema**, передавая любые другие произвольные аргументы в функцию.
OpenAPI 3.1.0 (используется начиная с FastAPI 0.99.0) добавил поддержку `examples`, который является частью стандарта **JSON Schema**.
Вы можете использовать это, чтобы добавить аргумент `example` для каждого поля:
До этого поддерживалось только ключевое слово `example` с одним примером. Оно всё ещё поддерживается в OpenAPI 3.1.0, но помечено как устаревшее и не является частью стандарта JSON Schema. Поэтому рекомендуется мигрировать `example` на `examples`. 🤓
{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *}
Подробнее — в конце этой страницы.
/// warning | Внимание
///
Имейте в виду, что эти дополнительные переданные аргументы не добавляют никакой валидации, только дополнительную информацию для документации.
## Дополнительные аргументы `Field` { #field-additional-arguments }
///
При использовании `Field()` с моделями Pydantic вы также можете объявлять дополнительные `examples`:
## Использование `example` и `examples` в OpenAPI
{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *}
## `examples` в JSON Schema — OpenAPI { #examples-in-json-schema-openapi }
При использовании любой из этих функций:
При использовании любой из функций:
* `Path()`
* `Query()`
@ -46,65 +74,151 @@
* `Form()`
* `File()`
вы также можете добавить аргумент, содержащий `example` или группу `examples` с дополнительной информацией, которая будет добавлена в **OpenAPI**.
вы также можете объявить набор `examples` с дополнительной информацией, которая будет добавлена в их **JSON Schema** внутри **OpenAPI**.
### Параметр `Body` с аргументом `example`
### `Body` с `examples` { #body-with-examples }
Здесь мы передаём аргумент `example`, как пример данных ожидаемых в параметре `Body()`:
Здесь мы передаём `examples`, содержащий один пример данных, ожидаемых в `Body()`:
{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:27] *}
{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *}
### Аргумент "example" в UI документации
### Пример в UI документации { #example-in-the-docs-ui }
С любым из вышеуказанных методов это будет выглядеть так в `/docs`:
С любым из перечисленных выше методов это будет выглядеть так в `/docs`:
<img src="/img/tutorial/body-fields/image01.png">
### `Body` с аргументом `examples`
### `Body` с несколькими `examples` { #body-with-multiple-examples }
Конечно, вы можете передать и несколько `examples`:
{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *}
Когда вы делаете это, примеры становятся частью внутренней **JSON Schema** для данных тела запроса.
Тем не менее, на <abbr title="2023-08-26">момент написания этого</abbr> Swagger UI, инструмент, отвечающий за отображение UI документации, не поддерживает показ нескольких примеров для данных в **JSON Schema**. Но ниже есть обходной путь.
### Специфические для OpenAPI `examples` { #openapi-specific-examples }
Ещё до того как **JSON Schema** поддержала `examples`, в OpenAPI была поддержка другого поля, также называемого `examples`.
Эти **специфические для OpenAPI** `examples` находятся в другой секции спецификации OpenAPI. Они находятся в **подробностях для каждой операции пути (обработчика пути)**, а не внутри каждого объекта Schema.
И Swagger UI уже какое‑то время поддерживает именно это поле `examples`. Поэтому вы можете использовать его, чтобы **отобразить** разные **примеры в UI документации**.
Структура этого специфичного для OpenAPI поля `examples` — это `dict` с **несколькими примерами** (вместо `list`), каждый с дополнительной информацией, которая также будет добавлена в **OpenAPI**.
Это не помещается внутрь каждого объекта Schema в OpenAPI, это находится снаружи, непосредственно на уровне самой *операции пути*.
### Использование параметра `openapi_examples` { #using-the-openapi-examples-parameter }
Вы можете объявлять специфические для OpenAPI `examples` в FastAPI с помощью параметра `openapi_examples` для:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
В качестве альтернативы одному аргументу `example`, вы можете передавать `examples` используя тип данных `dict` с **несколькими примерами**, каждый из которых содержит дополнительную информацию, которая также будет добавлена в **OpenAPI**.
Ключи `dict` идентифицируют каждый пример, а каждое значение — это ещё один `dict`.
Ключи `dict` указывают на каждый пример, а значения для каждого из них - на еще один тип `dict` с дополнительной информацией.
Каждый конкретный пример‑`dict` в `examples` может содержать:
Каждый конкретный пример типа `dict` в аргументе `examples` может содержать:
* `summary`: Краткое описание примера.
* `description`: Подробное описание, которое может содержать текст в Markdown.
* `value`: Это фактический пример, который отображается, например, `dict`.
* `externalValue`: альтернатива `value`, URL, указывающий на пример. Хотя это может поддерживаться не так многими инструментами, как `value`.
* `summary`: Краткое описание для примера.
* `description`: Полное описание, которое может содержать текст в формате Markdown.
* `value`: Это конкретный пример, который отображается, например, в виде типа `dict`.
* `externalValue`: альтернатива параметру `value`, URL-адрес, указывающий на пример. Хотя это может не поддерживаться таким же количеством инструментов разработки и тестирования API, как параметр `value`.
Использовать это можно так:
{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:49] *}
{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *}
### Аргумент "examples" в UI документации
### OpenAPI-примеры в UI документации { #openapi-examples-in-the-docs-ui }
С аргументом `examples`, добавленным в `Body()`, страница документации `/docs` будет выглядеть так:
С `openapi_examples`, добавленным в `Body()`, страница `/docs` будет выглядеть так:
<img src="/img/tutorial/body-fields/image02.png">
## Технические Детали
## Технические детали { #technical-details }
/// tip | Подсказка
Если вы уже используете **FastAPI** версии **0.99.0 или выше**, вы, вероятно, можете **пропустить** эти подробности.
Они более актуальны для старых версий, до того как стала доступна OpenAPI 3.1.0.
Считайте это кратким **уроком истории** про OpenAPI и JSON Schema. 🤓
///
/// warning | Внимание
Эти технические детали относятся к стандартам **JSON Schema** и **OpenAPI**.
Далее идут очень технические подробности о стандартах **JSON Schema** и **OpenAPI**.
Если предложенные выше идеи уже работают для вас, возможно этого будет достаточно и эти детали вам не потребуются, можете спокойно их пропустить.
Если идеи выше уже работают для вас, этого может быть достаточно, и, вероятно, вам не нужны эти детали — смело пропускайте их.
///
Когда вы добавляете пример внутрь модели Pydantic, используя `schema_extra` или `Field(example="something")`, этот пример добавляется в **JSON Schema** для данной модели Pydantic.
До OpenAPI 3.1.0 OpenAPI использовала более старую и модифицированную версию **JSON Schema**.
В JSON Schema не было `examples`, поэтому OpenAPI добавила собственное поле `example` в свою модифицированную версию.
OpenAPI также добавила поля `example` и `examples` в другие части спецификации:
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object" class="external-link" target="_blank">`Parameter Object` (в спецификации)</a>, которое использовалось в FastAPI:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object" class="external-link" target="_blank">`Request Body Object`, в поле `content`, в `Media Type Object` (в спецификации)</a>, которое использовалось в FastAPI:
* `Body()`
* `File()`
* `Form()`
/// info | Информация
Этот старый специфичный для OpenAPI параметр `examples` теперь называется `openapi_examples`, начиная с FastAPI `0.103.0`.
///
### Поле `examples` в JSON Schema { #json-schemas-examples-field }
Позже в новой версии спецификации JSON Schema было добавлено поле <a href="https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5" class="external-link" target="_blank">`examples`</a>.
А затем новый OpenAPI 3.1.0 был основан на последней версии (JSON Schema 2020-12), которая включала это новое поле `examples`.
И теперь это новое поле `examples` имеет приоритет над старым одиночным (и кастомным) полем `example`, которое теперь устарело.
Это новое поле `examples` в JSON Schema — это **просто `list`** примеров, а не dict с дополнительными метаданными, как в других местах OpenAPI (описанных выше).
/// info | Информация
Даже после того как OpenAPI 3.1.0 была выпущена с этой новой, более простой интеграцией с JSON Schema, какое‑то время Swagger UI, инструмент, предоставляющий автоматическую документацию, не поддерживал OpenAPI 3.1.0 (поддержка появилась начиная с версии 5.0.0 🎉).
Из‑за этого версии FastAPI до 0.99.0 всё ещё использовали версии OpenAPI ниже 3.1.0.
///
### `examples` в Pydantic и FastAPI { #pydantic-and-fastapi-examples }
Когда вы добавляете `examples` внутри модели Pydantic, используя `schema_extra` или `Field(examples=["something"])`, этот пример добавляется в **JSON Schema** для этой модели Pydantic.
И эта **JSON Schema** модели Pydantic включается в **OpenAPI** вашего API, а затем используется в UI документации.
Поля `example` как такового не существует в стандартах **JSON Schema**. В последних версиях JSON-схемы определено поле <a href="https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5" class="external-link" target="_blank">`examples`</a>, но OpenAPI 3.0.3 основан на более старой версии JSON-схемы, которая не имела поля `examples`.
В версиях FastAPI до 0.99.0 (0.99.0 и выше используют новый OpenAPI 3.1.0), когда вы использовали `example` или `examples` с любыми другими утилитами (`Query()`, `Body()`, и т.д.), эти примеры не добавлялись в JSON Schema, описывающую эти данные (даже в собственную версию JSON Schema OpenAPI), они добавлялись непосредственно в объявление *операции пути* в OpenAPI (вне частей OpenAPI, использующих JSON Schema).
Но теперь, когда FastAPI 0.99.0 и выше используют OpenAPI 3.1.0, который использует JSON Schema 2020-12, а также Swagger UI 5.0.0 и выше, всё стало более последовательным, и примеры включаются в JSON Schema.
Таким образом, OpenAPI 3.0.3 определяет своё собственное поле <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#fixed-fields-20" class="external-link" target="_blank">`example`</a> для модифицированной версии **JSON Schema**, которую он использует чтобы достичь той же цели (однако это именно поле `example`, а не `examples`), и именно это используется API в UI документации (с интеграцией Swagger UI).
### Swagger UI и специфичные для OpenAPI `examples` { #swagger-ui-and-openapi-specific-examples }
Итак, хотя поле `example` не является частью JSON-схемы, оно является частью настраиваемой версии JSON-схемы в OpenAPI, и именно это поле будет использоваться в UI документации.
Раньше, поскольку Swagger UI не поддерживал несколько примеров JSON Schema (по состоянию на 2023-08-26), у пользователей не было способа показать несколько примеров в документации.
Однако, когда вы используете поле `example` или `examples` с любой другой функцией (`Query()`, `Body()`, и т.д.), эти примеры не добавляются в JSON-схему, которая описывает эти данные (даже в собственную версию JSON-схемы OpenAPI), они добавляются непосредственно в объявление *операции пути* в OpenAPI (вне частей OpenAPI, которые используют JSON-схему).
Чтобы решить это, FastAPI `0.103.0` **добавил поддержку** объявления того же старого, **специфичного для OpenAPI**, поля `examples` с новым параметром `openapi_examples`. 🤓
Для функций `Path()`, `Query()`, `Header()`, и `Cookie()`, аргументы `example` или `examples` добавляются в <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#parameter-object" class="external-link" target="_blank">определение OpenAPI, к объекту `Parameter Object` (в спецификации)</a>.
### Итог { #summary }
И для функций `Body()`, `File()` и `Form()` аргументы `example` или `examples` аналогично добавляются в <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#mediaTypeObject" class="external-link" target="_blank"> определение OpenAPI, к объекту `Request Body Object`, в поле `content` в объекте `Media Type Object` (в спецификации)</a>.
Раньше я говорил, что не очень люблю историю... а теперь вот рассказываю «уроки технической истории». 😅
С другой стороны, существует более новая версия OpenAPI: **3.1.0**, недавно выпущенная. Она основана на последней версии JSON-схемы и большинство модификаций из OpenAPI JSON-схемы удалены в обмен на новые возможности из последней версии JSON-схемы, так что все эти мелкие отличия устранены. Тем не менее, Swagger UI в настоящее время не поддерживает OpenAPI 3.1.0, поэтому пока лучше продолжать использовать вышеупомянутые методы.
Коротко: **обновитесь до FastAPI 0.99.0 или выше** — так всё будет значительно **проще, последовательнее и интуитивнее**, и вам не придётся знать все эти исторические подробности. 😎

152
docs/ru/docs/tutorial/security/first-steps.md

@ -1,52 +1,58 @@
# Безопасность - первые шаги
# Безопасность — первые шаги { #security-first-steps }
Представим, что у вас есть свой **бэкенд** API на некотором домене.
Представим, что у вас есть **бэкенд** API на некотором домене.
И у вас есть **фронтенд** на другом домене или на другом пути того же домена (или в мобильном приложении).
И вы хотите иметь возможность аутентификации фронтенда с бэкендом, используя **имя пользователя** и **пароль**.
И вы хотите, чтобы фронтенд мог аутентифицироваться на бэкенде, используя **имя пользователя** и **пароль**.
Мы можем использовать **OAuth2** для создания такой системы с помощью **FastAPI**.
Мы можем использовать **OAuth2**, чтобы построить это с **FastAPI**.
Но давайте избавим вас от необходимости читать всю длинную спецификацию, чтобы найти те небольшие кусочки информации, которые вам нужны.
Но давайте сэкономим вам время на чтение всей длинной спецификации в поисках тех небольших фрагментов информации, которые вам нужны.
Для работы с безопасностью воспользуемся средствами, предоставленными **FastAPI**.
Воспользуемся инструментами, предоставленными **FastAPI**, чтобы работать с безопасностью.
## Как это выглядит
## Как это выглядит { #how-it-looks }
Давайте сначала просто воспользуемся кодом и посмотрим, как он работает, а затем детально разберём, что происходит.
Сначала просто воспользуемся кодом и посмотрим, как он работает, а затем вернемся и разберемся, что происходит.
## Создание `main.py`
## Создание `main.py` { #create-main-py }
Скопируйте пример в файл `main.py`:
{* ../../docs_src/security/tutorial001_an_py39.py *}
## Запуск
## Запуск { #run-it }
/// info | Дополнительная информация
Вначале, установите библиотеку <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>.
Пакет <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a> автоматически устанавливается вместе с **FastAPI**, если вы запускаете команду `pip install "fastapi[standard]"`.
А именно: `pip install python-multipart`.
Однако, если вы используете команду `pip install fastapi`, пакет `python-multipart` по умолчанию не включается.
Это связано с тем, что **OAuth2** использует "данные формы" для передачи `имени пользователя` и `пароля`.
Чтобы установить его вручную, убедитесь, что вы создали [виртуальное окружение](../../virtual-environments.md){.internal-link target=_blank}, активировали его и затем установили пакет:
```console
$ pip install python-multipart
```
Это связано с тем, что **OAuth2** использует «данные формы» для отправки `username` и `password`.
///
Запустите ваш сервер:
Запустите пример командой:
<div class="termy">
```console
$ uvicorn main:app --reload
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
## Проверка
## Проверка { #check-it }
Перейдите к интерактивной документации по адресу: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
@ -56,73 +62,75 @@ $ uvicorn main:app --reload
/// check | Кнопка авторизации!
У вас уже появилась новая кнопка "Authorize".
У вас уже появилась новая кнопка «Authorize».
А у *операции пути* теперь появился маленький замочек в правом верхнем углу, на который можно нажать.
А у вашей *операции пути* в правом верхнем углу есть маленький замочек, на который можно нажать.
///
При нажатии на нее появляется небольшая форма авторизации, в которую нужно ввести `имя пользователя` и `пароль` (и другие необязательные поля):
Если нажать на нее, появится небольшая форма авторизации, в которую нужно ввести `username` и `password` (и другие необязательные поля):
<img src="/img/tutorial/security/image02.png">
/// note | Технические детали
/// note | Примечание
Неважно, что вы введете в форму, она пока не будет работать. Но мы к этому еще придем.
Неважно, что вы введете в форму — пока это не будет работать. Но мы скоро до этого дойдем.
///
Конечно, это не фронтенд для конечных пользователей, но это отличный автоматический инструмент для интерактивного документирования всех ваших API.
Конечно, это не фронтенд для конечных пользователей, но это отличный автоматический инструмент для интерактивного документирования всего вашего API.
Он может использоваться командой фронтенда (которой можете быть и вы сами).
Им может пользоваться команда фронтенда (которой можете быть и вы сами).
Он может быть использован сторонними приложениями и системами.
Им могут пользоваться сторонние приложения и системы.
Кроме того, его можно использовать самостоятельно для отладки, проверки и тестирования одного и того же приложения.
И им также можете пользоваться вы сами, чтобы отлаживать, проверять и тестировать то же самое приложение.
## Аутентификация по паролю
## «`password` flow» (аутентификация по паролю) { #the-password-flow }
Теперь давайте вернемся немного назад и разберемся, что же это такое.
Теперь давайте немного вернемся и разберемся, что это все такое.
Аутентификация по паролю является одним из способов, определенных в OAuth2, для обеспечения безопасности и аутентификации.
«`password` flow» — это один из способов («flows»), определенных в OAuth2, для обеспечения безопасности и аутентификации.
OAuth2 был разработан для того, чтобы бэкэнд или API были независимы от сервера, который аутентифицирует пользователя.
OAuth2 был спроектирован так, чтобы бэкенд или API были независимы от сервера, который аутентифицирует пользователя.
Но в нашем случае одно и то же приложение **FastAPI** будет работать с API и аутентификацией.
Но в нашем случае одно и то же приложение **FastAPI** будет работать и с API, и с аутентификацией.
Итак, рассмотрим его с этой упрощенной точки зрения:
Итак, рассмотрим это с упрощенной точки зрения:
* Пользователь вводит на фронтенде `имя пользователя` и `пароль` и нажимает `Enter`.
* Фронтенд (работающий в браузере пользователя) отправляет эти `имя пользователя` и `пароль` на определенный URL в нашем API (объявленный с помощью параметра `tokenUrl="token"`).
* API проверяет эти `имя пользователя` и `пароль` и выдает в ответ "токен" (мы еще не реализовали ничего из этого).
* "Токен" - это просто строка с некоторым содержимым, которое мы можем использовать позже для верификации пользователя.
* Обычно срок действия токена истекает через некоторое время.
* Таким образом, пользователю придется снова войти в систему в какой-то момент времени.
* И если токен будет украден, то риск будет меньше, так как он не похож на постоянный ключ, который будет работать вечно (в большинстве случаев).
* Фронтенд временно хранит этот токен в каком-то месте.
* Пользователь щелкает мышью на фронтенде, чтобы перейти в другой раздел на фронтенде.
* Фронтенду необходимо получить дополнительные данные из API.
* Но для этого необходима аутентификация для конкретной конечной точки.
* Поэтому для аутентификации в нашем API он посылает заголовок `Authorization` со значением `Bearer` плюс сам токен.
* Если токен содержит `foobar`, то содержание заголовка `Authorization` будет таким: `Bearer foobar`.
* Пользователь вводит на фронтенде `username` и `password` и нажимает `Enter`.
* Фронтенд (работающий в браузере пользователя) отправляет эти `username` и `password` на конкретный URL в нашем API (объявленный с `tokenUrl="token"`).
* API проверяет этот `username` и `password` и отвечает «токеном» (мы еще ничего из этого не реализовали).
* «Токен» — это просто строка с некоторым содержимым, которое мы сможем позже использовать для проверки этого пользователя.
* Обычно у токена установлен срок действия: он истекает через некоторое время.
* Поэтому пользователю придется снова войти в систему в какой‑то момент.
* И если токен украдут, риск меньше: это не постоянный ключ, который будет работать вечно (в большинстве случаев).
* Фронтенд временно где‑то хранит этот токен.
* Пользователь кликает во фронтенде, чтобы перейти в другой раздел веб‑приложения.
* Фронтенду нужно получить дополнительные данные из API.
* Но для этого для конкретной конечной точки нужна аутентификация.
* Поэтому, чтобы аутентифицироваться в нашем API, он отправляет HTTP-заголовок `Authorization` со значением `Bearer ` плюс сам токен.
* Если токен содержит `foobar`, то содержимое заголовка `Authorization` будет: `Bearer foobar`.
## Класс `OAuth2PasswordBearer` в **FastAPI**
## Класс `OAuth2PasswordBearer` в **FastAPI** { #fastapis-oauth2passwordbearer }
**FastAPI** предоставляет несколько средств на разных уровнях абстракции для реализации этих функций безопасности.
В данном примере мы будем использовать **OAuth2**, с аутентификацией по паролю, используя токен **Bearer**. Для этого мы используем класс `OAuth2PasswordBearer`.
В этом примере мы будем использовать **OAuth2**, с потоком **Password**, используя токен **Bearer**. Для этого мы используем класс `OAuth2PasswordBearer`.
/// info | Дополнительная информация
Токен "bearer" - не единственный вариант, но для нашего случая он является наилучшим.
Токен «bearer» — не единственный вариант.
Но для нашего случая он — лучший.
И это может быть лучшим вариантом для большинства случаев использования, если только вы не являетесь экспертом в области OAuth2 и точно знаете, почему вам лучше подходит какой-то другой вариант.
И он может быть лучшим для большинства случаев использования, если только вы не являетесь экспертом по OAuth2 и точно знаете, почему другой вариант лучше подходит под ваши нужды.
В этом случае **FastAPI** также предоставляет инструменты для его реализации.
В этом случае **FastAPI** также предоставляет инструменты, чтобы его реализовать.
///
При создании экземпляра класса `OAuth2PasswordBearer` мы передаем в него параметр `tokenUrl`. Этот параметр содержит URL, который клиент (фронтенд, работающий в браузере пользователя) будет использовать для отправки `имени пользователя` и `пароля` с целью получения токена.
При создании экземпляра класса `OAuth2PasswordBearer` мы передаем параметр `tokenUrl`. Этот параметр содержит URL, который клиент (фронтенд, работающий в браузере пользователя) будет использовать для отправки `username` и `password`, чтобы получить токен.
{* ../../docs_src/security/tutorial001_an_py39.py hl[8] *}
@ -130,27 +138,27 @@ OAuth2 был разработан для того, чтобы бэкэнд ил
Здесь `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, если ваш 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 важно для того, чтобы ваше приложение продолжало работать даже в таком продвинутом случае, как [За прокси-сервером](../../advanced/behind-a-proxy.md){.internal-link target=_blank}.
///
Этот параметр не создает конечную точку / *операцию пути*, а объявляет, что URL `/token` будет таким, который клиент должен использовать для получения токена. Эта информация используется в OpenAPI, а затем в интерактивных системах документации API.
Этот параметр не создает конечную точку / *операцию пути*, а объявляет, что URL `/token` — это тот, который клиент должен использовать для получения токена. Эта информация используется в OpenAPI, а затем в интерактивных системах документации по API.
Вскоре мы создадим и саму операцию пути.
Скоро мы также создадим и саму операцию пути.
/// info | Дополнительная информация
Если вы очень строгий "питонист", то вам может не понравиться стиль названия параметра `tokenUrl` вместо `token_url`.
Если вы очень строгий «питонист», вам может не понравиться стиль имени параметра `tokenUrl` вместо `token_url`.
Это связано с тем, что тут используется то же имя, что и в спецификации OpenAPI. Таким образом, если вам необходимо более подробно изучить какую-либо из этих схем безопасности, вы можете просто использовать копирование/вставку, чтобы найти дополнительную информацию о ней.
Это потому, что используется то же имя, что и в спецификации OpenAPI. Так, если вам нужно разобраться подробнее в какой‑то из этих схем безопасности, вы можете просто скопировать и вставить это имя, чтобы найти больше информации.
///
Переменная `oauth2_scheme` является экземпляром `OAuth2PasswordBearer`, но она также является "вызываемой".
Переменная `oauth2_scheme` — это экземпляр `OAuth2PasswordBearer`, но она также «вызываемая».
Ее можно вызвать следующим образом:
Ее можно вызвать так:
```Python
oauth2_scheme(some, parameters)
@ -158,38 +166,38 @@ oauth2_scheme(some, parameters)
Поэтому ее можно использовать вместе с `Depends`.
### Использование
### Использование { #use-it }
Теперь вы можете передать ваш `oauth2_scheme` в зависимость с помощью `Depends`.
Теперь вы можете передать `oauth2_scheme` как зависимость с `Depends`.
{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
Эта зависимость будет предоставлять `строку`, которая присваивается параметру `token` в *функции операции пути*.
Эта зависимость предоставит `str`, который будет присвоен параметру `token` *функции-обработчика пути*.
**FastAPI** будет знать, что он может использовать эту зависимость для определения "схемы безопасности" в схеме OpenAPI (и автоматической документации по API).
**FastAPI** будет знать, что может использовать эту зависимость для определения «схемы безопасности» в схеме OpenAPI (и в автоматической документации по API).
/// info | Технические детали
**FastAPI** будет знать, что он может использовать класс `OAuth2PasswordBearer` (объявленный в зависимости) для определения схемы безопасности в OpenAPI, поскольку он наследуется от `fastapi.security.oauth2.OAuth2`, который, в свою очередь, наследуется от `fastapi.security.base.SecurityBase`.
**FastAPI** будет знать, что может использовать класс `OAuth2PasswordBearer` (объявленный в зависимости) для определения схемы безопасности в OpenAPI, потому что он наследуется от `fastapi.security.oauth2.OAuth2`, который, в свою очередь, наследуется от `fastapi.security.base.SecurityBase`.
Все утилиты безопасности, интегрируемые в OpenAPI (и автоматическая документация по API), наследуются от `SecurityBase`, поэтому **FastAPI** может знать, как интегрировать их в OpenAPI.
Все утилиты безопасности, интегрируемые с OpenAPI (и автоматической документацией по API), наследуются от `SecurityBase`, — так **FastAPI** понимает, как интегрировать их в OpenAPI.
///
## Что он делает
## Что он делает { #what-it-does }
Он будет искать в запросе заголовок `Authorization` и проверять, содержит ли он значение `Bearer` с некоторым токеном, и возвращать токен в виде `строки`.
Он будет искать в запросе заголовок `Authorization`, проверять, что его значение — это `Bearer ` плюс некоторый токен, и вернет токен как `str`.
Если он не видит заголовка `Authorization` или значение не имеет токена `Bearer`, то в ответ будет выдана ошибка с кодом состояния 401 (`UNAUTHORIZED`).
Если заголовок `Authorization` отсутствует или его значение не содержит токен `Bearer `, он сразу ответит ошибкой со статус-кодом 401 (`UNAUTHORIZED`).
Для возврата ошибки даже не нужно проверять, существует ли токен. Вы можете быть уверены, что если ваша функция была выполнена, то в этом токене есть `строка`.
Вам даже не нужно проверять наличие токена, чтобы вернуть ошибку. Вы можете быть уверены: если ваша функция была выполнена, в этом токене будет `str`.
Проверить это можно уже сейчас в интерактивной документации:
Это уже можно попробовать в интерактивной документации:
<img src="/img/tutorial/security/image03.png">
Мы пока не проверяем валидность токена, но для начала неплохо.
Мы пока не проверяем валидность токена, но для начала это уже неплохо.
## Резюме
## Резюме { #recap }
Таким образом, всего за 3-4 дополнительные строки вы получаете некую примитивную форму защиты.
Таким образом, всего за 3–4 дополнительные строки у вас уже есть некая примитивная форма защиты.

82
docs/ru/docs/tutorial/security/get-current-user.md

@ -1,99 +1,105 @@
# Данные текущего пользователя
# Получить текущего пользователя { #get-current-user }
В предыдущей главе система безопасности (основанная на системе внедрения зависимостей) передавала *функции, обрабатывающей эндпоинт,* `токен` в виде `строки`:
В предыдущей главе система безопасности (основанная на системе внедрения зависимостей) передавала *функции-обработчику пути* `token` типа `str`:
{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
Это пока что не слишком нам полезно. Давайте изменим код так, чтобы он возвращал нам данные пользователя, отправившего запрос.
Но это всё ещё не слишком полезно.
## Создание модели пользователя
Сделаем так, чтобы она возвращала текущего пользователя.
## Создать модель пользователя { #create-a-user-model }
Сначала создадим Pydantic-модель пользователя.
Точно так же, как мы использовали Pydantic для объявления тел запросов, мы можем использовать его где угодно:
Точно так же, как мы используем Pydantic для объявления тел запросов, мы можем использовать его где угодно:
{* ../../docs_src/security/tutorial002_an_py310.py hl[5,12:6] *}
## Создание зависимости `get_current_user`
## Создать зависимость `get_current_user` { #create-a-get-current-user-dependency }
Давайте создадим зависимость `get_current_user`.
Помните, что у зависимостей могут быть подзависимости?
`get_current_user` как раз будет иметь подзависимость `oauth2_scheme`, которую мы создали ранее.
`get_current_user` будет иметь зависимость от того же `oauth2_scheme`, который мы создали ранее.
Аналогично тому, как мы делали это ранее в *обработчике эндпоинта* наша новая зависимость `get_current_user` будет получать `token` в виде `строки` от подзависимости `oauth2_scheme`:
Аналогично тому, как мы делали ранее прямо в *операции пути*, новая зависимость `get_current_user` получит `token` типа `str` от подзависимости `oauth2_scheme`:
{* ../../docs_src/security/tutorial002_an_py310.py hl[25] *}
## Получение данных пользователя
## Получить пользователя { #get-the-user }
`get_current_user` будет использовать созданную нами (ненастоящую) служебную функцию, которая принимает токен в виде `строки` и возвращает нашу Pydantic-модель `User`:
`get_current_user` будет использовать созданную нами (ненастоящую) служебную функцию, которая принимает токен типа `str` и возвращает нашу Pydantic-модель `User`:
{* ../../docs_src/security/tutorial002_an_py310.py hl[19:22,26:27] *}
## Внедрение зависимости текущего пользователя
## Внедрить текущего пользователя { #inject-the-current-user }
Теперь мы можем использовать тот же `Depends` с нашей зависимостью `get_current_user` в *функции обрабатывающей эндпоинт*:
Теперь мы можем использовать тот же `Depends` с нашей `get_current_user` в *операции пути*:
{* ../../docs_src/security/tutorial002_an_py310.py hl[31] *}
Обратите внимание, что мы объявляем тип переменной `current_user` как Pydantic-модель `User`.
Обратите внимание, что мы объявляем тип `current_user` как Pydantic-модель `User`.
Это поможет выполнить внутри функции все проверки автозаполнения и типа.
Это поможет внутри функции с автозавершением и проверками типов.
/// tip | Подсказка
Возможно, вы помните, что тело запроса также объявляется с помощью Pydantic-моделей.
В этом месте у **FastAPI** не возникнет проблем, потому что вы используете `Depends`.
Возможно, вы помните, что тела запросов также объявляются с помощью Pydantic-моделей.
Здесь **FastAPI** не запутается, потому что вы используете `Depends`.
///
/// check | Заметка
То, как устроена эта система зависимостей, позволяет нам иметь различные зависимости, которые возвращают модель `User`.
Мы не ограничены наличием только одной зависимости, которая может возвращать данные такого типа.
То, как устроена эта система зависимостей, позволяет иметь разные зависимости, которые возвращают модель `User`.
Мы не ограничены наличием только одной зависимости, которая может возвращать такой тип данных.
///
## Другие модели
## Другие модели { #other-models }
Теперь вы можете получать информацию о текущем пользователе непосредственно в *функции обрабатывающей эндпоинт* и работать с механизмами безопасности на уровне **Внедрения Зависимостей**, используя `Depends`.
Теперь вы можете получать текущего пользователя напрямую в *функциях-обработчиках пути* и работать с механизмами безопасности на уровне **внедрения зависимостей**, используя `Depends`.
Причем для обеспечения требований безопасности можно использовать любую модель или данные (в данном случае - Pydantic-модель `User`).
И вы можете использовать любую модель или данные для требований безопасности (в данном случае Pydantic-модель `User`).
Но вы не ограничены использованием какой-то конкретной моделью данных, классом или типом.
Но вы не ограничены использованием какой-то конкретной модели данных, класса или типа.
Вы хотите использовать в модели `id` и `email`, а `username` вам не нужен? Ну разумеется. Воспользуйтесь тем же инструментарием.
Хотите, чтобы в модели были `id` и `email`, но не было `username`? Пожалуйста. Можно использовать те же инструменты.
Вам нужны только `строки`? Или только `словари`? Или непосредственно экземпляр модели класса базы данных? Все это работает точно также.
Хотите просто `str`? Или просто `dict`? Или напрямую экземпляр класса модели базы данных? Всё работает одинаково.
У вас нет пользователей, которые входят в ваше приложение, а только роботы, боты или другие системы, у которых есть только токен доступа? Опять же, все работает одинаково.
У вас вообще нет пользователей, которые входят в приложение, а есть роботы, боты или другие системы, у которых есть только токен доступа? Снова — всё работает так же.
Просто используйте любую модель, любой класс, любую базу данных, которые нужны для вашего приложения. Система внедрения зависимостей **FastAPI** поможет вам в этом.
Просто используйте любую модель, любой класс, любую базу данных, которые нужны вашему приложению. Система внедрения зависимостей **FastAPI** поможет вам в этом.
## Размер кода
## Размер кода { #code-size }
Этот пример может показаться многословным. Следует иметь в виду, что в одном файле мы смешиваем безопасность, модели данных, служебные функции и *эндпоинты*.
Этот пример может показаться многословным. Имейте в виду, что в одном файле мы смешиваем безопасность, модели данных, служебные функции и *операции пути*.
Но вот ключевой момент:
Но вот ключевой момент.
Все, что касается безопасности и внедрения зависимостей, пишется один раз.
Всё, что касается безопасности и внедрения зависимостей, пишется один раз.
И вы можете сделать его настолько сложным, насколько захотите. И все это будет написано только один раз, в одном месте, со всей своей гибкостью.
И вы можете сделать это настолько сложным, насколько захотите. И всё равно это будет написано только один раз, в одном месте. Со всей гибкостью.
И у вас могут быть тысячи конечных точек (*эндпоинтов*), использующих одну и ту же систему безопасности.
При этом у вас могут быть тысячи эндпоинтов (*операций пути*), использующих одну и ту же систему безопасности.
И все они (или любая их часть по вашему желанию) могут воспользоваться преимуществами повторного использования этих зависимостей или любых других зависимостей, которые вы создадите.
И все эти тысячи *эндпоинтов* могут составлять всего 3 строки:
И все эти тысячи *операций пути* могут состоять всего из 3 строк:
{* ../../docs_src/security/tutorial002_an_py310.py hl[30:32] *}
## Резюме
## Резюме { #recap }
Теперь вы можете получать данные о текущем пользователе непосредственно в своей *функции обработчике эндпоинта*.
Теперь вы можете получать текущего пользователя прямо в своей *функции-обработчике пути*.
Мы уже на полпути к этому.
Мы уже на полпути.
Осталось лишь добавить *эндпоинт* для отправки пользователем/клиентом своих `имени пользователя` и `пароля`.
Нужно лишь добавить *операцию пути*, чтобы пользователь/клиент мог отправить `username` и `password`.
Это будет рассмотрено в следующем разделе.
Это будет дальше.

68
docs/ru/docs/tutorial/security/oauth2-jwt.md

@ -1,12 +1,12 @@
# OAuth2 с паролем (и хешированием), Bearer с JWT-токенами
# OAuth2 с паролем (и хешированием), Bearer с JWT-токенами { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens }
Теперь, когда у нас определен процесс обеспечения безопасности, давайте сделаем приложение действительно безопасным, используя токены <abbr title="JSON Web Tokens">JWT</abbr> и безопасное хеширование паролей.
Теперь, когда у нас определен процесс обеспечения безопасности, давайте сделаем приложение действительно безопасным, используя токены <abbr title="JSON Web Tokens – веб‑токены JSON">JWT</abbr> и безопасное хеширование паролей.
Этот код можно реально использовать в своем приложении, сохранять хэши паролей в базе данных и т.д.
Мы продолжим разбираться, начиная с того места, на котором остановились в предыдущей главе.
## Про JWT
## Про JWT { #about-jwt }
JWT означает "JSON Web Tokens".
@ -26,7 +26,7 @@ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4
Если вы хотите поиграть с JWT-токенами и посмотреть, как они работают, посмотрите <a href="https://jwt.io/" class="external-link" target="_blank">https://jwt.io</a>.
## Установка `PyJWT`
## Установка `PyJWT` { #install-pyjwt }
Нам необходимо установить `pyjwt` для генерации и проверки JWT-токенов на языке Python.
@ -45,10 +45,10 @@ $ pip install pyjwt
/// info | Дополнительная информация
Если вы планируете использовать алгоритмы цифровой подписи, такие как RSA или ECDSA, вам следует установить зависимость библиотеки криптографии `pyjwt[crypto]`.
Подробнее об этом можно прочитать в <a href=«https://pyjwt.readthedocs.io/en/latest/installation.html» class=«external-link» target=«_blank»>документации по установке PyJWT</a>.
Подробнее об этом можно прочитать в <a href="https://pyjwt.readthedocs.io/en/latest/installation.html" class="external-link" target="_blank">документации по установке PyJWT</a>.
///
## Хеширование паролей
## Хеширование паролей { #password-hashing }
"Хеширование" означает преобразование некоторого содержимого (в данном случае пароля) в последовательность байтов (просто строку), которая выглядит как тарабарщина.
@ -56,26 +56,26 @@ $ pip install pyjwt
Но преобразовать тарабарщину обратно в пароль невозможно.
### Для чего нужно хеширование паролей
### Для чего нужно хеширование паролей { #why-use-password-hashing }
Если ваша база данных будет украдена, то вор не получит пароли пользователей в открытом виде, а только их хэши.
Таким образом, вор не сможет использовать этот пароль в другой системе (поскольку многие пользователи везде используют один и тот же пароль, это было бы опасно).
## Установка `passlib`
## Установка `pwdlib` { #install-pwdlib }
PassLib - это отличный пакет Python для работы с хэшами паролей.
pwdlib — это отличный пакет Python для работы с хэшами паролей.
Он поддерживает множество безопасных алгоритмов хеширования и утилит для работы с ними.
Рекомендуемый алгоритм - "Bcrypt".
Рекомендуемый алгоритм — "Argon2".
Убедитесь, что вы создали и активировали виртуальное окружение, и затем установите PassLib вместе с Bcrypt:
Убедитесь, что вы создали [виртуальное окружение](../../virtual-environments.md){.internal-link target=_blank}, активируйте его, и затем установите pwdlib вместе с Argon2:
<div class="termy">
```console
$ pip install "passlib[bcrypt]"
$ pip install "pwdlib[argon2]"
---> 100%
```
@ -83,40 +83,40 @@ $ pip install "passlib[bcrypt]"
</div>
/// tip | Подсказка
С помощью `passlib` можно даже настроить его на чтение паролей, созданных **Django**, плагином безопасности **Flask** или многими другими библиотеками.
С помощью `pwdlib` можно даже настроить его на чтение паролей, созданных **Django**, плагином безопасности **Flask** или многими другими библиотеками.
Таким образом, вы сможете, например, совместно использовать одни и те же данные из приложения Django в базе данных с приложением FastAPI. Или постепенно мигрировать Django-приложение, используя ту же базу данных.
При этом пользователи смогут одновременно входить в систему как из приложения Django, так и из приложения **FastAPI**.
///
## Хеширование и проверка паролей
## Хеширование и проверка паролей { #hash-and-verify-the-passwords }
Импортируйте необходимые инструменты из `passlib`.
Импортируйте необходимые инструменты из `pwdlib`.
Создайте "контекст" PassLib. Именно он будет использоваться для хэширования и проверки паролей.
Создайте экземпляр PasswordHash с рекомендованными настройками — он будет использоваться для хэширования и проверки паролей.
/// tip | Подсказка
Контекст PassLib также имеет функциональность для использования различных алгоритмов хеширования, в том числе и устаревших, только для возможности их проверки и т.д.
pwdlib также поддерживает алгоритм хеширования bcrypt, но не включает устаревшие алгоритмы — для работы с устаревшими хэшами рекомендуется использовать библиотеку passlib.
Например, вы можете использовать его для чтения и проверки паролей, сгенерированных другой системой (например, Django), но хэшировать все новые пароли другим алгоритмом, например Bcrypt.
Например, вы можете использовать ее для чтения и проверки паролей, сгенерированных другой системой (например, Django), но хэшировать все новые пароли другим алгоритмом, например Argon2 или Bcrypt.
И при этом быть совместимым со всеми этими системами.
///
Создайте служебную функцию для хэширования пароля, поступающего от пользователя.
А затем создайте другую - для проверки соответствия полученного пароля и хранимого хэша.
А затем создайте другую для проверки соответствия полученного пароля и хранимого хэша.
И еще одну - для аутентификации и возврата пользователя.
И еще одну для аутентификации и возврата пользователя.
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,56:57,60:61,70:76] *}
/// note | Технические детали
Если проверить новую (фальшивую) базу данных `fake_users_db`, то можно увидеть, как теперь выглядит хэшированный пароль: `"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"`.
Если проверить новую (фальшивую) базу данных `fake_users_db`, то можно увидеть, как теперь выглядит хэшированный пароль: `"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`.
///
## Работа с JWT токенами
## Работа с JWT токенами { #handle-jwt-tokens }
Импортируйте установленные модули.
@ -140,13 +140,13 @@ $ openssl rand -hex 32
Создайте переменную для срока действия токена.
Определите Pydantic Model, которая будет использоваться для формирования ответа на запрос на получение токена.
Определите Pydantic-модель, которая будет использоваться для формирования ответа на запрос на получение токена.
Создайте служебную функцию для генерации нового токена доступа.
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,79:87] *}
## Обновление зависимостей
## Обновление зависимостей { #update-the-dependencies }
Обновите `get_current_user` для получения того же токена, что и раньше, но на этот раз с использованием JWT-токенов.
@ -156,7 +156,7 @@ $ openssl rand -hex 32
{* ../../docs_src/security/tutorial004_an_py310.py hl[90:107] *}
## Обновление *операции пути* `/token`
## Обновление *операции пути* `/token` { #update-the-token-path-operation }
Создайте `timedelta` со временем истечения срока действия токена.
@ -164,7 +164,7 @@ $ openssl rand -hex 32
{* ../../docs_src/security/tutorial004_an_py310.py hl[118:133] *}
### Технические подробности о JWT ключе `sub`
### Технические подробности о JWT ключе `sub` { #technical-details-about-the-jwt-subject-sub }
В спецификации JWT говорится, что существует ключ `sub`, содержащий субъект токена.
@ -186,7 +186,7 @@ JWT может использоваться и для других целей,
Важно помнить, что ключ `sub` должен иметь уникальный идентификатор для всего приложения и представлять собой строку.
## Проверка в действии
## Проверка в действии { #check-it }
Запустите сервер и перейдите к документации: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
@ -201,7 +201,7 @@ JWT может использоваться и для других целей,
Username: `johndoe`
Password: `secret`
/// check | Заметка
/// check | Проверка
Обратите внимание, что нигде в коде не используется открытый текст пароля "`secret`", мы используем только его хэшированную версию.
///
@ -225,10 +225,10 @@ Password: `secret`
<img src="/img/tutorial/security/image10.png">
/// note | Техническая информация
Обратите внимание на заголовок `Authorization`, значение которого начинается с `Bearer`.
Обратите внимание на HTTP-заголовок `Authorization`, значение которого начинается с `Bearer `.
///
## Продвинутое использование `scopes`
## Продвинутое использование `scopes` { #advanced-usage-with-scopes }
В OAuth2 существует понятие "диапазоны" ("`scopes`").
@ -236,9 +236,9 @@ Password: `secret`
Затем вы можете передать этот токен непосредственно пользователю или третьей стороне для взаимодействия с вашим API с определенным набором ограничений.
О том, как их использовать и как они интегрированы в **FastAPI**, читайте далее в **Руководстве пользователя**.
О том, как их использовать и как они интегрированы в **FastAPI**, читайте далее в **Расширенном руководстве пользователя**.
## Резюме
## Резюме { #recap }
С учетом того, что вы видели до сих пор, вы можете создать безопасное приложение **FastAPI**, используя такие стандарты, как OAuth2 и JWT.
@ -252,10 +252,10 @@ Password: `secret`
Он предоставляет вам полную свободу действий, позволяя выбирать то, что лучше всего подходит для вашего проекта.
Вы можете напрямую использовать многие хорошо поддерживаемые и широко распространенные пакеты, такие как `passlib` и `PyJWT`, поскольку **FastAPI** не требует сложных механизмов для интеграции внешних пакетов.
Вы можете напрямую использовать многие хорошо поддерживаемые и широко распространенные пакеты, такие как `pwdlib` и `PyJWT`, поскольку **FastAPI** не требует сложных механизмов для интеграции внешних пакетов.
Напротив, он предоставляет инструменты, позволяющие максимально упростить этот процесс без ущерба для гибкости, надежности и безопасности.
При этом вы можете использовать и реализовывать безопасные стандартные протоколы, такие как OAuth2, относительно простым способом.
В **Руководстве пользователя** вы можете узнать больше о том, как использовать "диапазоны" ("`scopes`") OAuth2 для создания более точно настроенной системы разрешений в соответствии с теми же стандартами. OAuth2 с диапазонами - это механизм, используемый многими крупными провайдерами сервиса аутентификации, такими как Facebook, Google, GitHub, Microsoft, X (Twitter) и др., для авторизации сторонних приложений на взаимодействие с их API от имени их пользователей.
В **Расширенном руководстве пользователя** вы можете узнать больше о том, как использовать "диапазоны" ("`scopes`") OAuth2 для создания более точно настроенной системы разрешений в соответствии с теми же стандартами. OAuth2 с диапазонами это механизм, используемый многими крупными провайдерами сервиса аутентификации, такими как Facebook, Google, GitHub, Microsoft, X (Twitter) и др., для авторизации сторонних приложений на взаимодействие с их API от имени их пользователей.

165
docs/ru/docs/tutorial/security/simple-oauth2.md

@ -1,59 +1,58 @@
# Простая авторизация по протоколу OAuth2 с токеном типа Bearer
# Простая авторизация OAuth2 с паролем и «Bearer» { #simple-oauth2-with-password-and-bearer }
Теперь, отталкиваясь от предыдущей главы, добавим недостающие части, чтобы получить безопасную систему.
Теперь, отталкиваясь от предыдущей главы, добавим недостающие части, чтобы получить полный поток безопасности.
## Получение `имени пользователя` и `пароля`
## Получение `username` и `password` { #get-the-username-and-password }
Для получения `имени пользователя` и `пароля` мы будем использовать утилиты безопасности **FastAPI**.
Для получения `username` и `password` мы будем использовать утилиты безопасности **FastAPI**.
Протокол OAuth2 определяет, что при использовании "аутентификации по паролю" (которую мы и используем) клиент/пользователь должен передавать поля `username` и `password` в полях формы.
OAuth2 определяет, что при использовании "password flow" (аутентификация по паролю - именно его мы используем) клиент/пользователь должен передавать поля `username` и `password` в полях формы.
В спецификации сказано, что поля должны быть названы именно так. Поэтому `user-name` или `email` работать не будут.
Но не волнуйтесь, вы можете показать его конечным пользователям во фронтенде в том виде, в котором хотите.
Но не волнуйтесь, вы можете показать это конечным пользователям во фронтенде в том виде, в котором хотите.
А ваши модели баз данных могут использовать любые другие имена.
Но при авторизации согласно спецификации, требуется использовать именно эти имена, что даст нам возможность воспользоваться встроенной системой документации API.
Но для логин-операции пути нам нужно использовать именно эти имена, чтобы быть совместимыми со спецификацией (и иметь возможность, например, использовать встроенную систему документации API).
В спецификации также указано, что `username` и `password` должны передаваться в виде данных формы (так что никакого JSON здесь нет).
### Oбласть видимости (scope)
### `scope` { #scope }
В спецификации также говорится, что клиент может передать еще одно поле формы "`scope`".
В спецификации также говорится, что клиент может передать еще одно поле формы `scope`.
Имя поля формы - `scope` (в единственном числе), но на самом деле это длинная строка, состоящая из отдельных областей видимости (scopes), разделенных пробелами.
Имя поля формы `scope` (в единственном числе), но на самом деле это длинная строка, состоящая из отдельных "scopes", разделенных пробелами.
Каждая "область видимости" (scope) - это просто строка (без пробелов).
Каждый "scope" — это просто строка (без пробелов).
Обычно они используются для указания уровней доступа, например:
* `users:read` или `users:write` являются распространенными примерами.
* `users:read` или `users:write` — распространенные примеры.
* `instagram_basic` используется Facebook / Instagram.
* `https://www.googleapis.com/auth/drive` используется компанией Google.
* `https://www.googleapis.com/auth/drive` используется Google.
/// info | Дополнительнаяя информация
В OAuth2 "scope" - это просто строка, которая уточняет уровень доступа.
/// info | Дополнительная информация
В OAuth2 "scope" — это просто строка, которая указывает требуемое конкретное разрешение.
Не имеет значения, содержит ли он другие символы, например `:`, или является ли он URL.
Не имеет значения, содержит ли она другие символы, например `:`, или является ли это URL.
Эти детали зависят от конкретной реализации.
Эти детали зависят от реализации.
Для OAuth2 это просто строки.
///
## Код получения `имени пользователя` и `пароля`
Для решения задачи давайте воспользуемся утилитами, предоставляемыми **FastAPI**.
## Код для получения `username` и `password` { #code-to-get-the-username-and-password }
### `OAuth2PasswordRequestForm`
Теперь воспользуемся утилитами, предоставляемыми **FastAPI**, чтобы обработать это.
Сначала импортируйте `OAuth2PasswordRequestForm` и затем используйте ее как зависимость с `Depends` в *эндпоинте* `/token`:
### `OAuth2PasswordRequestForm` { #oauth2passwordrequestform }
Сначала импортируйте `OAuth2PasswordRequestForm` и затем используйте её как зависимость с `Depends` в операции пути для `/token`:
{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}
`OAuth2PasswordRequestForm` - это класс для использования в качестве зависимости для *функции обрабатывающей эндпоинт*, который определяет тело формы со следующими полями:
`OAuth2PasswordRequestForm` — это зависимость-класс, которая объявляет тело формы со следующими полями:
* `username`.
* `password`.
@ -61,51 +60,51 @@
* Необязательное поле `grant_type`.
/// tip | Подсказка
По спецификации OAuth2 поле `grant_type` является обязательным и содержит фиксированное значение `password`, но `OAuth2PasswordRequestForm` не обеспечивает этого.
По спецификации OAuth2 поле `grant_type` обязательно и содержит фиксированное значение `password`, но `OAuth2PasswordRequestForm` это не проверяет строго.
Если вам необходимо использовать `grant_type`, воспользуйтесь `OAuth2PasswordRequestFormStrict` вместо `OAuth2PasswordRequestForm`.
Если вам нужно это строгое требование, используйте `OAuth2PasswordRequestFormStrict` вместо `OAuth2PasswordRequestForm`.
///
* Необязательное поле `client_id` (в нашем примере он не нужен).
* Необязательное поле `client_secret` (в нашем примере он не нужен).
* Необязательное поле `client_id` (в нашем примере оно не нужно).
* Необязательное поле `client_secret` (в нашем примере оно не нужно).
/// info | Дополнительная информация
Форма `OAuth2PasswordRequestForm` не является специальным классом для **FastAPI**, как `OAuth2PasswordBearer`.
`OAuth2PasswordRequestForm` — это не специальный класс для **FastAPI**, как `OAuth2PasswordBearer`.
`OAuth2PasswordBearer` указывает **FastAPI**, что это схема безопасности. Следовательно, она будет добавлена в OpenAPI.
`OAuth2PasswordBearer` сообщает **FastAPI**, что это схема безопасности. Поэтому она добавляется в OpenAPI соответствующим образом.
Но `OAuth2PasswordRequestForm` - это всего лишь класс зависимости, который вы могли бы написать самостоятельно или вы могли бы объявить параметры `Form` напрямую.
А `OAuth2PasswordRequestForm` — это просто зависимость-класс, которую вы могли бы написать сами, или вы могли бы объявить параметры `Form` напрямую.
Но, поскольку это распространённый вариант использования, он предоставляется **FastAPI** напрямую, просто чтобы облегчить задачу.
Но так как это распространённый вариант использования, он предоставлен **FastAPI** напрямую, чтобы упростить задачу.
///
### Использование данных формы
### Использование данных формы { #use-the-form-data }
/// tip | Подсказка
В экземпляре зависимого класса `OAuth2PasswordRequestForm` атрибут `scope`, состоящий из одной длинной строки, разделенной пробелами, заменен на атрибут `scopes`, состоящий из списка отдельных строк, каждая из которых соответствует определенному уровню доступа.
У экземпляра зависимости `OAuth2PasswordRequestForm` не будет атрибута `scope` с длинной строкой, разделенной пробелами. Вместо этого будет атрибут `scopes` со списком отдельных строк — по одной для каждого переданного scope.
В данном примере мы не используем `scopes`, но если вам это необходимо, то такая функциональность имеется.
В данном примере мы не используем `scopes`, но если вам это необходимо, функциональность есть.
///
Теперь получим данные о пользователе из (ненастоящей) базы данных, используя `username` из поля формы.
Если такого пользователя нет, то мы возвращаем ошибку "неверное имя пользователя или пароль".
Если такого пользователя нет, то мы возвращаем ошибку "Incorrect username or password" (неверное имя пользователя или пароль).
Для ошибки мы используем исключение `HTTPException`:
Для ошибки используем исключение `HTTPException`:
{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}
### Проверка пароля
### Проверка пароля { #check-the-password }
На данный момент у нас есть данные о пользователе из нашей базы данных, но мы еще не проверили пароль.
Давайте сначала поместим эти данные в модель Pydantic `UserInDB`.
Давайте сначала поместим эти данные в Pydantic-модель `UserInDB`.
Ни в коем случае нельзя сохранять пароли в открытом виде, поэтому мы будем использовать (пока что ненастоящую) систему хеширования паролей.
Никогда нельзя сохранять пароли в открытом виде, поэтому мы будем использовать (пока что ненастоящую) систему хеширования паролей.
Если пароли не совпадают, мы возвращаем ту же ошибку.
#### Хеширование паролей
#### Хеширование паролей { #password-hashing }
"Хеширование" означает: преобразование некоторого содержимого (в данном случае пароля) в последовательность байтов (просто строку), которая выглядит как тарабарщина.
@ -113,19 +112,19 @@
Но преобразовать тарабарщину обратно в пароль невозможно.
##### Зачем использовать хеширование паролей
##### Зачем использовать хеширование паролей { #why-use-password-hashing }
Если ваша база данных будет украдена, то у вора не будет паролей пользователей в открытом виде, только хэши.
Если вашу базу данных украдут, у злоумышленника не будет паролей пользователей в открытом виде, только хэши.
Таким образом, вор не сможет использовать эти же пароли в другой системе (поскольку многие пользователи используют одни и те же пароли повсеместно, это было бы опасно).
Таким образом, он не сможет попробовать использовать эти же пароли в другой системе (поскольку многие пользователи используют один и тот же пароль повсеместно, это было бы опасно).
{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}
#### Про `**user_dict`
#### Про `**user_dict` { #about-user-dict }
`UserInDB(**user_dict)` означает:
*Передавать ключи и значения `user_dict` непосредственно в качестве аргументов ключ-значение, что эквивалентно:*
*Передать ключи и значения `user_dict` непосредственно как аргументы ключ-значение, эквивалентно:*
```Python
UserInDB(
@ -138,23 +137,23 @@ UserInDB(
```
/// info | Дополнительная информация
Более полное объяснение `**user_dict` можно найти в [документации к **Дополнительным моделям**](../extra-models.md#about-user_indict){.internal-link target=_blank}.
Более полное объяснение `**user_dict` можно найти в [документации к **Дополнительным моделям**](../extra-models.md#about-user-in-dict){.internal-link target=_blank}.
///
## Возврат токена
## Возврат токена { #return-the-token }
Ответ эндпоинта `token` должен представлять собой объект в формате JSON.
Ответ операции пути `/token` должен быть объектом JSON.
Он должен иметь `token_type`. В нашем случае, поскольку мы используем токены типа "Bearer", тип токена должен быть "`bearer`".
В нём должен быть `token_type`. В нашем случае, поскольку мы используем токены типа "Bearer", тип токена должен быть `bearer`.
И в нем должна быть строка `access_token`, содержащая наш токен доступа.
И в нём должен быть `access_token` — строка, содержащая наш токен доступа.
В этом простом примере мы нарушим все правила безопасности, и будем считать, что имя пользователя (username) полностью соответствует токену (token)
В этом простом примере мы намеренно поступим небезопасно и вернём тот же `username` в качестве токена.
/// tip | Подсказка
В следующей главе мы рассмотрим реальную защищенную реализацию с хешированием паролей и токенами <abbr title="JSON Web Tokens">JWT</abbr>.
В следующей главе вы увидите реальную защищённую реализацию с хешированием паролей и токенами <abbr title="JSON Web Tokens – JSON веб-токены">JWT</abbr>.
Но пока давайте остановимся на необходимых нам деталях.
Но пока давайте сосредоточимся на необходимых нам деталях.
///
{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *}
@ -162,50 +161,50 @@ UserInDB(
/// tip | Подсказка
Согласно спецификации, вы должны возвращать JSON с `access_token` и `token_type`, как в данном примере.
Это то, что вы должны сделать сами в своем коде и убедиться, что вы используете эти JSON-ключи.
Это то, что вы должны сделать сами в своём коде и убедиться, что вы используете именно эти JSON-ключи.
Это практически единственное, что нужно не забывать делать самостоятельно, чтобы следовать требованиям спецификации.
Это практически единственное, о чём нужно не забыть, чтобы соответствовать спецификациям.
Все остальное за вас сделает **FastAPI**.
Остальное за вас сделает **FastAPI**.
///
## Обновление зависимостей
## Обновление зависимостей { #update-the-dependencies }
Теперь мы обновим наши зависимости.
Мы хотим получить значение `current_user` *только* если этот пользователь активен.
Мы хотим получить `current_user` только если этот пользователь активен.
Поэтому мы создаем дополнительную зависимость `get_current_active_user`, которая, в свою очередь, использует в качестве зависимости `get_current_user`.
Поэтому мы создаём дополнительную зависимость `get_current_active_user`, которая, в свою очередь, использует в качестве зависимости `get_current_user`.
Обе эти зависимости просто вернут HTTP-ошибку, если пользователь не существует или неактивен.
Таким образом, в нашем эндпоинте мы получим пользователя только в том случае, если он существует, правильно аутентифицирован и активен:
Таким образом, в нашей операции пути мы получим пользователя только в том случае, если он существует, корректно аутентифицирован и активен:
{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}
/// info | Дополнительная информация
Дополнительный заголовок `WWW-Authenticate` со значением `Bearer`, который мы здесь возвращаем, также является частью спецификации.
Дополнительный HTTP-заголовок `WWW-Authenticate` со значением `Bearer`, который мы здесь возвращаем, также является частью спецификации.
Ответ сервера с HTTP-кодом 401 "UNAUTHORIZED" должен также возвращать заголовок `WWW-Authenticate`.
Любой HTTP статус-код 401 "UNAUTHORIZED" должен также возвращать заголовок `WWW-Authenticate`.
В случае с bearer-токенами (наш случай) значение этого заголовка должно быть `Bearer`.
На самом деле этот дополнительный заголовок можно пропустить и все будет работать.
Фактически, этот дополнительный заголовок можно опустить, и всё будет работать.
Но он приведён здесь для соответствия спецификации.
Но он приведён здесь для соответствия спецификациям.
Кроме того, могут существовать инструменты, которые ожидают его и могут использовать, и это может быть полезно для вас или ваших пользователей сейчас или в будущем.
Кроме того, могут существовать инструменты, которые ожидают его и могут использовать, и это может быть полезно для вас или ваших пользователей сейчас или в будущем.
В этом и заключается преимущество стандартов...
///
## Посмотим как это работает
## Посмотрим, как это работает { #see-it-in-action }
Откроем интерактивную документацию: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Откройте интерактивную документацию: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
### Аутентификация
### Аутентификация { #authenticate }
Нажмите кнопку "Авторизация".
Нажмите кнопку "Authorize".
Используйте учётные данные:
@ -215,13 +214,15 @@ UserInDB(
<img src="/img/tutorial/security/image04.png">
После авторизации в системе вы увидите следующее:
После аутентификации вы увидите следующее:
<img src="/img/tutorial/security/image05.png">
### Получение собственных пользовательских данных
### Получение собственных пользовательских данных { #get-your-own-user-data }
Теперь используйте операцию `GET` с путём `/users/me`.
Теперь, используя операцию `GET` с путем `/users/me`, вы получите данные пользователя, например:
Вы получите свои пользовательские данные, например:
```JSON
{
@ -235,7 +236,7 @@ UserInDB(
<img src="/img/tutorial/security/image06.png">
Если щелкнуть на значке замка и выйти из системы, а затем попытаться выполнить ту же операцию ещё раз, то будет выдана ошибка HTTP 401:
Если щёлкнуть на значке замка и выйти из системы, а затем попытаться выполнить ту же операцию ещё раз, будет выдана ошибка HTTP 401:
```JSON
{
@ -243,17 +244,17 @@ UserInDB(
}
```
### Неактивный пользователь
### Неактивный пользователь { #inactive-user }
Теперь попробуйте пройти аутентификацию с неактивным пользователем:
Теперь попробуйте с неактивным пользователем, аутентифицируйтесь с:
Пользователь: `alice`
Пароль: `secret2`
И попробуйте использовать операцию `GET` с путем `/users/me`.
И попробуйте использовать операцию `GET` с путём `/users/me`.
Вы получите ошибку "Inactive user", как тут:
Вы получите ошибку "Inactive user", как здесь:
```JSON
{
@ -261,12 +262,12 @@ UserInDB(
}
```
## Резюме
## Резюме { #recap }
Теперь у вас есть инструменты для реализации полноценной системы безопасности на основе `имени пользователя` и `пароля` для вашего API.
Теперь у вас есть инструменты для реализации полноценной системы безопасности на основе `username` и `password` для вашего API.
Используя эти средства, можно сделать систему безопасности совместимой с любой базой данных, с любым пользователем или моделью данных.
Используя эти средства, можно сделать систему безопасности совместимой с любой базой данных и с любой пользовательской или моделью данных.
Единственным недостатком нашей системы является то, что она всё ещё не защищена.
Единственная деталь, которой не хватает, — система пока ещё не "защищена" по-настоящему.
В следующей главе вы увидите, как использовать библиотеку безопасного хеширования паролей и токены <abbr title="JSON Web Tokens">JWT</abbr>.
В следующей главе вы увидите, как использовать библиотеку безопасного хеширования паролей и токены <abbr title="JSON Web Tokens – JSON веб-токены">JWT</abbr>.

223
docs/ru/docs/tutorial/sql-databases.md

@ -1,18 +1,18 @@
# SQL (реляционные) базы данных
# SQL (реляционные) базы данных { #sql-relational-databases }
**FastAPI** не требует использования реляционной базы данных. Вы можете воспользоваться любой базой данных, которой хотите.
**FastAPI** не требует использовать SQL (реляционную) базу данных. Но вы можете использовать любую базу данных, которую хотите.
В этом разделе мы продемонстрируем, как работать с <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel</a>.
Здесь мы рассмотрим пример с использованием <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel</a>.
Библиотека **SQLModel** построена на основе <a href="https://www.sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a> и Pydantic. Она была разработана автором **FastAPI** специально для приложений на основе FastAPI, которые используют **реляционные базы данных**.
**SQLModel** построен поверх <a href="https://www.sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a> и Pydantic. Его создал тот же автор, что и **FastAPI**, чтобы он идеально подходил для приложений FastAPI, которым нужны **SQL базы данных**.
/// tip | Подсказка
Вы можете воспользоваться любой библиотекой для работы с реляционными (SQL) или нереляционными (NoSQL) базами данных. (Их ещё называют <abbr title="ORM = Object Relational Mapper, этот термин для библиотеки, в которой классы представляют SQL-таблицы, а экземпляры классов представляют строки в этих таблицах.">**ORM**</abbr> библиотеками). FastAPI не принуждает вас к использованию чего-либо конкретного. 😎
Вы можете использовать любую другую библиотеку для работы с SQL или NoSQL базами данных (иногда их называют <abbr title="Object Relational Mapper – Объектно-реляционный маппер: термин для библиотеки, где некоторые классы представляют SQL-таблицы, а экземпляры представляют строки в этих таблицах">"ORMs"</abbr>), FastAPI ничего не навязывает. 😎
///
В основе SQLModel лежит SQLAlchemy, поэтому вы спокойно можете использовать любую базу данных, поддерживаемую SQLAlchemy (и, соответственно, поддерживаемую SQLModel), например:
Так как SQLModel основан на SQLAlchemy, вы можете легко использовать **любую поддерживаемую** SQLAlchemy базу данных (а значит, и поддерживаемую SQLModel), например:
* PostgreSQL
* MySQL
@ -20,21 +20,21 @@
* Oracle
* Microsoft SQL Server, и т.д.
В данном примере мы будем использовать базу данных **SQLite**, т.к. она состоит из единственного файла и поддерживается встроенными библиотеками Python. Таким образом, вы сможете скопировать данный пример и запустить его как он есть.
В этом примере мы будем использовать **SQLite**, потому что она использует один файл и имеет встроенную поддержку в Python. Так что вы можете скопировать этот пример и запустить его как есть.
В дальнейшем, для продакшн-версии вашего приложения, возможно, вам стоит использовать серверную базу данных, например, **PostgreSQL**.
Позже, для продакшн-приложения, возможно, вы захотите использовать серверную базу данных, например **PostgreSQL**.
/// tip | Подсказка
Существует официальный генератор проектов на **FastAPI** и **PostgreSQL**, который также включает frontend и дополнительные инструменты <a href="https://github.com/fastapi/full-stack-fastapi-template" class="external-link" target="_blank">https://github.com/fastapi/full-stack-fastapi-template</a>
Существует официальный генератор проектов на **FastAPI** и **PostgreSQL**, включающий frontend и другие инструменты: <a href="https://github.com/fastapi/full-stack-fastapi-template" class="external-link" target="_blank">https://github.com/fastapi/full-stack-fastapi-template</a>
///
Это очень простое и короткое руководство, поэтому, если вы хотите узнать о базах данных в целом, об SQL, разобраться с более продвинутым функционалом, то воспользуйтесь <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">документацией SQLModel</a>.
Это очень простое и короткое руководство. Если вы хотите узнать больше о базах данных в целом, об SQL или о более продвинутых возможностях, обратитесь к <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">документации SQLModel</a>.
## Установка `SQLModel`
## Установка `SQLModel` { #install-sqlmodel }
Создайте виртуальное окружение [virtual environment](../virtual-environments.md){.internal-link target=_blank}, активируйте его и установите `sqlmodel`:
Сначала убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его и затем установили `sqlmodel`:
<div class="termy">
@ -45,110 +45,109 @@ $ pip install sqlmodel
</div>
## Создание приложения с единственной моделью
## Создание приложения с единственной моделью { #create-the-app-with-a-single-model }
Мы начнем с создания наиболее простой первой версии нашего приложения с одной единственной моделью **SQLModel**.
Сначала мы создадим самую простую первую версию приложения с одной моделью **SQLModel**.
В дальнейшем с помощью **дополнительных моделей** мы его улучшим и сделаем более безопасным и универсальным. 🤓
Позже мы улучшим его, повысив безопасность и универсальность, добавив **несколько моделей**. 🤓
### Создание моделей
### Создание моделей { #create-models }
Импортируйте `SQLModel` и создайте модель базы данных:
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *}
Класс `Hero` очень напоминает модель Pydantic (фактически, под капотом, *это и есть модель Pydantic*).
Класс `Hero` очень похож на модель Pydantic (фактически, под капотом, *это и есть модель Pydantic*).
Но есть и некоторые различия
Есть несколько отличий:
* `table=True` для SQLModel означает, что это *модель-таблица*, которая должна представлять **таблицу** в реляционной базе данных. Это не просто *модель данных* (в отличие от обычного класса в Pydantic).
* `table=True` сообщает SQLModel, что это *модель-таблица*, она должна представлять **таблицу** в SQL базе данных, это не просто *модель данных* (как обычный класс Pydantic).
* `Field(primary_key=True)` для SQLModel означает, что поле `id` является первичным ключом в таблице базы данных (вы можете подробнее узнать о первичных ключах баз данных в документации по SQLModel).
* `Field(primary_key=True)` сообщает SQLModel, что `id` — это **первичный ключ** в SQL базе данных (подробнее о первичных ключах можно узнать в документации SQLModel).
Тип `int | None` сигнализирует для SQLModel, что столбец таблицы базы данных должен иметь тип `INTEGER`, или иметь пустое значение `NULL`.
Благодаря типу `int | None`, SQLModel будет знать, что этот столбец должен быть `INTEGER` в SQL базе данных и должен допускать значение `NULL`.
* `Field(index=True)` для SQLModel означает, что нужно создать **SQL индекс** для данного столбца. Это обеспечит более быстрый поиск при чтении данных, отфильтрованных по данному столбцу.
* `Field(index=True)` сообщает SQLModel, что нужно создать **SQL индекс** для этого столбца, что позволит быстрее выполнять выборки при чтении данных, отфильтрованных по этому столбцу.
SQLModel будет знать, что данные типа `str`, будут представлены в базе данных как `TEXT` (или `VARCHAR`, в зависимости от типа базы данных).
SQLModel будет знать, что объявленное как `str` станет SQL-столбцом типа `TEXT` (или `VARCHAR`, в зависимости от базы данных).
### Создание соединения с базой данных (Engine)
### Создание Engine { #create-an-engine }
В SQLModel объект соединения `engine` (по сути это `Engine` из SQLAlchemy) **содержит пул соединений** к базе данных.
Объект `engine` в SQLModel (под капотом это `engine` из SQLAlchemy) **удерживает соединения** с базой данных.
Для обеспечения всех подключений приложения к одной базе данных нужен только один объект соединения `engine`.
У вас должен быть **один объект `engine`** для всей кодовой базы, чтобы подключаться к одной и той же базе данных.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *}
Использование настройки `check_same_thread=False` позволяет FastAPI использовать одну и ту же SQLite базу данных в различных потоках (threads). Это необходимо, когда **один запрос** использует **более одного потока** (например, в зависимостях).
Параметр `check_same_thread=False` позволяет FastAPI использовать одну и ту же базу данных SQLite в разных потоках. Это необходимо, так как **один запрос** может использовать **больше одного потока** (например, в зависимостях).
Не беспокойтесь, учитывая структуру кода, мы позже позаботимся о том, чтобы использовать **отдельную SQLModel-сессию на каждый отдельный запрос**, это как раз то, что пытается обеспечить `check_same_thread`.
Не волнуйтесь, с такой структурой кода мы позже обеспечим использование **одной *сессии* SQLModel на запрос**, по сути именно этого и добивается `check_same_thread`.
### Создание таблиц
### Создание таблиц { #create-the-tables }
Далее мы добавляем функцию, использующую `SQLModel.metadata.create_all(engine)`, для того, чтобы создать **таблицы** для каждой из **моделей таблицы**.
Далее мы добавим функцию, которая использует `SQLModel.metadata.create_all(engine)`, чтобы **создать таблицы** для всех *моделей-таблиц*.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *}
### Создание зависимости Session
### Создание зависимости Session { #create-a-session-dependency }
Сессия базы данных (**`Session`**) хранит **объекты в памяти** и отслеживает любые необходимые изменения в данных, а затем **использует `engine`** для коммуникации с базой данных.
**`Session`** хранит **объекты в памяти** и отслеживает необходимые изменения в данных, затем **использует `engine`** для общения с базой данных.
Мы создадим FastAPI-**зависимость** с помощью `yield`, которая будет создавать новую сессию (Session) для каждого запроса. Это как раз и обеспечит использование отдельной сессии на каждый отдельный запрос. 🤓
Мы создадим **зависимость** FastAPI с `yield`, которая будет предоставлять новую `Session` для каждого запроса. Это и обеспечивает использование одной сессии на запрос. 🤓
Затем мы создадим объявленную (`Annotated`) зависимость `SessionDep`. Мы сделаем это для того, чтобы упростить остальной код, который будет использовать эту зависимость.
Затем мы создадим объявленную (`Annotated`) зависимость `SessionDep`, чтобы упростить остальной код, который будет использовать эту зависимость.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *}
### Создание таблиц базы данных при запуске приложения
### Создание таблиц базы данных при старте { #create-database-tables-on-startup }
Мы будем создавать таблицы базы данных при запуске приложения.
Мы создадим таблицы базы данных при запуске приложения.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *}
В данном примере мы создаем таблицы при наступлении события запуска приложения.
Здесь мы создаём таблицы в обработчике события запуска приложения.
В продуктовом приложении вы, скорее всего, будете использовать скрипт для миграции базы данных, который выполняется перед запуском приложения. 🤓
Для продакшна вы, вероятно, будете использовать скрипт миграций, который выполняется до запуска приложения. 🤓
/// tip | Подсказка
В SQLModel будут включены утилиты миграции, входящие в состав Alembic, но на данный момент вы просто можете использовать
<a href="https://alembic.sqlalchemy.org/en/latest/" class="external-link" target="_blank">Alembic</a> напрямую.
В SQLModel появятся утилиты миграций - обёртки над Alembic, но пока вы можете использовать <a href="https://alembic.sqlalchemy.org/en/latest/" class="external-link" target="_blank">Alembic</a> напрямую.
///
### Создание героя (Hero)
### Создание героя (Hero) { #create-a-hero }
Каждая модель в SQLModel является также моделью Pydantic, поэтому вы можете использовать её при **объявлении типов**, точно также, как и модели Pydantic.
Так как каждая модель SQLModel также является моделью Pydantic, вы можете использовать её в тех же **аннотациях типов**, в которых используете модели Pydantic.
Например, при объявлении параметра типа `Hero`, она будет считана из **тела JSON**.
Например, если вы объявите параметр типа `Hero`, он будет прочитан из **JSON body (тела запроса)**.
Точно также, вы можете использовать её при объявлении типа значения, возвращаемого функцией, и тогда структурированные данные будут отображены через пользовательский интерфейс автоматически сгенерированной документации FastAPI.
Аналогично вы можете объявить её как **тип возвращаемого значения** функции, и тогда форма данных отобразится в автоматически сгенерированном UI документации API.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
Мы используем зависимость `SessionDep` (сессию базы данных) для того, чтобы добавить нового героя `Hero` в объект сессии (`Session`), сохранить изменения в базе данных, обновить данные героя и затем вернуть их.
Здесь мы используем зависимость `SessionDep` (это `Session`), чтобы добавить нового `Hero` в экземпляр `Session`, зафиксировать изменения в базе данных, обновить данные в `hero` и затем вернуть его.
### Чтение данных о героях
### Чтение героев { #read-heroes }
Мы можем **читать** данные героев из базы данных с помощью `select()`. Мы можем включить `limit` и `offset` для постраничного считывания результатов.
Мы можем **читать** записи `Hero` из базы данных с помощью `select()`. Можно добавить `limit` и `offset` для постраничного вывода результатов.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *}
### Чтение данных отдельного героя
### Чтение одного героя { #read-one-hero }
Мы можем прочитать данные отдельного героя (`Hero`).
Мы можем **прочитать** одного `Hero`.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *}
### Удаление данных героя
### Удаление героя { #delete-a-hero }
Мы также можем удалить героя `Hero` из базы данных.
Мы также можем **удалить** `Hero`.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *}
### Запуск приложения
### Запуск приложения { #run-the-app }
Вы можете запустить приложение следующим образом:
Вы можете запустить приложение:
<div class="termy">
@ -160,49 +159,49 @@ $ fastapi dev main.py
</div>
Далее перейдите в пользовательский интерфейс API `/docs`. Вы увидите, что **FastAPI** использует модели для создания документации API. Эти же модели используются для сериализации и проверки данных.
Затем перейдите в UI `/docs`. Вы увидите, что **FastAPI** использует эти **модели** для **документирования** API, а также для **сериализации** и **валидации** данных.
<div class="screenshot">
<img src="/img/tutorial/sql-databases/image01.png">
</div>
## Добавление в приложение дополнительных (вспомогательных) моделей
## Обновление приложения с несколькими моделями { #update-the-app-with-multiple-models }
Теперь давайте проведём **рефакторинг** нашего приложения, чтобы сделать его более безопасным и более универсальным.
Теперь давайте немного **отрефакторим** приложение, чтобы повысить **безопасность** и **универсальность**.
Обратите внимание, что на данном этапе наше приложение позволяет на уровне клиента определять `id` создаваемого героя (`Hero`). 😱
Если вы посмотрите на предыдущую версию, в UI видно, что до сих пор клиент мог сам задавать `id` создаваемого `Hero`. 😱
Мы не можем этого допустить, т.к. существует риск переписать уже присвоенные `id` в базе данных. Присвоение `id` должно происходить **на уровне бэкэнда (backend)** или **на уровне базы данных**, но никак **не на уровне клиента**.
Так делать нельзя, иначе они могли бы перезаписать `id`, который уже присвоен в БД. Решение по `id` должно приниматься **бэкендом** или **базой данных**, а **не клиентом**.
Кроме того, мы создаем секретное имя `secret_name` для героя, но пока что, мы возвращаем его повсеместно, и это слабо напоминает **секретность**... 😅
Кроме того, мы создаём для героя `secret_name`, но пока что возвращаем его повсюду — это не очень **секретно**... 😅
Мы поправим это с помощью нескольких дополнительных (вспомогательных) моделей. Вот где SQLModel по-настоящему покажет себя. ✨
Мы исправим это, добавив несколько **дополнительных моделей**. Здесь SQLModel раскроется во всей красе. ✨
### Создание дополнительных моделей
### Создание нескольких моделей { #create-multiple-models }
В **SQLModel**, любая модель с параметром `table=True` является **моделью таблицы**.
В **SQLModel** любая модель с `table=True` — это **модель-таблица**.
Любая модель, не содержащая `table=True` является **моделью данных**, это по сути обычные модели Pydantic (с несколько расширенным функционалом). 🤓
Любая модель без `table=True` — это **модель данных**, по сути обычная модель Pydantic (с парой небольших дополнений). 🤓
С помощью SQLModel мы можем использовать **наследование**, что поможет нам **избежать дублирования** всех полей.
С SQLModel мы можем использовать **наследование**, чтобы **избежать дублирования** полей.
#### Базовый класс `HeroBase`
#### `HeroBase` — базовый класс { #herobase-the-base-class }
Давайте начнём с модели `HeroBase`, которая содержит поля, общие для всех моделей:
Начнём с модели `HeroBase`, которая содержит **общие поля** для всех моделей:
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
#### Модель таблицы `Hero`
#### `Hero`*модель-таблица* { #hero-the-table-model }
Далее давайте создадим **модель таблицы** `Hero` с дополнительными полями, которых может не быть в других моделях:
Далее создадим `Hero`, фактическую *модель-таблицу*, с **дополнительными полями**, которых может не быть в других моделях:
* `id`
* `secret_name`
Модель `Hero` наследует от `HeroBase`, и поэтому включает также поля из `HeroBase`. Таким образом, все поля, содержащиеся в `Hero`, будут следующими:
Так как `Hero` наследуется от `HeroBase`, он **также** имеет **поля**, объявленные в `HeroBase`, поэтому все поля `Hero`:
* `id`
* `name`
@ -211,25 +210,25 @@ $ fastapi dev main.py
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
#### Публичная модель данных `HeroPublic`
#### `HeroPublic` — публичная *модель данных* { #heropublic-the-public-data-model }
Далее мы создадим модель `HeroPublic`. Мы будем возвращать её клиентам API.
Далее мы создадим модель `HeroPublic`, именно она будет **возвращаться** клиентам API.
Она включает в себя те же поля, что и `HeroBase`, и, соответственно, поле `secret_name` в ней отсутствует.
У неё те же поля, что и у `HeroBase`, поэтому она не включает `secret_name`.
Наконец-то личность наших героев защищена! 🥷
В модели `HeroPublic` также объявляется поле `id: int`. Мы как бы заключаем договоренность с API клиентом, на то, что передаваемые данные всегда должны содержать поле `id`, и это поле должно содержать целое число (и никогда не содержать `None`).
Также здесь заново объявляется `id: int`. Тем самым мы заключаем **контракт** с клиентами API: они всегда могут рассчитывать, что поле `id` присутствует и это `int` (никогда не `None`).
/// tip | Подсказка
Модель ответа, гарантирующая наличие поля со значением типа `int` (не `None`), очень полезна при разработке API клиентов. Определенность в передаваемых данных может обеспечить написание более простого кода.
Гарантия того, что в модели ответа значение всегда присутствует и это `int` (не `None`), очень полезна для клиентов API — так можно писать гораздо более простой код.
Также **автоматически генерируемые клиенты** будут иметь более простой интерфейс. И в результате жизнь разработчиков, использующих ваш API, станет значительно легче. 😎
Кроме того, **автоматически сгенерированные клиенты** будут иметь более простые интерфейсы, и разработчикам, взаимодействующим с вашим API, будет работать значительно комфортнее. 😎
///
`HeroPublic` содержит все поля `HeroBase`, а также поле `id`, объявленное как `int` (не `None`):
Все поля `HeroPublic` такие же, как в `HeroBase`, а `id` объявлен как `int` (не `None`):
* `id`
* `name`
@ -237,23 +236,23 @@ $ fastapi dev main.py
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
#### Модель для создания героя `HeroCreate`
#### `HeroCreate`*модель данных* для создания героя { #herocreate-the-data-model-to-create-a-hero }
Сейчас мы создадим модель `HeroCreate`. Эта модель будет использоваться для проверки данных, переданных клиентом.
Теперь создадим модель `HeroCreate`, она будет **валидировать** данные от клиентов.
Она содержит те же поля, что и `HeroBase`, а также поле `secret_name`.
У неё те же поля, что и у `HeroBase`, а также есть `secret_name`.
Теперь, при создании нового героя, клиенты будут передавать секретное имя `secret_name`, которое будет сохранено в базе данных, но не будет возвращено в ответе API клиентам.
Теперь, когда клиенты **создают нового героя**, они будут отправлять `secret_name`, он сохранится в базе данных, но не будет возвращаться клиентам в API.
/// tip | Подсказка
Вот как нужно работать с **паролями**: получайте их, но не возвращайте их через API.
Так следует обрабатывать **пароли**: принимать их, но не возвращать в API.
Также хэшируйте значения паролей перед тем, как их сохранить. Ни в коем случае не храните пароли в открытом виде, как обычный текст.
Также перед сохранением значения паролей нужно **хэшировать**, **никогда не храните их в открытом виде**.
///
Поля модели `HeroCreate`:
Поля `HeroCreate`:
* `name`
* `age`
@ -261,15 +260,15 @@ $ fastapi dev main.py
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
#### Модель для обновления данных героя `HeroUpdate`
#### `HeroUpdate`*модель данных* для обновления героя { #heroupdate-the-data-model-to-update-a-hero }
В предыдущих версиях нашей программы мы не могли обновить данные героя, теперь, воспользовавшись дополнительными моделями, мы сможем это сделать. 🎉
В предыдущей версии приложения у нас не было способа **обновлять героя**, но теперь, с **несколькими моделями**, мы можем это сделать. 🎉
Модель данных `HeroUpdate` в некотором смысле особенная. Она содержит все те же поля, что и модель создания героя, но все поля модели являются **необязательными**. (Все они имеют значение по умолчанию.) Таким образом, при обновлении данных героя, вам достаточно передать только те поля, которые требуют изменения.
*Модель данных* `HeroUpdate` особенная: у неё **те же поля**, что и для создания нового героя, но все поля **необязательные** (у всех есть значение по умолчанию). Таким образом, при обновлении героя можно отправлять только те поля, которые нужно изменить.
Поскольку **все поля по сути меняются** (теперь тип каждого поля допускает значение `None` и значение по умолчанию `None`), мы должны их **объявить заново**.
Поскольку **фактически меняются все поля** (их тип теперь включает `None`, и по умолчанию они равны `None`), нам нужно **переобъявить** их.
Фактически, нам не нужно наследоваться от `HeroBase`, потому что мы будем заново объявлять все поля. Я оставлю наследование просто для поддержания общего стиля, но оно (наследование) здесь необязательно. 🤷
Наследоваться от `HeroBase` не обязательно, так как мы заново объявляем все поля. Я оставлю наследование для единообразия, но это не необходимо. Скорее дело вкуса. 🤷
Поля `HeroUpdate`:
@ -279,59 +278,59 @@ $ fastapi dev main.py
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
### Создание героя с помощью `HeroCreate` и возвращение результатов с помощью `HeroPublic`
### Создание с `HeroCreate` и возврат `HeroPublic` { #create-with-herocreate-and-return-a-heropublic }
Теперь, когда у нас есть дополнительные модели, мы можем обновить те части приложения, которые их используют.
Теперь, когда у нас есть **несколько моделей**, мы можем обновить части приложения, которые их используют.
Вместе c запросом на создание героя мы получаем объект данных `HeroCreate`, и создаем на его основе объект модели таблицы `Hero`.
Мы получаем в запросе *модель данных* `HeroCreate` и на её основе создаём *модель-таблицу* `Hero`.
Созданный объект *модели таблицы* `Hero` будет иметь все поля, переданные клиентом, а также поле `id`, сгенерированное базой данных.
Новая *модель-таблица* `Hero` будет иметь поля, отправленные клиентом, а также `id`, сгенерированный базой данных.
Далее функция вернёт объект *модели таблицы* `Hero`. Но поскольку, мы объявили `HeroPublic` как модель ответа, то **FastAPI** будет использовать именно её для проверки и сериализации данных.
Затем возвращаем из функции ту же *модель-таблицу* `Hero` как есть. Но так как мы объявили `response_model` с *моделью данных* `HeroPublic`, **FastAPI** использует `HeroPublic` для валидации и сериализации данных.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
/// tip | Подсказка
Теперь мы используем модель ответа `response_model=HeroPublic`, вместо того, чтобы объявить тип возвращаемого значения как `-> HeroPublic`. Мы это делаем потому, что тип возвращаемого значения не относится к `HeroPublic`.
Теперь мы используем `response_model=HeroPublic` вместо **аннотации типа возвращаемого значения** `-> HeroPublic`, потому что фактически возвращаемое значение — это *не* `HeroPublic`.
Если бы мы объявили тип возвращаемого значения как `-> HeroPublic`, то редактор и линтер начали бы ругаться (и вполне справедливо), что возвращаемое значение принадлежит типу `Hero`, а совсем не `HeroPublic`.
Если бы мы объявили `-> HeroPublic`, ваш редактор кода и линтер справедливо пожаловались бы, что вы возвращаете `Hero`, а не `HeroPublic`.
Объявляя модель ответа в `response_model`, мы как бы говорим **FastAPI**: делай свое дело, не вмешиваясь в аннотацию типов и не полагаясь на помощь редактора или других инструментов.
Объявляя модель в `response_model`, мы говорим **FastAPI** сделать своё дело, не вмешиваясь в аннотации типов и работу редактора кода и других инструментов.
///
### Чтение данных героев с помощью `HeroPublic`
### Чтение героев с `HeroPublic` { #read-heroes-with-heropublic }
Мы можем проделать то же самое **для чтения данных** героев. Мы применим модель ответа `response_model=list[HeroPublic]`, и тем самым обеспечим правильную проверку и сериализацию данных.
Аналогично мы можем **читать** `Hero` — снова используем `response_model=list[HeroPublic]`, чтобы данные валидировались и сериализовались корректно.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
### Чтение данных отдельного героя с помощью `HeroPublic`
### Чтение одного героя с `HeroPublic` { #read-one-hero-with-heropublic }
Мы можем **прочитать** данные отдельного героя:
Мы можем **прочитать** одного героя:
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
### Обновление данных героя с помощью `HeroUpdate`
### Обновление героя с `HeroUpdate` { #update-a-hero-with-heroupdate }
Мы можем **обновить данные героя**. Для этого мы воспользуемся HTTP методом `PATCH`.
Мы можем **обновить героя**. Для этого используем HTTP операцию `PATCH`.
В коде мы получаем объект словаря `dict` с данными, переданными клиентом (т.е. **только c данными, переданными клиентом**, исключая любые значения, которые могли бы быть там только потому, что они являются значениями по умолчанию). Для того чтобы сделать это, мы воспользуемся опцией `exclude_unset=True`. В этом главная хитрость. 🪄
В коде мы получаем `dict` со всеми данными, отправленными клиентом — **только с данными, отправленными клиентом**, исключая любые значения, которые были бы там лишь как значения по умолчанию. Для этого мы используем `exclude_unset=True`. Это главный трюк. 🪄
Затем мы применим `hero_db.sqlmodel_update(hero_data)`, и обновим `hero_db`, использовав данные `hero_data`.
Затем мы используем `hero_db.sqlmodel_update(hero_data)`, чтобы обновить `hero_db` данными из `hero_data`.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
### Удалим героя ещё раз
### Снова удаление героя { #delete-a-hero-again }
Операция **удаления** героя практически не меняется.
Операция **удаления** героя остаётся практически прежней.
В данном случае желание *`отрефакторить всё`* остаётся неудовлетворенным. 😅
Желание *«отрефакторить всё»* на этот раз останется неудовлетворённым. 😅
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
### Снова запустим приложение
### Снова запустим приложение { #run-the-app-again }
Вы можете снова запустить приложение:
@ -345,14 +344,14 @@ $ fastapi dev main.py
</div>
Если вы перейдете в пользовательский интерфейс API `/docs`, то вы увидите, что он был обновлен, и больше не принимает параметра `id` от клиента при создании нового героя, и т.д.
Если вы перейдёте в UI API `/docs`, вы увидите, что он обновился: теперь при создании героя он не ожидает получить `id` от клиента и т. д.
<div class="screenshot">
<img src="/img/tutorial/sql-databases/image02.png">
</div>
## Резюме
## Резюме { #recap }
Вы можете использовать <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">**SQLModel**</a> для взаимодействия с реляционными базами данных, а также для упрощения работы с **моделями данных** и **моделями таблиц**.
Вы можете использовать <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">**SQLModel**</a> для взаимодействия с SQL базой данных и упростить код с помощью *моделей данных* и *моделей-таблиц*.
Вы можете узнать гораздо больше информации в документации по **SQLModel**. Там вы найдете более подробное <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">мини-руководство по использованию SQLModel с **FastAPI**</a>. 🚀
Гораздо больше вы можете узнать в документации **SQLModel**, там есть более подробный мини-<a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">туториал по использованию SQLModel с **FastAPI**</a>. 🚀

18
docs/ru/docs/tutorial/static-files.md

@ -1,11 +1,11 @@
# Статические Файлы
# Статические Файлы { #static-files }
Вы можете предоставлять статические файлы автоматически из директории, используя `StaticFiles`.
## Использование `StaticFiles`
## Использование `StaticFiles` { #use-staticfiles }
* Импортируйте `StaticFiles`.
* "Примонтируйте" экземпляр `StaticFiles()` с указанием определенной директории.
* "Примонтируйте" экземпляр `StaticFiles()` к определённому пути.
{* ../../docs_src/static_files/tutorial001.py hl[2,6] *}
@ -17,16 +17,16 @@
///
### Что такое "Монтирование"
### Что такое "Монтирование" { #what-is-mounting }
"Монтирование" означает добавление полноценного "независимого" приложения в определенную директорию, которое затем обрабатывает все подпути.
"Монтирование" означает добавление полноценного "независимого" приложения на определённый путь, которое затем обрабатывает все подпути.
Это отличается от использования `APIRouter`, так как примонтированное приложение является полностью независимым.
OpenAPI и документация из вашего главного приложения не будет содержать ничего из примонтированного приложения, и т.д.
OpenAPI и документация из вашего главного приложения не будут содержать ничего из примонтированного приложения, и т.д.
Вы можете прочитать больше об этом в **Расширенном руководстве пользователя**.
Вы можете прочитать больше об этом в [Расширенном руководстве пользователя](../advanced/index.md){.internal-link target=_blank}.
## Детали
## Детали { #details }
Первый параметр `"/static"` относится к подпути, по которому это "подприложение" будет "примонтировано". Таким образом, любой путь начинающийся со `"/static"` будет обработан этим приложением.
@ -36,6 +36,6 @@ OpenAPI и документация из вашего главного прил
Все эти параметры могут отличаться от "`static`", настройте их в соответствии с вашими нуждами и конкретными деталями вашего собственного приложения.
## Больше информации
## Больше информации { #more-info }
Для получения дополнительной информации о деталях и настройках ознакомьтесь с <a href="https://www.starlette.io/staticfiles/" class="external-link" target="_blank">Документацией Starlette о статических файлах</a>.

40
docs/ru/docs/tutorial/testing.md

@ -1,4 +1,4 @@
# Тестирование
# Тестирование { #testing }
Благодаря <a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette</a>, тестировать приложения **FastAPI** легко и приятно.
@ -6,13 +6,17 @@
Используя эти инструменты, Вы можете напрямую задействовать <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a> с **FastAPI**.
## Использование класса `TestClient`
## Использование класса `TestClient` { #using-testclient }
/// info | Информация
Для использования класса `TestClient` необходимо установить библиотеку <a href="https://www.python-httpx.org" class="external-link" target="_blank">`httpx`</a>.
Для использования класса `TestClient` сначала установите <a href="https://www.python-httpx.org" class="external-link" target="_blank">`httpx`</a>.
Например, так: `pip install httpx`.
Убедитесь, что Вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его, а затем установили пакет, например:
```console
$ pip install httpx
```
///
@ -42,7 +46,7 @@
Также можно написать `from starlette.testclient import TestClient`.
**FastAPI** предоставляет тот же самый `starlette.testclient` как `fastapi.testclient`. Это всего лишь небольшое удобство для Вас, как разработчика.
**FastAPI** предоставляет тот же самый `starlette.testclient` как `fastapi.testclient`. Это всего лишь небольшое удобство для Вас, как разработчика. Но он берётся напрямую из Starlette.
///
@ -52,13 +56,13 @@
///
## Разделение тестов и приложения
## Разделение тестов { #separating-tests }
В реальном приложении Вы, вероятно, разместите тесты в отдельном файле.
Кроме того, Ваше приложение **FastAPI** может состоять из нескольких файлов, модулей и т.п.
### Файл приложения **FastAPI**
### Файл приложения **FastAPI** { #fastapi-app-file }
Допустим, структура файлов Вашего приложения похожа на ту, что описана на странице [Более крупные приложения](bigger-applications.md){.internal-link target=_blank}:
@ -69,12 +73,12 @@
│   └── main.py
```
Здесь файл `main.py` является "точкой входа" в Ваше приложение и содержит инициализацию Вашего приложения **FastAPI**:
В файле `main.py` находится Ваше приложение **FastAPI**:
{* ../../docs_src/app_testing/main.py *}
### Файл тестов
### Файл тестов { #testing-file }
Также у Вас может быть файл `test_main.py` содержащий тесты. Можно разместить тестовый файл и файл приложения в одной директории (в директориях для Python-кода желательно размещать и файл `__init__.py`):
@ -93,11 +97,11 @@
...и писать дальше тесты, как и раньше.
## Тестирование: расширенный пример
## Тестирование: расширенный пример { #testing-extended-example }
Теперь давайте расширим наш пример и добавим деталей, чтоб посмотреть, как тестировать различные части приложения.
### Расширенный файл приложения **FastAPI**
### Расширенный файл приложения **FastAPI** { #extended-fastapi-app-file }
Мы продолжим работу с той же файловой структурой, что и ранее:
@ -113,7 +117,7 @@
В нём описана операция `GET`, которая может вернуть ошибку.
Ещё есть операция `POST` и она тоже может вернуть ошибку.
Ещё есть операция `POST`, и она может вернуть несколько ошибок.
Обе *операции пути* требуют наличия в запросе заголовка `X-Token`.
@ -155,7 +159,7 @@
////
//// tab | Python 3.8+ без Annotated
//// tab | Python 3.8+ без Annotated
/// tip | Подсказка
@ -169,7 +173,7 @@
////
### Расширенный файл тестов
### Расширенный файл тестов { #extended-testing-file }
Теперь обновим файл `test_main.py`, добавив в него тестов:
@ -198,9 +202,11 @@
///
## Запуск тестов
## Запуск { #run-it }
Далее Вам нужно установить `pytest`.
Далее Вам нужно установить `pytest`:
Убедитесь, что Вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его, а затем установили пакет, например:
<div class="termy">
@ -214,7 +220,7 @@ $ pip install pytest
Он автоматически найдёт все файлы и тесты, выполнит их и предоставит Вам отчёт о результатах тестирования.
Запустите тесты командой `pytest` и увидите результат:
Запустите тесты:
<div class="termy">

333
docs/ru/docs/virtual-environments.md

@ -1,71 +1,69 @@
# Виртуальная среда
# Виртуальные окружения { #virtual-environments }
При работе с проектами в Python рекомендуется использовать **виртуальную среду разработки** (или какой-нибудь другой подобный механизм). Это нужно для того, чтобы изолировать устанавливаемые пакеты для каждого отдельного проекта.
При работе с проектами на Python рекомендуется использовать **виртуальное окружение** (или похожий механизм), чтобы изолировать пакеты, которые вы устанавливаете для каждого проекта.
/// info | Дополнительная информация
Если вы уже знакомы с виртуальными средами разработки, знаете как их создавать и использовать, то вы можете свободно пропустить данный раздел. 🤓
Если вы уже знакомы с виртуальными окружениями, знаете, как их создавать и использовать, вы можете пропустить этот раздел. 🤓
///
/// tip | Подсказка
**Виртуальная среда** и **переменная окружения** это две разные вещи.
**Виртуальное окружение** — это не то же самое, что **переменная окружения**.
**Переменная окружения** это системная переменная, которую могут использовать программы.
**Переменная окружения** это переменная в системе, которую могут использовать программы.
**Виртуальная среда** это папка, содержащая файлы.
**Виртуальное окружение** — это директория с файлами внутри.
///
/// info | Дополнительная информация
В этом разделе мы научим вас пользоваться виртуальными средами разработки и расскажем, как они работают.
На этой странице вы узнаете, как пользоваться **виртуальными окружениями** и как они работают.
Если же вы готовы воспользоваться инструментом, **который умеет управлять всем, что касается Python-проектов**,
(включая установку Python), то попробуйте <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>.
Если вы готовы начать использовать **инструмент, который управляет всем** за вас (включая установку Python), попробуйте <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>.
///
## Создание проекта
## Создание проекта { #create-a-project }
В первую очередь, создайте директорию для вашего проекта.
Сначала создайте директорию для вашего проекта.
Я обычно создаю папку под названием `code` внутри моего домашнего каталога `/home/user`.
Обычно я создаю папку с именем `code` в моем домашнем каталоге.
Затем внутри данной папки я создаю отдельную директорию под каждый свой проект.
А внутри неё создаю отдельную директорию для каждого проекта.
<div class="termy">
```console
// Перейдите в домашний каталог
$ cd
// Создайте отдельную папку под все будущие программные проекты (code)
// Создайте директорию для всех ваших проектов с кодом
$ mkdir code
// Войдите в директорию code
// Перейдите в эту директорию code
$ cd code
// Создайте директрорию под данный проект (awesome-project)
// Создайте директорию для этого проекта
$ mkdir awesome-project
// Перейдите в созданную директорию проекта
// Перейдите в директорию проекта
$ cd awesome-project
```
</div>
## Создание виртуальной среды разработки
## Создание виртуального окружения { #create-a-virtual-environment }
Начиная работу с Python-проектом, сразу же создавайте виртуальную среду разработки
**<abbr title="есть и другие опции, но мы рассмотрим наиболее простой вариант">внутри вашего проекта</abbr>**.
Когда вы начинаете работать над Python‑проектом **впервые**, создайте виртуальное окружение **<abbr title="есть и другие опции, но это простой ориентир">внутри вашего проекта</abbr>**.
/// tip | Подсказка
Виртуальная среда разработки создается один раз, и в дальнейшем, работая с проектом, этого больше делать не придется.
Делать это нужно **один раз на проект**, не каждый раз, когда вы работаете.
///
//// tab | `venv`
Для создания виртуальной среды вы можете воспользоваться модулем `venv`, который является частью встроенной библиотеки Python.
Для создания виртуального окружения вы можете использовать модуль `venv`, который поставляется вместе с Python.
<div class="termy">
@ -77,10 +75,10 @@ $ python -m venv .venv
/// details | Что делает эта команда?
* `python`: использовать программу под именем `python`
* `-m`: вызывать модуль как скрипт, в следующей инструкции мы скажем какой именно модуль вызвать
* `venv`: использовать модуль под названием `venv`, который обычно устанавливается вместе с Python
* `.venv`: создать виртуальную среду разработки в новой директории `.venv`
* `python`: использовать программу под названием `python`
* `-m`: вызвать модуль как скрипт, далее мы укажем, какой модуль вызвать
* `venv`: использовать модуль `venv`, который обычно устанавливается вместе с Python
* `.venv`: создать виртуальное окружение в новой директории `.venv`
///
@ -88,7 +86,7 @@ $ python -m venv .venv
//// tab | `uv`
Если вы установили <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>, то вы можете им воспользоваться для создания виртуальной среды разработки.
Если у вас установлен <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>, вы можете использовать его для создания виртуального окружения.
<div class="termy">
@ -100,29 +98,29 @@ $ uv venv
/// tip | Подсказка
По умолчанию `uv` создаст виртуальную среду разработки в папке под названием `.venv`.
По умолчанию `uv` создаст виртуальное окружение в директории с именем `.venv`.
Но вы можете это изменить, передав дополнительный аргумент с именем директории.
Но вы можете переопределить это, передав дополнительный аргумент с именем директории.
///
////
Данная команда создаст новую виртуальную среду разработки в папке `.venv`.
Эта команда создаст новое виртуальное окружение в директории `.venv`.
/// details | `.venv` или другое имя?
Вы можете поместить виртуальную среду разработки в папку с другим именем, но традиционным (конвенциональным) названием является `.venv` .
Вы можете создать виртуальное окружение в другой директории, но по соглашению его называют `.venv`.
///
## Активация виртуальной среды разработки
## Активация виртуального окружения { #activate-the-virtual-environment }
Активируйте виртуальную среду разработки, и тогда любая запускаемая Python-команда или устанавливаемый пакет будут ее использовать.
Активируйте новое виртуальное окружение, чтобы все команды Python и устанавливаемые пакеты использовали именно его.
/// tip | Подсказка
При работе над проектом делайте это **каждый раз** при запуске **новой сессии в терминале**.
Делайте это **каждый раз**, когда вы начинаете **новую сессию терминала** для работы над проектом.
///
@ -152,7 +150,7 @@ $ .venv\Scripts\Activate.ps1
//// tab | Windows Bash
Или при использовании Bash для Windows (напр. <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>):
Или если вы используете Bash для Windows (например, <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>):
<div class="termy">
@ -164,13 +162,21 @@ $ source .venv/Scripts/activate
////
## Проверка активации виртуальной среды
/// tip | Подсказка
Каждый раз, когда вы устанавливаете **новый пакет** в это окружение, **активируйте** окружение снова.
Проверьте, активна ли виртуальная среда (удостоверимся, что предыдущая команда сработала).
Это гарантирует, что если вы используете **программу терминала (<abbr title="command line interface – интерфейс командной строки">CLI</abbr>)**, установленную этим пакетом, вы будете использовать именно ту, что из вашего виртуального окружения, а не какую‑то глобально установленную, возможно другой версии, чем вам нужна.
///
## Проверка, что виртуальное окружение активно { #check-the-virtual-environment-is-active }
Проверьте, что виртуальное окружение активно (предыдущая команда сработала).
/// tip | Подсказка
Убедитесь в том, что все работает так, как нужно и вы используете именно ту виртуальную среду разработки, которую нужно. Делать это необязательно, но желательно.
Это **необязательно**, но это хороший способ **проверить**, что всё работает как ожидается и вы используете запланированное виртуальное окружение.
///
@ -186,7 +192,7 @@ $ which python
</div>
Если данная команда показывает, что исполняемый файл `python` (`.venv\bin\python`), находится внутри виртуальной среды вашего проекта (у нас это `awesome-project`), значит все отработало как нужно. 🎉
Если отображается исполняемый файл `python` по пути `.venv/bin/python` внутри вашего проекта (в нашем случае `awesome-project`), значит всё сработало. 🎉
////
@ -202,29 +208,29 @@ C:\Users\user\code\awesome-project\.venv\Scripts\python
</div>
Если данная команда показывает, что исполняемый файл `python` (`.venv\Scripts\python`), находится внутри виртуальной среды вашего проекта (у нас это `awesome-project`), значит все отработало как нужно. 🎉
Если отображается исполняемый файл `python` по пути `.venv\Scripts\python` внутри вашего проекта (в нашем случае `awesome-project`), значит всё сработало. 🎉
////
## Обновление `pip`
## Обновление `pip` { #upgrade-pip }
/// tip | Подсказка
Если вы используете <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>, то вы должны будете его использовать для установки пакетов вместо `pip`, поэтому обновлять `pip` вам ненужно. 😎
Если вы используете <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>, то для установки вы будете использовать его вместо `pip`, поэтому обновлять `pip` не нужно. 😎
///
Если для установки пакетов вы используете `pip` (он устанавливается по умолчанию вместе с Python), то обновите `pip` до последней версии.
Если для установки пакетов вы используете `pip` (он идёт по умолчанию вместе с Python), вам стоит **обновить** его до последней версии.
Большинство экзотических ошибок, возникающих при установке пакетов, устраняется предварительным обновлением `pip`.
Многие экзотические ошибки при установке пакетов решаются простым предварительным обновлением `pip`.
/// tip | Подсказка
Обычно это делается только один раз, сразу после создания виртуальной среды разработки.
Обычно это делается **один раз**, сразу после создания виртуального окружения.
///
Убедитесь в том, что виртуальная среда активирована (с помощью вышеуказанной команды) и запустите следующую команду:
Убедитесь, что виртуальное окружение активно (см. команду выше) и запустите:
<div class="termy">
@ -236,19 +242,19 @@ $ python -m pip install --upgrade pip
</div>
## Добавление `.gitignore`
## Добавление `.gitignore` { #add-gitignore }
Если вы используете **Git** (а вы должны его использовать), то добавьте файл `.gitignore` и исключите из Git всё, что находится в папке `.venv`.
Если вы используете **Git** (а вам стоит его использовать), добавьте файл `.gitignore`, чтобы исключить из Git всё, что находится в вашей `.venv`.
/// tip | Подсказка
Если для создания виртуальной среды вы используете <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>, то для вас все уже сделано и вы можете пропустить этот шаг. 😎
Если вы использовали <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> для создания виртуального окружения, он уже сделал это за вас — можно пропустить этот шаг. 😎
///
/// tip | Подсказка
Это делается один раз, сразу после создания виртуальной среды разработки.
Сделайте это **один раз**, сразу после создания виртуального окружения.
///
@ -262,13 +268,13 @@ $ echo "*" > .venv/.gitignore
/// details | Что делает эта команда?
* `echo "*"`: напечатать `*` в консоли (следующий шаг это слегка изменит)
* `>`: все что находится слева от `>` не печатать в консоль, но записать в файл находящийся справа от `>`
* `.gitignore`: имя файла, в который нужно записать текст.
* `echo "*"`: «напечатать» в терминале текст `*` (следующая часть немного меняет поведение)
* `>`: всё, что команда слева от `>` выводит в терминал, вместо печати нужно записать в файл, указанный справа от `>`
* `.gitignore`: имя файла, в который нужно записать текст
`*` в Git означает "всё". Т.е. будет проигнорировано всё, что содержится в папке `.venv`.
А `*` в Git означает «всё». То есть будет игнорироваться всё в директории `.venv`.
Данная команда создаст файл `.gitignore` следующего содержания:
Эта команда создаст файл `.gitignore` со следующим содержимым:
```gitignore
*
@ -276,25 +282,25 @@ $ echo "*" > .venv/.gitignore
///
## Установка пакетов
## Установка пакетов { #install-packages }
После установки виртуальной среды, вы можете устанавливать в нее пакеты.
После активации окружения вы можете устанавливать в него пакеты.
/// tip | Подсказка
Сделайте это **один раз**, при установке или обновлении пакетов, нужных вашему проекту.
Сделайте это **один раз** при установке или обновлении пакетов, необходимых вашему проекту.
Если вам понадобится обновить версию пакета или добавить новый пакет, то вы должны будете **сделать это снова**.
Если вам нужно обновить версию или добавить новый пакет, вы **сделаете это снова**.
///
### Установка пакетов напрямую
### Установка пакетов напрямую { #install-packages-directly }
Если вы торопитесь и не хотите объявлять зависимости проекта в отдельном файле, то вы можете установить их напрямую.
Если вы торопитесь и не хотите объявлять зависимости проекта в отдельном файле, вы можете установить их напрямую.
/// tip | Подсказка
Объявление пакетов, которые использует ваш проект, и их версий в отдельном файле (например, в `requirements.txt` или в `pyproject.toml`) - это отличная идея.
Очень хорошая идея — указать используемые вашим проектом пакеты и их версии в файле (например, `requirements.txt` или `pyproject.toml`).
///
@ -314,7 +320,7 @@ $ pip install "fastapi[standard]"
//// tab | `uv`
Если вы используете <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>:
Если у вас установлен <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>:
<div class="termy">
@ -327,9 +333,9 @@ $ uv pip install "fastapi[standard]"
////
### Установка из `requirements.txt`
### Установка из `requirements.txt` { #install-from-requirements-txt }
Если у вас есть `requirements.txt`, то вы можете использовать его для установки пакетов.
Если у вас есть `requirements.txt`, вы можете использовать его для установки пакетов.
//// tab | `pip`
@ -346,7 +352,7 @@ $ pip install -r requirements.txt
//// tab | `uv`
Если вы используете <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>:
Если у вас установлен <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>:
<div class="termy">
@ -361,7 +367,7 @@ $ uv pip install -r requirements.txt
/// details | `requirements.txt`
`requirements.txt` с парочкой пакетов внутри выглядит приблизительно так:
`requirements.txt` с некоторыми пакетами может выглядеть так:
```requirements.txt
fastapi[standard]==0.113.0
@ -370,9 +376,9 @@ pydantic==2.8.0
///
## Запуск программы
## Запуск вашей программы { #run-your-program }
После активации виртуальной среды разработки вы можете запустить свою программу и она будет использовать версию Python и пакеты, установленные в виртуальной среде.
После активации виртуального окружения вы можете запустить свою программу, и она будет использовать Python из вашего виртуального окружения вместе с установленными там пакетами.
<div class="termy">
@ -384,9 +390,9 @@ Hello World
</div>
## Настройка редактора
## Настройка вашего редактора кода { #configure-your-editor }
Вероятно, вы захотите воспользоваться редактором. Убедитесь, что вы настроили его на использование той самой виртуальной среды, которую вы создали. (Скорее всего, она автоматически будет обнаружена). Это позволит вам использовать авто-завершение и выделение ошибок в редакторе.
Скорее всего, вы будете использовать редактор кода. Убедитесь, что вы настроили его на использование того же виртуального окружения, которое вы создали (обычно он определяет его автоматически), чтобы получить автозавершение и подсветку ошибок.
Например:
@ -395,13 +401,13 @@ Hello World
/// tip | Подсказка
Обычно это делается один раз, при создании виртуальной среды разработки.
Обычно это нужно сделать только **один раз**, при создании виртуального окружения.
///
## Деактивация виртуальной среды разработки
## Деактивация виртуального окружения { #deactivate-the-virtual-environment }
По окончании работы над проектом вы можете деактивировать виртуальную среду.
Когда закончите работу над проектом, вы можете **деактивировать** виртуальное окружение.
<div class="termy">
@ -411,55 +417,55 @@ $ deactivate
</div>
Таким образом, при запуске `python`, будет использована версия `python` установленная глобально, а не из этой виртуальной среды вместе с установленными в ней пакетами.
Таким образом, при запуске `python` он не будет пытаться запускаться из этого виртуального окружения с установленными там пакетами.
## Все готово к работе
## Готово к работе { #ready-to-work }
Теперь вы готовы к тому, чтобы начать работу над своим проектом.
Теперь вы готовы начать работать над своим проектом.
/// tip | Подсказка
Хотите разобраться со всем, что написано выше?
Хотите понять, что это всё было выше?
Продолжайте читать. 👇🤓
///
## Зачем использовать виртуальную среду?
## Зачем нужны виртуальные окружения { #why-virtual-environments }
Для работы с FastAPI вам потребуется установить <a href="https://www.python.org/" class="external-link" target="_blank">Python</a>.
Чтобы работать с FastAPI, вам нужно установить <a href="https://www.python.org/" class="external-link" target="_blank">Python</a>.
После этого вам нужно будет **установить** FastAPI и другие **пакеты**, которые вы собираетесь использовать.
После этого вам нужно будет **установить** FastAPI и другие **пакеты**, которые вы хотите использовать.
Для установки пакетов обычно используют `pip`, который устанавливается вместе с Python (или же используют альтернативные решения).
Для установки пакетов обычно используют команду `pip`, которая идет вместе с Python (или альтернативные инструменты).
Тем не менее, если вы просто используете `pip` напрямую, то пакеты будут установлены в **глобальное Python-окружение** (глобально установленный Python).
Тем не менее, если просто использовать `pip` напрямую, пакеты будут установлены в **глобальное окружение Python** (глобально установленный Python).
### Проблема
### Проблема { #the-problem }
Так в чем же проблема с установкой пакетов в глобальную среду Python?
Так в чём проблема установки пакетов в глобальное окружение Python?
В какой-то момент вам, вероятно, придется писать множество разных программ, которые используют различные пакеты. 😱
Со временем вы, вероятно, будете писать много разных программ, зависящих от **разных пакетов**. И некоторые из ваших проектов будут зависеть от **разных версий** одного и того же пакета. 😱
Например, вы создаете проект `philosophers-stone`, который зависит от пакета под названием **`harry`, версии `1`**. Таким образом, вам нужно установить `harry`.
Например, вы можете создать проект `philosophers-stone`, который зависит от пакета **`harry` версии `1`**. Значит, нужно установить `harry`.
```mermaid
flowchart LR
stone(philosophers-stone) -->|requires| harry-1[harry v1]
```
Затем, в какой-то момент, вы создаете другой проект под названием `prisoner-of-azkaban`, и этот проект тоже зависит от `harry`, но он уже использует **`harry` версии `3`**.
Затем вы создаёте другой проект `prisoner-of-azkaban`, который тоже зависит от `harry`, но ему нужен **`harry` версии `3`**.
```mermaid
flowchart LR
azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]
```
Проблема заключается в том, что при установке в глобальное окружение, а не в локальную виртуальную среду разработки, вам нужно будет выбирать, какую версию пакета `harry` устанавливать.
Проблема в том, что если устанавливать пакеты глобально (в глобальное окружение), а не в локальное **виртуальное окружение**, вам придётся выбирать, какую версию `harry` установить.
Если вам нужен `philosophers-stone`, то вам нужно сначала установить `harry` версии `1`:
Если вы хотите запустить `philosophers-stone`, сначала нужно установить `harry` версии `1`, например так:
<div class="termy">
@ -469,7 +475,7 @@ $ pip install "harry==1"
</div>
И тогда в вашем глобальном окружении Python будет установлен `harry` версии `1`:
Тогда у вас в глобальном окружении Python будет установлен `harry` версии `1`:
```mermaid
flowchart LR
@ -481,7 +487,7 @@ flowchart LR
end
```
Но если позднее вы захотите запустить `prisoner-of-azkaban`, то вам нужно будет удалить `harry` версии 1, и установить `harry` версии `3` (при установке пакета версии `3` поверх пакета версии `1`, пакет версии `1` удаляется автоматически).
Но если затем вы захотите запустить `prisoner-of-azkaban`, вам нужно будет удалить `harry` версии `1` и установить `harry` версии `3` (или просто установка версии `3` автоматически удалит версию `1`).
<div class="termy">
@ -491,9 +497,9 @@ $ pip install "harry==3"
</div>
И тогда, в вашей глобальной среде окружения Python, будет установлен пакет `harry` версии `3`.
В итоге у вас будет установлен `harry` версии `3` в глобальном окружении Python.
И когда вы снова попытаетесь запустить `philosophers-stone`, то существует вероятность того, что он не будет работать, так как он использует `harry` версии `1`.
А если вы снова попробуете запустить `philosophers-stone`, есть шанс, что он **не будет работать**, так как ему нужен `harry` версии `1`.
```mermaid
flowchart LR
@ -512,47 +518,47 @@ flowchart LR
/// tip | Подсказка
В пакетах Python очень часто стараются изо всех сил избегать внесения критических изменений в новые версии, но лучше перестраховаться и планово устанавливать новые версии, а затем запускать тесты, чтобы проверить, все ли работает правильно.
В Python-пакетах часто стараются изо всех сил **избегать ломающих изменений** в **новых версиях**, но лучше действовать осторожно: устанавливать новые версии осознанно и тогда, когда вы можете прогнать тесты и убедиться, что всё работает корректно.
///
Теперь представьте, что это происходит со многими другими пакетами, которые используются в ваших проектах. С этим очень сложно справиться. И скорее всего, в конечном итоге вы будете запускать некоторые проекты с некоторыми несовместимыми зависимостями и не будете знать, почему что-то не работает.
Теперь представьте то же самое с **многими** другими **пакетами**, от которых зависят все ваши **проекты**. Этим очень сложно управлять. И вы, скорее всего, в какой‑то момент будете запускать проекты с **несовместимыми версиями** пакетов и не понимать, почему что‑то не работает.
Кроме того, в зависимости от вашей операционной системы (напр. Linux, Windows, macOS), она может поставляться с уже установленным Python. Вероятно, что в этом случае в ней уже установлены системные пакеты определенных версий. Если вы устанавливаете пакеты глобально, то вы можете **поломать** программы, являющиеся частью ОС.
Кроме того, в зависимости от ОС (например, Linux, Windows, macOS), она может поставляться с уже установленным Python. И тогда, вероятно, в системе уже есть предустановленные пакеты определённых версий, **нужные вашей системе**. Если вы устанавливаете пакеты в глобальное окружение Python, вы можете в итоге **сломать** некоторые системные программы.
## Куда устанавливаются пакеты?
## Куда устанавливаются пакеты { #where-are-packages-installed }
Когда вы устанавливаете Python, то на вашей машине создается некоторое количество директорий, содержащих некоторое количество файлов.
Когда вы устанавливаете Python, на вашем компьютере создаются некоторые директории с файлами.
Среди них есть каталоги, отвечающие за хранение всех устанавливаемых вами пакетов.
Часть этих директорий отвечает за хранение всех устанавливаемых вами пакетов.
Когда вы запустите команду:
Когда вы запускаете:
<div class="termy">
```console
// Не запускайте эту команду, это просто пример 🤓
// Не запускайте это сейчас, это просто пример 🤓
$ pip install "fastapi[standard]"
---> 100%
```
</div>
То будет скачан сжатый файл, содержащий код FastAPI, обычно скачивание происходит с <a href="https://pypi.org/project/fastapi/" class="external-link" target="_blank">PyPI</a>.
Будет загружен сжатый файл с кодом FastAPI, обычно с <a href="https://pypi.org/project/fastapi/" class="external-link" target="_blank">PyPI</a>.
Также будут скачаны файлы, содержащие пакеты, которые использует FastAPI.
Также будут **загружены** файлы для других пакетов, от которых зависит FastAPI.
Затем все файлы будут извлечены и помещены в директорию на вашем компьютере.
Затем все эти файлы будут **распакованы** и помещены в директорию на вашем компьютере.
По умолчанию эти файлы будут загружены и извлечены в один из каталогов установки Python, т.е. в глобальную среду.
По умолчанию они попадут в директорию из вашей установки Python — это **глобальное окружение**.
## Что такое виртуальная среда разработки?
## Что такое виртуальные окружения { #what-are-virtual-environments }
Решением проблемы размещения всех пакетов в глобальной среде будет использование отдельной виртуальной среды под каждый проект, над которым вы работаете.
Решение проблемы с пакетами в глобальном окружении — использовать **виртуальное окружение для каждого проекта**, над которым вы работаете.
Виртуальная среда это обычная папка, очень похожая на глобальную, куда вы можете устанавливать пакеты для вашего проекта.
Виртуальное окружение — это **директория**, очень похожая на глобальную, куда вы можете устанавливать пакеты для конкретного проекта.
Таким образом, каждый проект будет иметь свою отдельную виртуальную среду разработки (в директории `.venv`) вместе со своими пакетами.
Таким образом, у каждого проекта будет своё виртуальное окружение (директория `.venv`) со своими пакетами.
```mermaid
flowchart TB
@ -571,9 +577,9 @@ flowchart TB
stone-project ~~~ azkaban-project
```
## Что означает активация виртуальной среды?
## Что означает активация виртуального окружения { #what-does-activating-a-virtual-environment-mean }
Когда вы активируете виртуальную среду разработки, например, так:
Когда вы активируете виртуальное окружение, например так:
//// tab | Linux, macOS
@ -601,7 +607,7 @@ $ .venv\Scripts\Activate.ps1
//// tab | Windows Bash
Или если вы воспользуетесь Bash под Windows (напр. <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>):
Или если вы используете Bash для Windows (например, <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>):
<div class="termy">
@ -613,10 +619,9 @@ $ source .venv/Scripts/activate
////
Эта команда создаст или изменит некоторые [переменные окружения](environment-variables.md){.internal-link target=_blank}, которые будут доступны для следующих команд.
Эта команда создаст или изменит некоторые [переменные окружения](environment-variables.md){.internal-link target=_blank}, которые будут доступны для последующих команд.
Одной из таких переменных является `PATH`.
Одна из таких переменных — `PATH`.
/// tip | Подсказка
@ -624,9 +629,9 @@ $ source .venv/Scripts/activate
///
При активации виртуальной среды путь `.venv/bin` (для Linux и macOS) или `.venv\Scripts` (для Windows) добавляется в переменную окружения `PATH`.
Активация виртуального окружения добавляет его путь `.venv/bin` (на Linux и macOS) или `.venv\Scripts` (на Windows) в переменную окружения `PATH`.
Предположим, что до активации виртуальной среды переменная `PATH` выглядела так:
Предположим, что до активации окружения переменная `PATH` выглядела так:
//// tab | Linux, macOS
@ -634,7 +639,7 @@ $ source .venv/Scripts/activate
/usr/bin:/bin:/usr/sbin:/sbin
```
Это означает, что система ищет программы в следующих каталогах:
Это означает, что система будет искать программы в:
* `/usr/bin`
* `/bin`
@ -649,13 +654,13 @@ $ source .venv/Scripts/activate
C:\Windows\System32
```
Это означает, что система ищет программы в:
Это означает, что система будет искать программы в:
* `C:\Windows\System32`
////
После активации виртуальной среды переменная окружение `PATH` будет выглядеть примерно так:
После активации виртуального окружения переменная `PATH` будет выглядеть примерно так:
//// tab | Linux, macOS
@ -663,21 +668,21 @@ C:\Windows\System32
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Это означает, что система теперь будет искать программы в:
Это означает, что теперь система в первую очередь будет искать программы в:
```plaintext
/home/user/code/awesome-project/.venv/bin
```
прежде чем начать искать в других каталогах.
прежде чем искать в других директориях.
Таким образом, когда вы введете в консоль `python`, система будет искать Python в
Поэтому, когда вы введёте в терминале `python`, система найдёт программу Python по пути
```plaintext
/home/user/code/awesome-project/.venv/bin/python
```
и будет использовать именно его.
и использует именно её.
////
@ -687,31 +692,31 @@ C:\Windows\System32
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
```
Это означает, что система в первую очередь начнет искать программы в:
Это означает, что теперь система в первую очередь будет искать программы в:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts
```
прежде чем начать искать в других директориях.
прежде чем искать в других директориях.
Таким образом, если вы введете в консоль команду `python`, то система найдет Python в:
Поэтому, когда вы введёте в терминале `python`, система найдёт программу Python по пути
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
и использует его.
и использует именно её.
////
Очень важной деталью является то, что путь к виртуальной среде будет помещен в самое начало переменной `PATH`. Система обнаружит данный путь к Python раньше, чем какой-либо другой. Таким образом, при запуске команды `python`, будет использован именно Python из виртуальной среды разработки, а не какой-нибудь другой (например, Python из глобальной среды)
Важная деталь: путь к виртуальному окружению будет добавлен в самое **начало** переменной `PATH`. Система найдёт его **раньше**, чем любой другой установленный Python. Таким образом, при запуске `python` будет использоваться Python **из виртуального окружения**, а не какой‑то другой `python` (например, из глобального окружения).
Активация виртуальной среды разработки также меняет и несколько других вещей, но данная функция является основной.
Активация виртуального окружения также меняет ещё несколько вещей, но это — одна из важнейших.
## Проверка виртуальной среды
## Проверка виртуального окружения { #checking-a-virtual-environment }
Когда вы проверяете активна ли виртуальная среда разработки, например, так:
Когда вы проверяете, активно ли виртуальное окружение, например, так:
//// tab | Linux, macOS, Windows Bash
@ -741,33 +746,33 @@ C:\Users\user\code\awesome-project\.venv\Scripts\python
////
Это означает, что будет использоваться `python` **из виртуальной среды разработки**.
Это означает, что будет использоваться программа `python` **из виртуального окружения**.
Вы используете `which` для Linux и macOS и `Get-Command` для Windows PowerShell.
На Linux и macOS используется `which`, а в Windows PowerShell — `Get-Command`.
Эта команда работает следующим образом: она проверяет переменную окружения `PATH`, проходя по очереди каждый указанный путь в поисках программы под названием `python`. И когда она её находит, то возвращает путь к данной программе.
Как работает эта команда: она проходит по переменной окружения `PATH`, идя **по каждому пути по порядку**, и ищет программу с именем `python`. Как только находит — **показывает путь** к этой программе.
Основной момент при вызове команды `python` состоит в том, какой именно "`python`" будет запущен.
Самое важное — при вызове `python` именно этот «`python`» и будет выполняться.
Таким образом, вы можете убедиться, что используете правильную виртуальную среду разработки.
Так вы можете подтвердить, что находитесь в правильном виртуальном окружении.
/// tip | Подсказка
Легко активировать одну виртуальную среду, вызвать один Python и **перейти к следующему проекту**.
Легко активировать одно виртуальное окружение, получить один Python, а затем **перейти к другому проекту**.
И следующий проект не будет работать потому, что вы используете **неправильный Python** из виртуальной среды другого проекта.
И второй проект **не будет работать**, потому что вы используете **не тот Python**, из виртуального окружения другого проекта.
Так что, будет нелишним проверить, какой `python` вы используете. 🤓
Полезно уметь проверить, какой именно `python` используется. 🤓
///
## Зачем деактивируют виртуальную среду?
## Зачем деактивировать виртуальное окружение { #why-deactivate-a-virtual-environment }
Предположим, что вы работаете над проектом `philosophers-stone`, **активируете виртуальную среду разработки**, устанавливаете пакеты и работаете с данной средой.
Например, вы работаете над проектом `philosophers-stone`, **активируете виртуальное окружение**, устанавливаете пакеты и работаете с ним.
И позже вам понадобилось поработать с **другим проектом** `prisoner-of-azkaban`.
Затем вы хотите поработать над **другим проектом** `prisoner-of-azkaban`.
Вы переходите к этому проекту:
Вы переходите в этот проект:
<div class="termy">
@ -777,7 +782,7 @@ $ cd ~/code/prisoner-of-azkaban
</div>
Если вы не деактивировали виртуальное окружение проекта `philosophers-stone`, то при запуске `python` через консоль будет вызван Python из `philosophers-stone`
Если вы не деактивируете виртуальное окружение `philosophers-stone`, при запуске `python` в терминале он попытается использовать Python из `philosophers-stone`.
<div class="termy">
@ -794,46 +799,46 @@ Traceback (most recent call last):
</div>
Но если вы деактивируете виртуальную среду разработки и активируете новую среду для `prisoner-of-askaban`, то вы тогда запустите Python из виртуального окружения `prisoner-of-azkaban`.
Но если вы деактивируете виртуальное окружение и активируете новое для `prisoner-of-askaban`, тогда при запуске `python` он будет использовать Python из виртуального окружения `prisoner-of-azkaban`.
<div class="termy">
```console
$ cd ~/code/prisoner-of-azkaban
// Вам не требуется находится в старой директории для деактивации среды разработки, вы можете это сделать откуда угодно, даже из каталога другого проекта, в который вы перешли. 😎
// Вам не нужно находиться в старой директории, чтобы деактивировать окружение, вы можете сделать это где угодно, даже после перехода в другой проект 😎
$ deactivate
// Активируйте виртуальную среду разработки в prisoner-of-azkaban/.venv 🚀
// Активируйте виртуальное окружение в prisoner-of-azkaban/.venv 🚀
$ source .venv/bin/activate
// Тепреь, когда вы запустите python, он найдет пакет sirius, установленный в виртуальной среде
// Теперь при запуске python он найдёт пакет sirius, установленный в этом виртуальном окружении
$ python main.py
Я торжественно клянусь в этом! 🐺
I solemnly swear 🐺
```
</div>
## Альтернативы
## Альтернативы { #alternatives }
Это простое руководство поможет вам начать работу и научит тому, как всё работает **изнутри**.
Это простое руководство, чтобы вы начали и поняли, как всё работает **под капотом**.
Существует много альтернативных решений для работы с виртуальными средами разработки, с программными зависимостями, а также с проектами.
Существует много **альтернатив** для управления виртуальными окружениями, зависимостями (requirements), проектами.
Когда вы будете готовы использовать единый инструмент для управления проектом, программными зависимостями, виртуальными средами разработки и т.д., то я рекомендую вам попробовать <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>.
Когда вы будете готовы и захотите использовать инструмент для **управления всем проектом** — зависимостями пакетов, виртуальными окружениями и т. п., я бы предложил попробовать <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>.
`uv` может очень многое. Он умеет:
`uv` может многое:
* **Устанавливать Python**, включая установку различных версий
* Управлять средой виртуального окружения вашего проекта
* **Устанавливать Python**, включая разные версии
* Управлять **виртуальным окружением** ваших проектов
* Устанавливать **пакеты**
* Управлять пакетами и их версиями внутри вашего проекта
* Удостовериться, что вы используете **точный** набор пакетов и версий при установке, включая зависимости. Таким образом, вы можете быть уверенны, что проект, запускается в production, точно также, как и при разработке, этот механизм называется *locking*
* Многие другие вещи
* Управлять **зависимостями и версиями** пакетов вашего проекта
* Обеспечивать наличие **точного** набора пакетов и версий к установке, включая их зависимости, чтобы вы были уверены, что сможете запускать проект в продакшне точно так же, как и на компьютере при разработке — это называется **locking**
* И многое другое
## Заключение
## Заключение { #conclusion }
Если вы прочитали и поняли всё это, то теперь вы знаете **гораздо больше** о виртуальных средах разработки, чем многие другие разработчики. 🤓
Если вы прочитали и поняли всё это, теперь **вы знаете гораздо больше** о виртуальных окружениях, чем многие разработчики. 🤓
Скорее всего, знание этих деталей будет полезно вам в будущем. Когда вы будете отлаживать что-то, кажущееся сложным, вы будете знать, **как это работает под капотом**. 😎
Знание этих деталей, скорее всего, пригодится вам в будущем, когда вы будете отлаживать что‑то сложное: вы будете понимать, **как всё работает под капотом**. 😎

Loading…
Cancel
Save