Например, чтобы объявить ещё один ответ со статус-кодом `404` и Pydantic-моделью `Message`, можно написать:
-{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
+{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
/// note | Примечание
А также ответ со статус-кодом `200`, который использует ваш `response_model`, но включает пользовательский `example`:
-{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
+{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
Всё это будет объединено и включено в ваш OpenAPI и отображено в документации API:
Для этого объявляем метод `__call__`:
-{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
+{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
В этом случае именно `__call__` **FastAPI** использует для проверки дополнительных параметров и подзависимостей, и именно он будет вызван, чтобы позже передать значение параметру в вашей *функции-обработчике пути*.
Теперь мы можем использовать `__init__`, чтобы объявить параметры экземпляра, с помощью которых будем «параметризовать» зависимость:
-{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[9] *}
+{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
В этом случае **FastAPI** вовсе не трогает `__init__` и не зависит от него — мы используем его напрямую в нашем коде.
Мы можем создать экземпляр этого класса так:
-{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[18] *}
+{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
Так мы «параметризуем» нашу зависимость: теперь внутри неё хранится "bar" в атрибуте `checker.fixed_content`.
…и передаст возвращённое значение как значение зависимости в параметр `fixed_content_included` нашей *функции-обработчика пути*:
-{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
+{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
/// tip | Совет
--- /dev/null
+# Продвинутые типы Python { #advanced-python-types }
+
+Ниже несколько дополнительных идей, которые могут быть полезны при работе с типами Python.
+
+## Использование `Union` или `Optional` { #using-union-or-optional }
+
+Если по какой-то причине ваш код не может использовать `|`, например, если это не аннотация типов, а что-то вроде `response_model=`, вместо вертикальной черты (`|`) можно использовать `Union` из `typing`.
+
+Например, вы можете объявить, что значение может быть `str` или `None`:
+
+```python
+from typing import Union
+
+
+def say_hi(name: Union[str, None]):
+ print(f"Hi {name}!")
+```
+
+В `typing` также есть сокращение, чтобы объявить, что значение может быть `None`, — `Optional`.
+
+Вот совет с моей очень субъективной точки зрения:
+
+- 🚨 Избегайте использования `Optional[SomeType]`
+- Вместо этого ✨ используйте **`Union[SomeType, None]`** ✨.
+
+Оба варианта эквивалентны и под капотом это одно и то же, но я бы рекомендовал `Union` вместо `Optional`, потому что слово «optional» может наводить на мысль, что значение необязательное, тогда как на самом деле это означает «значение может быть `None`», даже если оно не является необязательным и по-прежнему требуется.
+
+По-моему, `Union[SomeType, None]` более явно передаёт смысл.
+
+Речь только о словах и названиях. Но эти слова могут влиять на то, как вы и ваша команда думаете о коде.
+
+В качестве примера возьмём такую функцию:
+
+```python
+from typing import Optional
+
+
+def say_hi(name: Optional[str]):
+ print(f"Hey {name}!")
+```
+
+Параметр `name` объявлен как `Optional[str]`, но он не является необязательным: вы не можете вызвать функцию без этого параметра:
+
+```Python
+say_hi() # О нет, это вызывает ошибку! 😱
+```
+
+Параметр `name` по-прежнему обязателен (не «optional»), так как у него нет значения по умолчанию. При этом `name` принимает `None` в качестве значения:
+
+```Python
+say_hi(name=None) # Это работает, None допустим 🎉
+```
+
+Хорошая новость: в большинстве случаев вы сможете просто использовать `|` для объявления объединений типов:
+
+```python
+def say_hi(name: str | None):
+ print(f"Hey {name}!")
+```
+
+Так что обычно вам не о чем переживать из‑за названий вроде `Optional` и `Union`. 😎
Файл `main.py`:
-{* ../../docs_src/async_tests/app_a_py39/main.py *}
+{* ../../docs_src/async_tests/app_a_py310/main.py *}
Файл `test_main.py` содержит тесты для `main.py`, теперь он может выглядеть так:
-{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
+{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
## Запуск тестов { #run-it }
Маркер `@pytest.mark.anyio` говорит pytest, что тестовая функция должна быть вызвана асинхронно:
-{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
+{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
/// tip | Подсказка
Затем мы можем создать `AsyncClient` со ссылкой на приложение и посылать асинхронные запросы, используя `await`.
-{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
+{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
Это эквивалентно следующему:
/// tip | Подсказка
-Если вы столкнулись с `RuntimeError: Task attached to a different loop` при вызове асинхронных функций в ваших тестах (например, при использовании <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MongoDB's MotorClient</a>), то не забывайте инициализировать объекты, которым нужен цикл событий (event loop), только внутри асинхронных функций, например, в `'@app.on_event("startup")` callback.
+Если вы столкнулись с `RuntimeError: Task attached to a different loop` при вызове асинхронных функций в ваших тестах (например, при использовании <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MongoDB's MotorClient</a>), то не забывайте инициализировать объекты, которым нужен цикл событий (event loop), только внутри асинхронных функций, например, в `@app.on_event("startup")` callback.
///
Например, вы объявили операцию пути `/items/`:
-{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
Если клиент обратится к `/items`, по умолчанию произойдёт редирект на `/items/`.
Хотя весь ваш код написан с расчётом, что путь один — `/app`.
-{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
Прокси будет «обрезать» префикс пути на лету перед передачей запроса на сервер приложения (скорее всего Uvicorn, запущенный через FastAPI CLI), поддерживая у вашего приложения иллюзию, что его обслуживают по `/app`, чтобы вам не пришлось менять весь код и добавлять префикс `/api/v1`.
Здесь мы добавляем его в сообщение лишь для демонстрации.
-{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
Затем, если вы запустите Uvicorn так:
Если нет возможности передать опцию командной строки `--root-path` (или аналог), вы можете указать параметр `root_path` при создании приложения FastAPI:
-{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
+{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
Передача `root_path` в `FastAPI` эквивалентна опции командной строки `--root-path` для Uvicorn или Hypercorn.
Uvicorn ожидает, что прокси обратится к нему по `http://127.0.0.1:8000/app`, а уже задача прокси — добавить сверху префикс `/api/v1`.
-## Ð\9e пÑ\80окÑ\81и Ñ\81 Ñ\83Ñ\80езаннÑ\8bм пÑ\80еÑ\84икÑ\81ом пути { #about-proxies-with-a-stripped-path-prefix }
+## Ð\9e пÑ\80окÑ\81и Ñ\81 Ñ\84Ñ\83нкÑ\86ией Ñ\83далениÑ\8f пÑ\80еÑ\84икÑ\81а пути { #about-proxies-with-a-stripped-path-prefix }
-Ð\9fомниÑ\82е, Ñ\87Ñ\82о пÑ\80окÑ\81и Ñ\81 Ñ\83Ñ\80езаннÑ\8bм пÑ\80еÑ\84икÑ\81ом пути — лишь один из вариантов настройки.
+Ð\9fомниÑ\82е, Ñ\87Ñ\82о пÑ\80окÑ\81и Ñ\81 Ñ\84Ñ\83нкÑ\86ией Ñ\83далениÑ\8f пÑ\80еÑ\84икÑ\81а пути — лишь один из вариантов настройки.
-Ð\92о многиÑ\85 Ñ\81лÑ\83Ñ\87аÑ\8fÑ\85 по Ñ\83молÑ\87аниÑ\8e пÑ\80окÑ\81и бÑ\83деÑ\82 без Ñ\83Ñ\80езанного префикса пути.
+Ð\92о многиÑ\85 Ñ\81лÑ\83Ñ\87аÑ\8fÑ\85 по Ñ\83молÑ\87аниÑ\8e пÑ\80окÑ\81и бÑ\83деÑ\82 без Ñ\84Ñ\83нкÑ\86ии Ñ\83далениÑ\8f префикса пути.
-Ð\92 Ñ\82аком Ñ\81лÑ\83Ñ\87ае (без Ñ\83Ñ\80езанного пÑ\80еÑ\84икÑ\81а) прокси слушает, например, по адресу `https://myawesomeapp.com`, и если браузер идёт на `https://myawesomeapp.com/api/v1/app`, а ваш сервер (например, Uvicorn) слушает на `http://127.0.0.1:8000`, то прокси (без урезанного префикса) обратится к Uvicorn по тому же пути: `http://127.0.0.1:8000/api/v1/app`.
+Ð\92 Ñ\82аком Ñ\81лÑ\83Ñ\87ае (без Ñ\84Ñ\83нкÑ\86ии Ñ\83далениÑ\8f пÑ\80еÑ\84икÑ\81а пÑ\83Ñ\82и) прокси слушает, например, по адресу `https://myawesomeapp.com`, и если браузер идёт на `https://myawesomeapp.com/api/v1/app`, а ваш сервер (например, Uvicorn) слушает на `http://127.0.0.1:8000`, то прокси (без урезанного префикса) обратится к Uvicorn по тому же пути: `http://127.0.0.1:8000/api/v1/app`.
## Локальное тестирование с Traefik { #testing-locally-with-traefik }
-Ð\92Ñ\8b можеÑ\82е легко поÑ\8dкÑ\81пеÑ\80именÑ\82иÑ\80оваÑ\82Ñ\8c локалÑ\8cно Ñ\81 Ñ\83Ñ\80езаннÑ\8bм пÑ\80еÑ\84икÑ\81ом пути, используя <a href="https://docs.traefik.io/" class="external-link" target="_blank">Traefik</a>.
+Ð\92Ñ\8b можеÑ\82е легко поÑ\8dкÑ\81пеÑ\80именÑ\82иÑ\80оваÑ\82Ñ\8c локалÑ\8cно Ñ\81 Ñ\84Ñ\83нкÑ\86ией Ñ\83далениÑ\8f пÑ\80еÑ\84икÑ\81а пути, используя <a href="https://docs.traefik.io/" class="external-link" target="_blank">Traefik</a>.
<a href="https://github.com/containous/traefik/releases" class="external-link" target="_blank">Скачайте Traefik</a> — это один бинарный файл; распакуйте архив и запустите его прямо из терминала.
Например:
-{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
+{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
Будет сгенерирована схема OpenAPI примерно такая:
Если вы не хотите, чтобы FastAPI добавлял автоматический сервер, используя `root_path`, укажите параметр `root_path_in_servers=False`:
-{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
+{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
и тогда этот сервер не будет добавлен в схему OpenAPI.
Но если вы уверены, что содержимое, которое вы возвращаете, **сериализуемо в JSON**, вы можете передать его напрямую в класс ответа и избежать дополнительных накладных расходов, которые FastAPI понёс бы, пропуская возвращаемое содержимое через `jsonable_encoder` перед передачей в класс ответа.
-{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial001b_py310.py hl[2,7] *}
/// info | Информация
- Импортируйте `HTMLResponse`.
- Передайте `HTMLResponse` в параметр `response_class` вашего декоратора операции пути.
-{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
/// info | Информация
Тот же пример сверху, возвращающий `HTMLResponse`, может выглядеть так:
-{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
+{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
/// warning | Предупреждение
-`Response`, возвÑ\80аÑ\89Ñ\91ннÑ\8bй напÑ\80Ñ\8fмÑ\83Ñ\8e ваÑ\88ей Ñ\84Ñ\83нкÑ\86ией-обÑ\80абоÑ\82Ñ\87иком пÑ\83Ñ\82и, не бÑ\83деÑ\82 задокÑ\83менÑ\82иÑ\80ован в OpenAPI (напÑ\80имеÑ\80, `Content-Type` нне бÑ\83деÑ\82 задокÑ\83менÑ\82иÑ\80ова) и не будет виден в автоматически сгенерированной интерактивной документации.
+`Response`, возвÑ\80аÑ\89Ñ\91ннÑ\8bй напÑ\80Ñ\8fмÑ\83Ñ\8e ваÑ\88ей Ñ\84Ñ\83нкÑ\86ией-обÑ\80абоÑ\82Ñ\87иком пÑ\83Ñ\82и, не бÑ\83деÑ\82 задокÑ\83менÑ\82иÑ\80ован в OpenAPI (напÑ\80имеÑ\80, `Content-Type` не бÑ\83деÑ\82 задокÑ\83менÑ\82иÑ\80ован) и не будет виден в автоматически сгенерированной интерактивной документации.
///
/// info | Информация
-РазÑ\83мееÑ\82Ñ\81Ñ\8f, Ñ\84акÑ\82иÑ\87еÑ\81кие заголовок `Content-Type`, статус-код и т.д. возьмутся из объекта `Response`, который вы вернули.
+РазÑ\83мееÑ\82Ñ\81Ñ\8f, Ñ\84акÑ\82иÑ\87еÑ\81кий заголовок `Content-Type`, статус-код и т.д. возьмутся из объекта `Response`, который вы вернули.
///
Например, это может быть что-то вроде:
-{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
+{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
В этом примере функция `generate_html_response()` уже генерирует и возвращает `Response` вместо возврата HTML в `str`.
FastAPI (фактически Starlette) автоматически добавит заголовок Content-Length. Также будет добавлен заголовок Content-Type, основанный на `media_type` и с добавлением charset для текстовых типов.
-{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
+{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Принимает текст или байты и возвращает ответ в виде простого текста.
-{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
///
-{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial001_py310.py hl[2,7] *}
/// tip | Совет
Вы можете вернуть `RedirectResponse` напрямую:
-{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
+{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
---
Или можно использовать его в параметре `response_class`:
-{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
Если вы сделаете так, то сможете возвращать URL напрямую из своей функции-обработчика пути.
Также вы можете использовать параметр `status_code` в сочетании с параметром `response_class`:
-{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Принимает асинхронный генератор или обычный генератор/итератор и отправляет тело ответа потоково.
-{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
+{* ../../docs_src/custom_response/tutorial007_py310.py hl[2,14] *}
#### Использование `StreamingResponse` с файлоподобными объектами { #using-streamingresponse-with-file-like-objects }
Это включает многие библиотеки для работы с облачным хранилищем, обработки видео и т.д.
-{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
+{* ../../docs_src/custom_response/tutorial008_py310.py hl[2,10:12,14] *}
1. Это функция-генератор. Она является «функцией-генератором», потому что содержит оператор(ы) `yield` внутри.
2. Используя блок `with`, мы гарантируем, что файлоподобный объект будет закрыт после завершения работы функции-генератора. То есть после того, как она закончит отправку ответа.
Файловые ответы будут содержать соответствующие заголовки `Content-Length`, `Last-Modified` и `ETag`.
-{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
+{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
Вы также можете использовать параметр `response_class`:
-{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
+{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
В этом случае вы можете возвращать путь к файлу напрямую из своей функции-обработчика пути.
Вы могли бы создать `CustomORJSONResponse`. Главное, что вам нужно сделать — реализовать метод `Response.render(content)`, который возвращает содержимое как `bytes`:
-{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
+{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
Теперь вместо того, чтобы возвращать:
В примере ниже **FastAPI** будет использовать `ORJSONResponse` по умолчанию во всех операциях пути вместо `JSONResponse`.
-{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
+{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
/// tip | Совет
6. Здесь мы возвращаем словарь, содержащий `items`, который является списком dataclass.
- FastAPI по-прежнему способен <abbr title="преобразование данных в формат, который можно передавать">сериализовать</abbr> данные в JSON.
+ FastAPI по-прежнему способен <dfn title="преобразование данных в формат, который можно передавать">сериализовать</dfn> данные в JSON.
7. Здесь `response_model` использует аннотацию типа — список dataclass `Author`.
Мы создаём асинхронную функцию `lifespan()` с `yield` примерно так:
-{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
+{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
Здесь мы симулируем дорогую операцию startup по загрузке модели, помещая (фиктивную) функцию модели в словарь с моделями Машинного обучения до `yield`. Этот код будет выполнен до того, как приложение начнет принимать запросы, во время startup.
Первое, на что стоит обратить внимание, — мы определяем асинхронную функцию с `yield`. Это очень похоже на Зависимости с `yield`.
-{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
+{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
Первая часть функции, до `yield`, будет выполнена до запуска приложения.
Это превращает функцию в «асинхронный менеджер контекста».
-{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
+{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
Менеджер контекста в Python — это то, что можно использовать в операторе `with`. Например, `open()` можно использовать как менеджер контекста:
Параметр `lifespan` приложения `FastAPI` принимает асинхронный менеджер контекста, поэтому мы можем передать ему наш новый асинхронный менеджер контекста `lifespan`.
-{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
+{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
## Альтернативные события (устаревшие) { #alternative-events-deprecated }
Чтобы добавить функцию, которую нужно запустить до старта приложения, объявите её как обработчик события `"startup"`:
-{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
+{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
В этом случае функция-обработчик события `startup` инициализирует «базу данных» items (это просто `dict`) некоторыми значениями.
Чтобы добавить функцию, которую нужно запустить при завершении работы приложения, объявите её как обработчик события `"shutdown"`:
-{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
+{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
Здесь функция-обработчик события `shutdown` запишет строку текста `"Application shutdown"` в файл `log.txt`.
Поскольку **FastAPI** основан на спецификации **OpenAPI**, его API можно описать в стандартном формате, понятном множеству инструментов.
-Это упрощает генерацию актуальной **документации**, клиентских библиотек (<abbr title="Software Development Kits – Наборы средств разработки">**SDKs**</abbr>) на разных языках, а также **тестирования** или **воркфлоу автоматизации**, которые остаются синхронизированными с вашим кодом.
+Это упрощает генерацию актуальной **документации**, клиентских библиотек (<abbr title="Software Development Kits - Наборы средств разработки">**SDKs**</abbr>) на разных языках, а также **тестирования** или **воркфлоу автоматизации**, которые остаются синхронизированными с вашим кодом.
В этом руководстве вы узнаете, как сгенерировать **TypeScript SDK** для вашего бэкенда на FastAPI.
Начнём с простого приложения FastAPI:
-{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
+{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
Обратите внимание, что *операции пути (обработчики пути)* определяют модели, которые они используют для полезной нагрузки запроса и полезной нагрузки ответа, с помощью моделей `Item` и `ResponseMessage`.
Например, у вас может быть раздел для **items** и другой раздел для **users**, и они могут быть разделены тегами:
-{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
+{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
### Генерация TypeScript‑клиента с тегами { #generate-a-typescript-client-with-tags }
Затем вы можете передать эту пользовательскую функцию в **FastAPI** через параметр `generate_unique_id_function`:
-{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
+{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
### Генерация TypeScript‑клиента с пользовательскими ID операций { #generate-a-typescript-client-with-custom-operation-ids }
### Предобработка спецификации OpenAPI для генератора клиента { #preprocess-the-openapi-specification-for-the-client-generator }
-Сгенерированном коде всё ещё есть **дублирующаяся информация**.
+Ð\92 Ñ\81генерированном коде всё ещё есть **дублирующаяся информация**.
Мы уже знаем, что этот метод относится к **items**, потому что это слово есть в `ItemsService` (взято из тега), но при этом имя тега всё ещё добавлено префиксом к имени метода. 😕
Мы можем скачать OpenAPI JSON в файл `openapi.json`, а затем **убрать этот префикс‑тег** таким скриптом:
-{* ../../docs_src/generate_clients/tutorial004_py39.py *}
+{* ../../docs_src/generate_clients/tutorial004_py310.py *}
//// tab | Node.js
Любой входящий запрос по `http` или `ws` будет перенаправлен на безопасную схему.
-{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
+{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Гарантирует, что во всех входящих запросах корректно установлен `Host`‑заголовок, чтобы защититься от атак на HTTP‑заголовок Host.
-{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
+{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
Поддерживаются следующие аргументы:
Это middleware обрабатывает как обычные, так и потоковые ответы.
-{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
+{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
Поддерживаются следующие аргументы:
Вся логика регистрации URL-адресов для вебхуков и код, который реально отправляет эти запросы, целиком на вашей стороне. Вы пишете это так, как вам нужно, в своем собственном коде.
-## Документирование вебхуков с помощью FastAPI и OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
+## Документирование вебхуков с помощью **FastAPI** и OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
-С FastAPI, используя OpenAPI, вы можете определить имена этих вебхуков, типы HTTP-операций, которые ваше приложение может отправлять (например, `POST`, `PUT` и т.д.), а также тела запросов, которые ваше приложение будет отправлять.
+С **FastAPI**, используя OpenAPI, вы можете определить имена этих вебхуков, типы HTTP-операций, которые ваше приложение может отправлять (например, `POST`, `PUT` и т.д.), а также тела запросов, которые ваше приложение будет отправлять.
Это значительно упростит вашим пользователям реализацию их API для приема ваших вебхук-запросов; возможно, они даже смогут автоматически сгенерировать часть кода своего API.
При создании приложения на **FastAPI** есть атрибут `webhooks`, с помощью которого можно объявлять вебхуки так же, как вы объявляете операции пути (обработчики пути), например с `@app.webhooks.post()`.
-{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *}
+{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:12,15:20] *}
Определенные вами вебхуки попадут в схему **OpenAPI** и в автоматический **интерфейс документации**.
Нужно убедиться, что он уникален для каждой операции.
-{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
### Использование имени *функции-обработчика пути* как operationId { #using-the-path-operation-function-name-as-the-operationid }
Делать это следует после добавления всех *операций пути*.
-{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
/// tip | Совет
Чтобы исключить *операцию пути* из генерируемой схемы OpenAPI (а значит, и из автоматических систем документации), используйте параметр `include_in_schema` и установите его в `False`:
-{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
## Расширенное описание из docstring { #advanced-description-from-docstring }
`openapi_extra` может пригодиться, например, чтобы объявить [Расширения OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
-{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
Если вы откроете автоматическую документацию API, ваше расширение появится внизу страницы конкретной *операции пути*.
Это можно сделать с помощью `openapi_extra`:
-{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
-В этом примере мы не объявляли никакую Pydantic-модель. Фактически тело запроса даже не <abbr title="преобразовано из простого формата, например байтов, в объекты Python">распарсено</abbr> как JSON, оно читается напрямую как `bytes`, а функция `magic_data_reader()` будет отвечать за его парсинг каким-то способом.
+В этом примере мы не объявляли никакую Pydantic-модель. Фактически тело запроса даже не <dfn title="преобразован из простого формата, например байтов, в объекты Python">распарсено</dfn> как JSON, оно читается напрямую как `bytes`, а функция `magic_data_reader()` будет отвечать за его парсинг каким-то способом.
Тем не менее, мы можем объявить ожидаемую схему для тела запроса.
Например, в этом приложении мы не используем встроенную функциональность FastAPI для извлечения JSON Schema из моделей Pydantic, равно как и автоматическую валидацию JSON. Мы объявляем тип содержимого HTTP-запроса как YAML, а не JSON:
-{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
Тем не менее, хотя мы не используем встроенную функциональность по умолчанию, мы всё равно используем Pydantic-модель, чтобы вручную сгенерировать JSON Schema для данных, которые мы хотим получить в YAML.
А затем в нашем коде мы напрямую парсим это содержимое YAML и снова используем ту же Pydantic-модель, чтобы валидировать YAML-содержимое:
-{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
/// tip | Совет
И затем вы можете установить `status_code` в этом *временном* объекте ответа.
-{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
+{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
После этого вы можете вернуть любой объект, который вам нужен, как обычно (`dict`, модель базы данных и т.д.).
Затем установить cookies в этом временном объекте ответа.
-{* ../../docs_src/response_cookies/tutorial002_py39.py hl[1, 8:9] *}
+{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
После этого можно вернуть любой объект, как и раньше (например, `dict`, объект модели базы данных и так далее).
Затем установите cookies и верните этот объект:
-{* ../../docs_src/response_cookies/tutorial001_py39.py hl[10:12] *}
+{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
/// tip | Совет
Вы можете поместить ваш XML-контент в строку, поместить её в `Response` и вернуть:
-{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
+{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
## Примечания { #notes }
А затем вы можете устанавливать HTTP-заголовки в этом *временном* объекте ответа.
-{* ../../docs_src/response_headers/tutorial002_py39.py hl[1, 7:8] *}
+{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
После этого вы можете вернуть любой нужный объект, как обычно (например, `dict`, модель из базы данных и т.д.).
Создайте ответ, как описано в [Вернуть Response напрямую](response-directly.md){.internal-link target=_blank}, и передайте заголовки как дополнительный параметр:
-{* ../../docs_src/response_headers/tutorial001_py39.py hl[10:12] *}
+{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
/// note | Технические детали
* Она возвращает объект типа `HTTPBasicCredentials`:
* Он содержит отправленные `username` и `password`.
-{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
+{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
Когда вы впервые откроете URL (или нажмёте кнопку «Execute» в документации), браузер попросит ввести имя пользователя и пароль:
Затем можно использовать `secrets.compare_digest()`, чтобы убедиться, что `credentials.username` равен `"stanleyjobson"`, а `credentials.password` — `"swordfish"`.
-{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *}
+{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
Это было бы похоже на:
После того как обнаружено, что учётные данные некорректны, верните `HTTPException` со статус-кодом ответа 401 (тем же, что и при отсутствии учётных данных) и добавьте HTTP-заголовок `WWW-Authenticate`, чтобы браузер снова показал окно входа:
-{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}
+{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}
Вы можете использовать все те же возможности валидации и инструменты, что и для Pydantic‑моделей, например разные типы данных и дополнительную валидацию через `Field()`.
-{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
+{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
/// tip | Совет
Затем вы можете использовать новый объект `settings` в вашем приложении:
-{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *}
+{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
### Запуск сервера { #run-the-server }
Например, у вас может быть файл `config.py` со следующим содержимым:
-{* ../../docs_src/settings/app01_py39/config.py *}
+{* ../../docs_src/settings/app01_py310/config.py *}
А затем использовать его в файле `main.py`:
-{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *}
+{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
/// tip | Совет
Продолжая предыдущий пример, ваш файл `config.py` может выглядеть так:
-{* ../../docs_src/settings/app02_an_py39/config.py hl[10] *}
+{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
Обратите внимание, что теперь мы не создаем экземпляр по умолчанию `settings = Settings()`.
Теперь мы создаем зависимость, которая возвращает новый `config.Settings()`.
-{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *}
+{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
/// tip | Совет
Затем мы можем запросить ее в *функции-обработчике пути* как зависимость и использовать там, где нужно.
-{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *}
+{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
### Настройки и тестирование { #settings-and-testing }
Далее будет очень просто предоставить другой объект настроек во время тестирования, создав переопределение зависимости для `get_settings`:
-{* ../../docs_src/settings/app02_an_py39/test_main.py hl[9:10,13,21] *}
+{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
В переопределении зависимости мы задаем новое значение `admin_email` при создании нового объекта `Settings`, а затем возвращаем этот новый объект.
Затем обновите ваш `config.py` так:
-{* ../../docs_src/settings/app03_an_py39/config.py hl[9] *}
+{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
/// tip | Совет
Но так как мы используем декоратор `@lru_cache` сверху, объект `Settings` будет создан только один раз — при первом вызове. ✔️
-{* ../../docs_src/settings/app03_an_py39/main.py hl[1,11] *}
+{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
Затем при любых последующих вызовах `get_settings()` в зависимостях для следующих запросов, вместо выполнения внутреннего кода `get_settings()` и создания нового объекта `Settings`, будет возвращаться тот же объект, что был возвращен при первом вызове, снова и снова.
Сначала создайте основное, верхнего уровня, приложение **FastAPI** и его *операции пути*:
-{* ../../docs_src/sub_applications/tutorial001_py39.py hl[3, 6:8] *}
+{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
### Подприложение { #sub-application }
Это подприложение — обычное стандартное приложение FastAPI, но именно оно будет «смонтировано»:
-{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 14:16] *}
+{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
### Смонтируйте подприложение { #mount-the-sub-application }
В этом случае оно будет смонтировано по пути `/subapi`:
-{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 19] *}
+{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
### Проверьте автоматическую документацию API { #check-the-automatic-api-docs }
- Объявите параметр `Request` в *операции пути*, которая будет возвращать шаблон.
- Используйте созданный `templates`, чтобы отрендерить и вернуть `TemplateResponse`; передайте имя шаблона, объект `request` и словарь «context» с парами ключ-значение для использования внутри шаблона Jinja2.
-{* ../../docs_src/templates/tutorial001_py39.py hl[4,11,15:18] *}
+{* ../../docs_src/templates/tutorial001_py310.py hl[4,11,15:18] *}
/// note | Примечание
Если вам нужно, чтобы `lifespan` выполнялся в ваших тестах, вы можете использовать `TestClient` вместе с оператором `with`:
-{* ../../docs_src/app_testing/tutorial004_py39.py hl[9:15,18,27:28,30:32,41:43] *}
+{* ../../docs_src/app_testing/tutorial004_py310.py hl[9:15,18,27:28,30:32,41:43] *}
Вы можете узнать больше подробностей в статье [Запуск lifespan в тестах на официальном сайте документации Starlette.](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)
Для устаревших событий `startup` и `shutdown` вы можете использовать `TestClient` следующим образом:
-{* ../../docs_src/app_testing/tutorial003_py39.py hl[9:12,20:24] *}
+{* ../../docs_src/app_testing/tutorial003_py310.py hl[9:12,20:24] *}
Для этого используйте `TestClient` с менеджером контекста `with`, подключаясь к WebSocket:
-{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *}
+{* ../../docs_src/app_testing/tutorial002_py310.py hl[27:31] *}
/// note | Примечание
Для этого нужно обратиться к запросу напрямую.
-{* ../../docs_src/using_request_directly/tutorial001_py39.py hl[1,7:8] *}
+{* ../../docs_src/using_request_directly/tutorial001_py310.py hl[1,7:8] *}
Если объявить параметр *функции-обработчика пути* с типом `Request`, **FastAPI** поймёт, что нужно передать объект `Request` в этот параметр.
Для примера нам нужен наиболее простой способ, который позволит сосредоточиться на серверной части веб‑сокетов и получить рабочий код:
-{* ../../docs_src/websockets/tutorial001_py39.py hl[2,6:38,41:43] *}
+{* ../../docs_src/websockets/tutorial001_py310.py hl[2,6:38,41:43] *}
## Создание `websocket` { #create-a-websocket }
Создайте `websocket` в своем **FastAPI** приложении:
-{* ../../docs_src/websockets/tutorial001_py39.py hl[1,46:47] *}
+{* ../../docs_src/websockets/tutorial001_py310.py hl[1,46:47] *}
/// note | Технические детали
Через эндпоинт веб-сокета вы можете получать и отправлять сообщения.
-{* ../../docs_src/websockets/tutorial001_py39.py hl[48:52] *}
+{* ../../docs_src/websockets/tutorial001_py310.py hl[48:52] *}
Вы можете получать и отправлять двоичные, текстовые и JSON данные.
Если веб-сокет соединение закрыто, то `await websocket.receive_text()` вызовет исключение `WebSocketDisconnect`, которое можно поймать и обработать как в этом примере:
-{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *}
+{* ../../docs_src/websockets/tutorial003_py310.py hl[79:81] *}
Чтобы воспроизвести пример:
После этого смонтируйте его на путь.
-{* ../../docs_src/wsgi/tutorial001_py39.py hl[1,3,23] *}
+{* ../../docs_src/wsgi/tutorial001_py310.py hl[1,3,23] *}
/// note | Примечание
### <a href="https://marshmallow.readthedocs.io/en/stable/" class="external-link" target="_blank">Marshmallow</a> { #marshmallow }
-Одна из основных возможностей, нужных системам API, — «<abbr title="также называемая маршаллингом или преобразованием">сериализация</abbr>» данных, то есть преобразование данных из кода (Python) во что-то, что можно отправить по сети. Например, преобразование объекта с данными из базы в JSON-объект. Преобразование объектов `datetime` в строки и т. п.
+Одна из основных возможностей, нужных системам API, — «<dfn title="также называемая маршаллингом, преобразованием">сериализация</dfn>» данных, то есть преобразование данных из кода (Python) во что-то, что можно отправить по сети. Например, преобразование объекта с данными из базы в JSON-объект. Преобразование объектов `datetime` в строки и т. п.
Ещё одна важная возможность, востребованная API, — валидация данных: убеждаться, что данные валидны с учётом заданных параметров. Например, что какое-то поле — `int`, а не произвольная строка. Это особенно полезно для входящих данных.
Именно для этих возможностей и был создан Marshmallow. Это отличная библиотека, я много ей пользовался раньше.
-Но она появилась до того, как в Python появились аннотации типов. Поэтому для определения каждой <abbr title="описание того, как данные должны быть сформированы">схемы</abbr> нужно использовать специальные утилиты и классы, предоставляемые Marshmallow.
+Но она появилась до того, как в Python появились аннотации типов. Поэтому для определения каждой <dfn title="определение того, как должны быть сформированы данные">схемы</dfn> нужно использовать специальные утилиты и классы, предоставляемые Marshmallow.
/// check | Вдохновило **FastAPI** на
### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a> { #webargs }
-Ещё одна важная возможность для API — <abbr title="чтение и преобразование данных в объекты Python">парсинг</abbr> данных из входящих HTTP-запросов.
+Ещё одна важная возможность для API — <dfn title="чтение и преобразование в данные Python">парсинг</dfn> данных из входящих HTTP-запросов.
Webargs — это инструмент, созданный для этого поверх нескольких фреймворков, включая Flask.
В нём встроена система внедрения зависимостей, вдохновлённая Angular 2. Требуется предварительная регистрация «инжектируемых» компонентов (как и во всех известных мне системах внедрения зависимостей), что добавляет многословности и повторяемости кода.
-Поскольку параметры описываются с помощью типов TypeScript (аналог аннотаций типов в Python), поддержка редактора весьма хороша.
+Ð\9fоÑ\81колÑ\8cкÑ\83 паÑ\80амеÑ\82Ñ\80Ñ\8b опиÑ\81Ñ\8bваÑ\8eÑ\82Ñ\81Ñ\8f Ñ\81 помоÑ\89Ñ\8cÑ\8e Ñ\82ипов TypeScript (аналог анноÑ\82аÑ\86ий Ñ\82ипов в Python), поддеÑ\80жка Ñ\80едакÑ\82оÑ\80а кода веÑ\81Ñ\8cма Ñ\85оÑ\80оÑ\88а.
Но так как данные о типах TypeScript не сохраняются после компиляции в JavaScript, он не может полагаться на типы для одновременного определения валидации, сериализации и документации. Из‑за этого и некоторых проектных решений для получения валидации, сериализации и автоматической генерации схем приходится добавлять декораторы во многих местах. В итоге это становится довольно многословным.
В нём были автоматические валидация данных, сериализация данных и генерация схемы OpenAPI на основе тех же аннотаций типов в нескольких местах.
-Определение схемы тела запроса не использовало те же аннотации типов Python, как в Pydantic, — это было ближе к Marshmallow, поэтому поддержка редактора была бы хуже, но всё равно APIStar оставался лучшим доступным вариантом.
+Ð\9eпÑ\80еделение Ñ\81Ñ\85емÑ\8b Ñ\82ела запÑ\80оÑ\81а не иÑ\81полÑ\8cзовало Ñ\82е же анноÑ\82аÑ\86ии Ñ\82ипов Python, как в Pydantic, â\80\94 Ñ\8dÑ\82о бÑ\8bло ближе к Marshmallow, поÑ\8dÑ\82омÑ\83 поддеÑ\80жка Ñ\80едакÑ\82оÑ\80а кода бÑ\8bла бÑ\8b Ñ\85Ñ\83же, но вÑ\81Ñ\91 Ñ\80авно APIStar оÑ\81Ñ\82авалÑ\81Ñ\8f лÑ\83Ñ\87Ñ\88им доÑ\81Ñ\82Ñ\83пнÑ\8bм ваÑ\80ианÑ\82ом.
На тот момент у него были лучшие показатели в бенчмарках (его превосходил только Starlette).
### <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> { #starlette }
-Starlette — это лёгкий <abbr title="Новый стандарт построения асинхронных веб-сервисов Python">ASGI</abbr> фреймворк/набор инструментов, идеально подходящий для создания высокопроизводительных asyncio‑сервисов.
+Starlette — это лёгкий <dfn title="Новый стандарт построения асинхронных веб-приложений на Python">ASGI</dfn> фреймворк/набор инструментов, идеально подходящий для создания высокопроизводительных asyncio‑сервисов.
Он очень простой и интуитивный. Спроектирован так, чтобы его было легко расширять, и чтобы компоненты были модульными.
## Нет времени? { #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`, например:
Асинхронный код значит, что в языке 💬 есть способ сказать компьютеру/программе 🤖, что в некоторый момент кода ему 🤖 придётся подождать, пока *что-то ещё* где-то в другом месте завершится. Назовём это *что-то ещё* «медленный файл» 📝.
-Ð\98 пока мÑ\8b ждÑ\91м завеÑ\80Ñ\88ениÑ\8f Ñ\80абоÑ\82Ñ\8b Ñ\81 «медленнÑ\8bи файлом» 📝, компьютер может заняться другой работой.
+Ð\98 пока мÑ\8b ждÑ\91м завеÑ\80Ñ\88ениÑ\8f Ñ\80абоÑ\82Ñ\8b Ñ\81 «медленнÑ\8bм файлом» 📝, компьютер может заняться другой работой.
Затем компьютер/программа 🤖 будет возвращаться каждый раз, когда появится возможность (пока снова где-то идёт ожидание), или когда 🤖 завершит всю текущую работу. И он 🤖 проверит, не завершилась ли какая-либо из задач, которых он ждал, и сделает то, что нужно.
Далее он 🤖 возьмёт первую завершившуюся задачу (скажем, наш «медленный файл» 📝) и продолжит делать с ней то, что требуется.
-Это «ожидание чего-то ещё» обычно относится к операциям <abbr title="Input and Output – Ввод/вывод">I/O</abbr>, которые относительно «медленные» (по сравнению со скоростью процессора и оперативной памяти), например ожидание:
+Это «ожидание чего-то ещё» обычно относится к операциям <abbr title="Input and Output - Ввод/вывод">I/O</abbr>, которые относительно «медленные» (по сравнению со скоростью процессора и оперативной памяти), например ожидание:
* отправки данных клиентом по сети
* получения клиентом данных, отправленных вашей программой по сети
* возврата результатов запроса к базе данных
* и т.д.
-Поскольку основное время выполнения уходит на ожидание операций <abbr title="Input and Output – Ввод/вывод">I/O</abbr>, их называют операциями, «ограниченными вводом-выводом» (I/O bound).
+Поскольку основное время выполнения уходит на ожидание операций <abbr title="Input and Output - Ввод/вывод">I/O</abbr>, их называют операциями, «ограниченными вводом-выводом» (I/O bound).
Это называется «асинхронным», потому что компьютеру/программе не нужно «синхронизироваться» с медленной задачей, простаивая и выжидая точный момент её завершения, чтобы забрать результат и продолжить работу.
В этом сценарии каждый уборщик (включая вас) был бы процессором, выполняющим свою часть работы.
-И так как основное время выполнения уходит на реальную работу (а не ожидание), а работу в компьютере выполняет <abbr title="Central Processing Unit – Центральный процессор">CPU</abbr>, такие задачи называют «ограниченными процессором» (CPU bound).
+И так как основное время выполнения уходит на реальную работу (а не ожидание), а работу в компьютере выполняет <abbr title="Central Processing Unit - Центральный процессор">CPU</abbr>, такие задачи называют «ограниченными процессором» (CPU bound).
---
Когда вы объявляете *функцию-обработчик пути* обычным `def` вместо `async def`, она запускается во внешнем пуле потоков, который затем «ожидается», вместо прямого вызова (прямой вызов заблокировал бы сервер).
-Если вы пришли из другого async-фреймворка, который работает иначе, и привыкли объявлять тривиальные *функции-обработчики пути*, выполняющие только вычисления, через простой `def` ради крошечной выгоды в производительности (около 100 наносекунд), обратите внимание: в **FastAPI** эффект будет противоположным. В таких случаях лучше использовать `async def`, если только ваши *функции-обработчики пути* не используют код, выполняющий блокирующий <abbr title="Input/Output – Ввод/вывод: чтение или запись на диск, сетевые соединения.">I/O</abbr>.
+Если вы пришли из другого async-фреймворка, который работает иначе, и привыкли объявлять тривиальные *функции-обработчики пути*, выполняющие только вычисления, через простой `def` ради крошечной выгоды в производительности (около 100 наносекунд), обратите внимание: в **FastAPI** эффект будет противоположным. В таких случаях лучше использовать `async def`, если только ваши *функции-обработчики пути* не используют код, выполняющий блокирующий <abbr title="Input/Output - Ввод/вывод: чтение или запись на диск, сетевые соединения.">I/O</abbr>.
Тем не менее, в обоих случаях велика вероятность, что **FastAPI** [всё равно будет быстрее](index.md#performance){.internal-link target=_blank} (или как минимум сопоставим) с вашим предыдущим фреймворком.
* **Uvicorn**: ASGI-сервер
* **Starlette**: (использует Uvicorn) веб-микрофреймворк
- * **FastAPI**: (использует Starlette) API-микрофреймворк с рядом дополнительных возможностей для создания API, включая валидацию данных и т. п.
+ * **FastAPI**: (использует Starlette) API-микрофреймворк с рядом дополнительных возможностей для создания API, включая валидацию данных и т.п.
* **Uvicorn**:
* Будет иметь наилучшую производительность, так как помимо самого сервера у него немного дополнительного кода.
* Вы не будете писать приложение непосредственно на Uvicorn. Это означало бы, что Ваш код должен включать как минимум весь код, предоставляемый Starlette (или **FastAPI**). И если Вы так сделаете, то в конечном итоге Ваше приложение будет иметь те же накладные расходы, что и при использовании фреймворка, минимизирующего код Вашего приложения и Ваши ошибки.
- * Если Вы сравниваете Uvicorn, сравнивайте его с Daphne, Hypercorn, uWSGI и т. д. — серверами приложений.
+ * Если Вы сравниваете Uvicorn, сравнивайте его с Daphne, Hypercorn, uWSGI и т.д. — серверами приложений.
* **Starlette**:
* Будет на следующем месте по производительности после Uvicorn. Фактически Starlette запускается под управлением Uvicorn, поэтому он может быть только «медленнее» Uvicorn из‑за выполнения большего объёма кода.
- * Зато он предоставляет Вам инструменты для создания простых веб‑приложений с маршрутизацией по путям и т. п.
- * Если Вы сравниваете Starlette, сравнивайте его с Sanic, Flask, Django и т. д. — веб‑фреймворками (или микрофреймворками).
+ * Зато он предоставляет Вам инструменты для создания простых веб‑приложений с маршрутизацией по путям и т.п.
+ * Если Вы сравниваете Starlette, сравнивайте его с Sanic, Flask, Django и т.д. — веб‑фреймворками (или микрофреймворками).
* **FastAPI**:
* Точно так же, как Starlette использует Uvicorn и не может быть быстрее него, **FastAPI** использует Starlette, поэтому не может быть быстрее его.
* FastAPI предоставляет больше возможностей поверх Starlette — те, которые почти всегда нужны при создании API, такие как валидация и сериализация данных. В довесок Вы ещё и получаете автоматическую документацию (автоматическая документация даже не увеличивает накладные расходы при работе приложения, так как она создаётся при запуске).
- * Если бы Вы не использовали FastAPI, а использовали Starlette напрямую (или другой инструмент вроде Sanic, Flask, Responder и т. д.), Вам пришлось бы самостоятельно реализовать валидацию и сериализацию данных. То есть, в итоге, Ваше приложение имело бы такие же накладные расходы, как если бы оно было создано с использованием FastAPI. И во многих случаях валидация и сериализация данных представляют собой самый большой объём кода, написанного в приложениях.
+ * Если бы Вы не использовали FastAPI, а использовали Starlette напрямую (или другой инструмент вроде Sanic, Flask, Responder и т.д.), Вам пришлось бы самостоятельно реализовать валидацию и сериализацию данных. То есть, в итоге, Ваше приложение имело бы такие же накладные расходы, как если бы оно было создано с использованием FastAPI. И во многих случаях валидация и сериализация данных представляют собой самый большой объём кода, написанного в приложениях.
* Таким образом, используя FastAPI, Вы экономите время разработки, уменьшаете количество ошибок, строк кода и, вероятно, получите ту же производительность (или лучше), как и если бы не использовали его (поскольку Вам пришлось бы реализовать все его возможности в своём коде).
* Если Вы сравниваете FastAPI, сравнивайте его с фреймворком веб‑приложений (или набором инструментов), который обеспечивает валидацию данных, сериализацию и документацию, такими как Flask-apispec, NestJS, Molten и им подобные. Фреймворки с интегрированной автоматической валидацией данных, сериализацией и документацией.
В [предыдущей главе про HTTPS](https.md){.internal-link target=_blank} мы разобрались, как HTTPS обеспечивает шифрование для вашего API.
-Также мы увидели, что HTTPS обычно обеспечивает компонент, **внешний** по отношению к серверу вашего приложения — **TLS Termination Proxy**.
+Также мы увидели, что HTTPS обычно обеспечивает компонент, **внешний** по отношению к серверу вашего приложения — **прокси-сервер TSL-терминации**.
И должен быть компонент, отвечающий за **обновление HTTPS‑сертификатов** — это может быть тот же самый компонент или отдельный.
### Примеры инструментов для HTTPS { #example-tools-for-https }
-Некоторые инструменты, которые можно использовать как TLS Termination Proxy:
+Некоторые инструменты, которые можно использовать как прокси-сервер TSL-терминации:
* Traefik
* Автоматически обновляет сертификаты ✨
* С внешним компонентом (например, cert-manager) для обновления сертификатов
* Обрабатывается внутри облачного провайдера как часть его услуг (см. ниже 👇)
-Другой вариант — использовать **облачный сервис**, который возьмёт на себя больше задач, включая настройку HTTPS. Там могут быть ограничения или дополнительная стоимость и т.п., но в таком случае вам не придётся самим настраивать TLS Termination Proxy.
+Другой вариант — использовать **облачный сервис**, который возьмёт на себя больше задач, включая настройку HTTPS. Там могут быть ограничения или дополнительная стоимость и т.п., но в таком случае вам не придётся самим настраивать прокси-сервер TSL-терминации.
В следующих главах я покажу конкретные примеры.
Процесс‑менеджер, вероятно, будет тем, кто слушает **порт** на IP. И он будет передавать всю коммуникацию воркер‑процессам.
-Эти воркеры будут запускать ваше приложение, выполнять основные вычисления для получения **запроса** и возврата **ответа**, и загружать всё, что вы кладёте в переменные, в RAM.
+Эти воркеры будут запускать ваше приложение, выполнять основные вычисления для получения **HTTP‑запроса** и возврата **HTTP‑ответа**, и загружать всё, что вы кладёте в переменные, в RAM.
<img src="/img/deployment/concepts/process-ram.drawio.svg">
Ваш сервер(а) — это **ресурс**, который ваши программы могут потреблять или **использовать**: время вычислений на CPU и доступную оперативную память (RAM).
-Какую долю системных ресурсов вы хотите потреблять/использовать? Можно подумать «немного», но на практике вы, скорее всего, захотите потреблять **максимум без падений**.
+Какую долю системных ресурсов вы хотите потреблять/использовать? Можно подумать «немного», но на практике вы, вероятно, захотите потреблять **максимум без падений**.
Если вы платите за 3 сервера, но используете лишь малую часть их RAM и CPU, вы, вероятно, **тратите деньги впустую** 💸 и **электроэнергию серверов** 🌎 и т.п.
<summary>Предпросмотр Dockerfile 👀</summary>
```Dockerfile
-FROM python:3.9
+FROM python:3.14
WORKDIR /code
```{ .dockerfile .annotate }
# (1)!
-FROM python:3.9
+FROM python:3.14
# (2)!
WORKDIR /code
└── requirements.txt
```
-#### За прокси-сервером TLS терминации { #behind-a-tls-termination-proxy }
+#### За прокси-сервером TSL-терминации { #behind-a-tls-termination-proxy }
-Если вы запускаете контейнер за прокси-сервером завершения TLS (балансировщиком нагрузки), таким как Nginx или Traefik, добавьте опцию `--proxy-headers`. Это сообщит Uvicorn (через FastAPI CLI), что приложение работает за HTTPS и можно доверять соответствующим заголовкам.
+Если вы запускаете контейнер за прокси-сервером TSL-терминации (балансировщиком нагрузки), таким как Nginx или Traefik, добавьте опцию `--proxy-headers`. Это сообщит Uvicorn (через FastAPI CLI), что приложение работает за HTTPS и можно доверять соответствующим заголовкам.
```Dockerfile
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
Тогда в `Dockerfile` нужно изменить пути копирования:
```{ .dockerfile .annotate hl_lines="10 13" }
-FROM python:3.9
+FROM python:3.14
WORKDIR /code
## Репликация — количество процессов { #replication-number-of-processes }
-Если у вас есть <abbr title="Группа машин, настроенных так, чтобы быть соединенными и работать вместе определенным образом.">кластер</abbr> машин с **Kubernetes**, Docker Swarm Mode, Nomad или другой похожей системой для управления распределёнными контейнерами на нескольких машинах, скорее всего вы будете **управлять репликацией** на **уровне кластера**, а не использовать **менеджер процессов** (например, Uvicorn с воркерами) в каждом контейнере.
+Если у вас есть <dfn title="Группа машин, настроенных так, чтобы быть соединенными и работать вместе определенным образом.">кластер</dfn> машин с **Kubernetes**, Docker Swarm Mode, Nomad или другой похожей системой для управления распределёнными контейнерами на нескольких машинах, скорее всего вы будете **управлять репликацией** на **уровне кластера**, а не использовать **менеджер процессов** (например, Uvicorn с воркерами) в каждом контейнере.
Одна из таких систем управления распределёнными контейнерами, как Kubernetes, обычно имеет встроенный способ управлять **репликацией контейнеров**, поддерживая **балансировку нагрузки** для входящих запросов — всё это на **уровне кластера**.
### Балансировщик нагрузки { #load-balancer }
-При использовании контейнеров обычно есть компонент, **слушающий главный порт**. Это может быть другой контейнер — **прокси завершения TLS** для обработки **HTTPS** или похожий инструмент.
+При использовании контейнеров обычно есть компонент, **слушающий главный порт**. Это может быть другой контейнер — **прокси-сервер TSL-терминации** для обработки **HTTPS** или похожий инструмент.
Поскольку этот компонент принимает **нагрузку** запросов и распределяет её между воркерами **сбалансированно**, его часто называют **балансировщиком нагрузки**.
/// tip | Подсказка
-Тот же компонент **прокси завершения TLS**, который обрабатывает HTTPS, скорее всего также будет **балансировщиком нагрузки**.
+Тот же компонент **прокси-сервер TSL-терминации**, который обрабатывает HTTPS, скорее всего также будет **балансировщиком нагрузки**.
///
-При работе с контейнерами система, которую вы используете для запуска и управления ими, уже имеет внутренние средства для передачи **сетевого взаимодействия** (например, HTTP-запросов) от **балансировщика нагрузки** (который также может быть **прокси завершения TLS**) к контейнеру(-ам) с вашим приложением.
+При работе с контейнерами система, которую вы используете для запуска и управления ими, уже имеет внутренние средства для передачи **сетевого взаимодействия** (например, HTTP-запросов) от **балансировщика нагрузки** (который также может быть **прокси-сервером TSL-терминации**) к контейнеру(-ам) с вашим приложением.
### Один балансировщик — несколько контейнеров-воркеров { #one-load-balancer-multiple-worker-containers }
В таких случаях вы можете использовать опцию командной строки `--workers`, чтобы указать нужное количество воркеров:
```{ .dockerfile .annotate }
-FROM python:3.9
+FROM python:3.14
WORKDIR /code
Чаще всего всё начинается с **приобретения** **имени домена**. Затем вы настраиваете его на DNS‑сервере (возможно, у того же облачного провайдера).
-Скорее всего, вы получите облачный сервер (виртуальную машину) или что-то подобное, и у него будет <abbr title="Не изменяется">постоянный</abbr> **публичный IP-адрес**.
+Скорее всего, вы получите облачный сервер (виртуальную машину) или что-то подобное, и у него будет <dfn title="Со временем не меняется. Не динамический.">постоянный</dfn> **публичный IP-адрес**.
На DNS‑сервере(ах) вы настроите запись («`A record`» - запись типа A), указывающую, что **ваш домен** должен указывать на публичный **IP‑адрес вашего сервера**.
Например, мы, команда, стоящая за FastAPI, создали <a href="https://fastapicloud.com" class="external-link" target="_blank">**FastAPI Cloud**</a>, чтобы сделать развёртывание приложений FastAPI в облаке как можно более простым и прямолинейным, с тем же удобством для разработчика, что и при работе с FastAPI.
-Ð\92 Ñ\8dÑ\82ом блоке Ñ\8f покажу вам некоторые из основных концепций, которые вы, вероятно, должны иметь в виду при развертывании приложения **FastAPI** (хотя большинство из них применимо к любому другому типу веб-приложений).
+Я покажу вам некоторые из основных концепций, которые вы, вероятно, должны иметь в виду при развертывании приложения **FastAPI** (хотя большинство из них применимо к любому другому типу веб-приложений).
В последующих разделах вы узнаете больше деталей и методов, необходимых для этого. ✨
# О версиях FastAPI { #about-fastapi-versions }
-**FastAPI** Ñ\83же иÑ\81полÑ\8cзÑ\83еÑ\82Ñ\81Ñ\8f в пÑ\80одакÑ\88ене во многих приложениях и системах. Покрытие тестами поддерживается на уровне 100%. Но его разработка всё ещё движется быстрыми темпами.
+**FastAPI** Ñ\83же иÑ\81полÑ\8cзÑ\83еÑ\82Ñ\81Ñ\8f в пÑ\80одакÑ\88н во многих приложениях и системах. Покрытие тестами поддерживается на уровне 100%. Но его разработка всё ещё движется быстрыми темпами.
Часто добавляются новые функции, регулярно исправляются баги, код продолжает постоянно совершенствоваться.
По указанным причинам текущие версии до сих пор `0.x.x`. Это говорит о том, что каждая версия может содержать обратно несовместимые изменения, следуя <a href="https://semver.org/" class="external-link" target="_blank">Семантическому версионированию</a>.
-Уже Ñ\81ейÑ\87аÑ\81 вÑ\8b можеÑ\82е Ñ\81оздаваÑ\82Ñ\8c пÑ\80иложениÑ\8f в пÑ\80одакÑ\88ене, используя **FastAPI** (и скорее всего так и делаете), главное убедиться в том, что вы используете версию, которая корректно работает с вашим кодом.
+Уже Ñ\81ейÑ\87аÑ\81 вÑ\8b можеÑ\82е Ñ\81оздаваÑ\82Ñ\8c пÑ\80иложениÑ\8f в пÑ\80одакÑ\88н, используя **FastAPI** (и скорее всего так и делаете), главное убедиться в том, что вы используете версию, которая корректно работает с вашим кодом.
## Закрепите вашу версию `fastapi` { #pin-your-fastapi-version }
Эти переменные окружения могут работать только с **текстовыми строками**, поскольку они являются внешними по отношению к Python и должны быть совместимы с другими программами и остальной системой (и даже с различными операционными системами, такими как Linux, Windows, macOS).
-ÐÑ\82о ознаÑ\87аеÑ\82, Ñ\87Ñ\82о **лÑ\8eбое знаÑ\87ение**, Ñ\81Ñ\87иÑ\82анное в Python из пеÑ\80еменной окÑ\80Ñ\83жениÑ\8f, **бÑ\83деÑ\82 `str`**, и лÑ\8eбое пÑ\80еобÑ\80азование к дÑ\80Ñ\83гомÑ\83 Ñ\82ипÑ\83 или лÑ\8eбаÑ\8f пÑ\80овеÑ\80ка должны быть выполнены в коде.
+ÐÑ\82о ознаÑ\87аеÑ\82, Ñ\87Ñ\82о **лÑ\8eбое знаÑ\87ение**, Ñ\81Ñ\87иÑ\82анное в Python из пеÑ\80еменной окÑ\80Ñ\83жениÑ\8f, **бÑ\83деÑ\82 `str`**, и лÑ\8eбое пÑ\80еобÑ\80азование к дÑ\80Ñ\83гомÑ\83 Ñ\82ипÑ\83 или лÑ\8eбаÑ\8f валидаÑ\86иÑ\8f должны быть выполнены в коде.
Подробнее об использовании переменных окружения для работы с **настройками приложения** вы узнаете в [Расширенное руководство пользователя - Настройки и переменные среды](./advanced/settings.md){.internal-link target=_blank}.
Для работы в режиме продакшн вместо `fastapi dev` нужно использовать `fastapi run`. 🚀
-Внутри **FastAPI CLI** используется <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a>, высокопроизводительный, готовый к работе в продакшне ASGI-сервер. 😎
+Внутри **FastAPI CLI** используется <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a>, высокопроизводительный, готовый к работе в продакшн ASGI-сервер. 😎
## `fastapi dev` { #fastapi-dev }
### Основано на открытых стандартах { #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://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> для создания API, включая объявления <dfn title="также известны как HTTP-методы, например: POST, GET, PUT, DELETE">операций</dfn> <dfn title="также известен как: эндпоинты, маршруты">пути</dfn>, параметров, тел запросов, безопасности и т.д.
* Автоматическая документация моделей данных с помощью <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (так как сама спецификация OpenAPI основана на JSON Schema).
* Разработан вокруг этих стандартов, после тщательного их изучения. Это не дополнительная надстройка поверх.
* Это также позволяет использовать автоматическую **генерацию клиентского кода** на многих языках.
* Объекты JSON (`dict`).
* Массив JSON (`list`) с определёнными типами элементов.
* Строковые (`str`) поля с ограничением минимальной и максимальной длины.
- * Числа (`int`, `float`) с минимальными и максимальными значениями и т. п.
+ * Числа (`int`, `float`) с минимальными и максимальными значениями и т.п.
* Проверка для более экзотических типов, таких как:
* URL.
* HTTP Basic.
* **OAuth2** (также с **токенами JWT**). Ознакомьтесь с руководством [OAuth2 с JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}.
* Ключи API в:
- * Заголовках.
+ * HTTP-заголовках.
* Параметрах запросов.
* Cookies и т.п.
Вдобавок все функции безопасности от Starlette (включая **сессионные cookies**).
-Все инструменты и компоненты спроектированы для многократного использования и легко интегрируются с вашими системами, хранилищами данных, реляционными и NoSQL базами данных и т. д.
+Все инструменты и компоненты спроектированы для многократного использования и легко интегрируются с вашими системами, хранилищами данных, реляционными и NoSQL базами данных и т.д.
### Внедрение зависимостей { #dependency-injection }
-FastAPI включает в себя чрезвычайно простую в использовании, но чрезвычайно мощную систему <abbr title='известную как: "components", "resources", "services", "providers"'><strong>Внедрения зависимостей</strong></abbr>.
+FastAPI включает в себя чрезвычайно простую в использовании, но чрезвычайно мощную систему <dfn title='также известна как: "компоненты", "ресурсы", "сервисы", "провайдеры"'><strong>Внедрения зависимостей</strong></dfn>.
* Даже зависимости могут иметь зависимости, создавая иерархию или **«граф» зависимостей**.
* Всё **автоматически обрабатывается** фреймворком.
* Все зависимости могут запрашивать данные из запросов и **дополнять операции пути** ограничениями и автоматической документацией.
* **Автоматическая проверка** даже для параметров *операций пути*, определённых в зависимостях.
-* Поддержка сложных систем аутентификации пользователей, **соединений с базами данных** и т. д.
-* **Никаких компромиссов** с базами данных, интерфейсами и т. д. Но при этом — лёгкая интеграция со всеми ними.
+* Поддержка сложных систем аутентификации пользователей, **соединений с базами данных** и т.д.
+* **Никаких компромиссов** с базами данных, интерфейсами и т.д. Но при этом — лёгкая интеграция со всеми ними.
### Нет ограничений на "Плагины" { #unlimited-plug-ins }
### Проверен { #tested }
-* 100% <abbr title="Количество автоматически проверяемого кода">покрытие тестами</abbr>.
-* 100% <abbr title="Аннотации типов Python, благодаря которым ваш редактор и внешние инструменты могут обеспечить вам лучшую поддержку">аннотирование типов</abbr> в кодовой базе.
+* 100% <dfn title="Количество автоматически проверяемого кода">покрытие тестами</dfn>.
+* 100% <dfn title="Аннотации типов Python, благодаря которым ваш редактор кода и внешние инструменты могут обеспечить вам лучшую поддержку">аннотирование типов</dfn> в кодовой базе.
* Используется в продакшн‑приложениях.
## Возможности Starlette { #starlette-features }
**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>’ы для баз данных.
Это также означает, что во многих случаях вы можете передавать тот же объект, который получили из запроса, **непосредственно в базу данных**, так как всё проверяется автоматически.
* **Никакой нервотрёпки**:
* Не нужно изучать новые схемы в микроязыках.
* Если вы знаете типы в Python, вы знаете, как использовать Pydantic.
-* Прекрасно сочетается с вашим **<abbr title="Integrated Development Environment - Интегрированная среда разработки: попросту «редактора кода»">IDE</abbr>/<abbr title="Программа, проверяющая ошибки в коде">linter</abbr>/мозгом**:
+* Прекрасно сочетается с вашим **<abbr title="Integrated Development Environment - Интегрированная среда разработки: похоже на редактор кода">IDE</abbr>/<dfn title="Программа, которая проверяет код на ошибки">линтер</dfn>/мозгом**:
* Потому что структуры данных pydantic — это всего лишь экземпляры классов, определённых вами; автозавершение, проверка кода, mypy и ваша интуиция — всё будет работать с вашими валидированными данными.
* Валидация **сложных структур**:
- * Использование иерархических моделей Pydantic; `List`, `Dict` и т. п. из модуля `typing` (входит в стандартную библиотеку Python).
+ * Использование иерархических моделей Pydantic; `List`, `Dict` и т.п. из модуля `typing` (входит в стандартную библиотеку Python).
* Валидаторы позволяют чётко и легко определять, проверять и документировать сложные схемы данных в виде JSON Schema.
* У вас могут быть глубоко **вложенные объекты JSON**, и все они будут проверены и аннотированы.
* **Расширяемость**:
## Исследования { #investigation }
-Ð\91лагодаÑ\80Ñ\8f опÑ\8bÑ\82Ñ\83 иÑ\81полÑ\8cзованиÑ\8f Ñ\81Ñ\83Ñ\89еÑ\81Ñ\82вÑ\83Ñ\8eÑ\89иÑ\85 алÑ\8cÑ\82еÑ\80наÑ\82ив, мÑ\8b Ñ\81 коллегами изÑ\83Ñ\87или иÑ\85 оÑ\81новнÑ\8bе идеи и Ñ\81комбиниÑ\80овали Ñ\81обÑ\80аннÑ\8bе знаниÑ\8f наилÑ\83Ñ\87Ñ\88им обÑ\80азом.
+Ð\98Ñ\81полÑ\8cзÑ\83Ñ\8f вÑ\81е Ñ\81Ñ\83Ñ\89еÑ\81Ñ\82вовавÑ\88ие Ñ\80анее алÑ\8cÑ\82еÑ\80наÑ\82ивÑ\8b, Ñ\8f полÑ\83Ñ\87ил возможноÑ\81Ñ\82Ñ\8c Ñ\83 каждой из ниÑ\85 Ñ\87емÑ\83-Ñ\82о наÑ\83Ñ\87иÑ\82Ñ\8cÑ\81Ñ\8f, позаимÑ\81Ñ\82воваÑ\82Ñ\8c идеи и обÑ\8aединиÑ\82Ñ\8c иÑ\85 наилÑ\83Ñ\87Ñ\88им обÑ\80азом длÑ\8f Ñ\81ебÑ\8f и длÑ\8f команд Ñ\80азÑ\80абоÑ\82Ñ\87иков, Ñ\81 коÑ\82оÑ\80Ñ\8bми Ñ\8f Ñ\80абоÑ\82ал.
Например, стало ясно, что необходимо брать за основу стандартные аннотации типов Python.
Я проверил несколько идей на самых популярных редакторах кода: PyCharm, VS Code, редакторы на базе Jedi.
-Ð\94аннÑ\8bе по Ñ\80едакÑ\82оÑ\80ам Ñ\8f взÑ\8fл из <a href="https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools" class="external-link" target="_blank">опÑ\80оÑ\81а Python-разработчиков</a>, который охватывает около 80% пользователей.
+СоглаÑ\81но поÑ\81леднемÑ\83 <a href="https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools" class="external-link" target="_blank">опÑ\80оÑ\81Ñ\83 Python-разработчиков</a>, который охватывает около 80% пользователей.
Это означает, что **FastAPI** был специально проверен на редакторах, используемых 80% Python-разработчиками. И поскольку большинство других редакторов, как правило, работают аналогичным образом, все его преимущества должны работать практически для всех редакторов.
Таким образом, я смог найти наилучшие способы сократить дублирование кода, обеспечить повсеместное автозавершение, проверку типов и ошибок и т.д.
-И все это, чтобы все пользователи могли получать наилучший опыт разработки.
+И все это, чтобы все разработчики могли получать наилучший опыт разработки.
## Зависимости { #requirements }
По моим предложениям был изменён код этого фреймворка, чтобы сделать его полностью совместимым с JSON Schema, поддержать различные способы определения ограничений и улучшить поддержку в редакторах кода (проверки типов, автозавершение) на основе тестов в нескольких редакторах.
-В то же время, я принимал участие в разработке <a href="https://www.starlette.dev/" class="external-link" target="_blank">**Starlette**</a>, ещё один из основных компонентов FastAPI.
+Во время разработки я также внес вклад в <a href="https://www.starlette.dev/" class="external-link" target="_blank">**Starlette**</a>, другую ключевую зависимость.
## Разработка { #development }
Например, вы можете создать подкласс `HTTPBearer`, который будет возвращать ошибку `403 Forbidden` вместо стандартной `401 Unauthorized`:
-{* ../../docs_src/authentication_error_status_code/tutorial001_an_py39.py hl[9:13] *}
+{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
/// tip | Совет
## О безопасности, API и документации { #about-security-apis-and-docs }
-Скрытие пользовательских интерфейсов документации в продакшн *не должно* быть способом защиты вашего API.
+Скрытие пользовательских интерфейсов документации в продакшн не должно быть способом защиты вашего API.
-Это не добавляет дополнительной безопасности вашему API, *операции пути* (обработчики пути) всё равно будут доступны по своим путям.
+Это не добавляет дополнительной безопасности вашему API, операции пути (обработчики пути) всё равно будут доступны по своим путям.
Если в вашем коде есть уязвимость, она всё равно останется.
Например:
-{* ../../docs_src/conditional_openapi/tutorial001_py39.py hl[6,11] *}
+{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
Здесь мы объявляем настройку `openapi_url` с тем же значением по умолчанию — `"/openapi.json"`.
Но вы можете отключить её, установив `syntaxHighlight` в `False`:
-{* ../../docs_src/configure_swagger_ui/tutorial001_py39.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
…и после этого Swagger UI больше не будет показывать подсветку синтаксиса:
Аналогично вы можете задать тему подсветки синтаксиса с ключом "syntaxHighlight.theme" (обратите внимание, что посередине стоит точка):
-{* ../../docs_src/configure_swagger_ui/tutorial002_py39.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
Эта настройка изменит цветовую тему подсветки синтаксиса:
Например, чтобы отключить `deepLinking`, можно передать такие настройки в `swagger_ui_parameters`:
-{* ../../docs_src/configure_swagger_ui/tutorial003_py39.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
## Другие параметры Swagger UI { #other-swagger-ui-parameters }
Чтобы отключить её, установите их URL в значение `None` при создании вашего приложения `FastAPI`:
-{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[8] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
### Подключить пользовательскую документацию { #include-the-custom-docs }
Аналогично и для ReDoc...
-{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[2:6,11:19,22:24,27:33] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
/// tip | Совет
Чтобы убедиться, что всё работает, создайте *операцию пути*:
-{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[36:38] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
### Тестирование { #test-it }
* Импортируйте `StaticFiles`.
* Смонтируйте экземпляр `StaticFiles()` в определённый путь.
-{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[7,11] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
### Протестируйте статические файлы { #test-the-static-files }
Чтобы отключить её, установите их URL в значение `None` при создании вашего приложения `FastAPI`:
-{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[9] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
### Подключить пользовательскую документацию со статическими файлами { #include-the-custom-docs-for-static-files }
Аналогично и для ReDoc...
-{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[2:6,14:22,25:27,30:36] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
/// tip | Совет
Чтобы убедиться, что всё работает, создайте *операцию пути*:
-{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[39:41] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
### Тестирование UI со статическими файлами { #test-static-files-ui }
* `version`: Версия вашего API, например `2.5.0`.
* `openapi_version`: Версия используемой спецификации OpenAPI. По умолчанию — последняя: `3.1.0`.
* `summary`: Краткое описание API.
-* `description`: Описание вашего API; может включать Markdown и будет отображается в документации.
+* `description`: Описание вашего API; может включать Markdown и будет отображаться в документации.
* `routes`: Список маршрутов — это каждая зарегистрированная *операция пути*. Берутся из `app.routes`.
/// info | Информация
Сначала напишите приложение **FastAPI** как обычно:
-{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[1,4,7:9] *}
+{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
### Сгенерируйте схему OpenAPI { #generate-the-openapi-schema }
Затем используйте ту же вспомогательную функцию для генерации схемы OpenAPI внутри функции `custom_openapi()`:
-{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[2,15:21] *}
+{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
### Измените схему OpenAPI { #modify-the-openapi-schema }
Теперь можно добавить расширение ReDoc, добавив кастомный `x-logo` в «объект» `info` в схеме OpenAPI:
-{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[22:24] *}
+{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
### Кэшируйте схему OpenAPI { #cache-the-openapi-schema }
Она будет создана один раз, а затем тот же кэшированный вариант будет использоваться для последующих запросов.
-{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[13:14,25:26] *}
+{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
### Переопределите метод { #override-the-method }
Теперь вы можете заменить метод `.openapi()` на вашу новую функцию.
-{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[29] *}
+{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
### Проверьте { #check-it }
Вот небольшой пример того, как можно интегрировать Strawberry с FastAPI:
-{* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
+{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
Подробнее о Strawberry можно узнать в <a href="https://strawberry.rocks/" class="external-link" target="_blank">документации Strawberry</a>.
# Как сделать — Рецепты { #how-to-recipes }
-Здесь вы найдете разные рецепты и руководства «как сделать» по различным темам.
+Здесь вы найдете разные рецепты и руководства «как сделать» по **различным темам**.
-Большинство из этих идей более-менее независимы, и в большинстве случаев вам стоит изучать их только если они напрямую относятся к вашему проекту.
+Большинство из этих идей более-менее **независимы**, и в большинстве случаев вам стоит изучать их только если они напрямую относятся к **вашему проекту**.
Если что-то кажется интересным и полезным для вашего проекта, смело изучайте; в противном случае, вероятно, можно просто пропустить.
/// tip | Совет
-Если вы хотите изучить FastAPI структурированно (рекомендуется), вместо этого читайте [Учебник — Руководство пользователя](../tutorial/index.md){.internal-link target=_blank} по главам.
+Если вы хотите **изучить FastAPI** структурированно (рекомендуется), вместо этого читайте [Учебник — Руководство пользователя](../tutorial/index.md){.internal-link target=_blank} по главам.
///
* **Скорость**: Очень высокая производительность, на уровне **NodeJS** и **Go** (благодаря Starlette и Pydantic). [Один из самых быстрых доступных фреймворков Python](#performance).
* **Быстрота разработки**: Увеличьте скорость разработки фич примерно на 200–300%. *
* **Меньше ошибок**: Сократите примерно на 40% количество ошибок, вызванных человеком (разработчиком). *
-* **Интуитивность**: Отличная поддержка редактора кода. <abbr title="также известное как: автодополнение, автозавершение, IntelliSense">Автозавершение</abbr> везде. Меньше времени на отладку.
+* **Интуитивность**: Отличная поддержка редактора кода. <dfn title="также известное как: автодополнение, автозавершение, IntelliSense">Автозавершение</dfn> везде. Меньше времени на отладку.
* **Простота**: Разработан так, чтобы его было легко использовать и осваивать. Меньше времени на чтение документации.
* **Краткость**: Минимизируйте дублирование кода. Несколько возможностей из каждого объявления параметров. Меньше ошибок.
* **Надежность**: Получите код, готовый к продакшн. С автоматической интерактивной документацией.
<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="Command Line Interface – Интерфейс командной строки">CLI</abbr> для использования в терминале вместо веб-API, посмотрите <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</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**. ⌨️ 🚀
* Валидацию данных:
* Автоматические и понятные ошибки, когда данные некорректны.
* Валидацию даже для глубоко вложенных объектов JSON.
-* <abbr title="также известное как: сериализация, парсинг, маршалинг">Преобразование</abbr> входных данных: из сети в данные и типы Python. Чтение из:
+* <dfn title="также известное как: сериализация, парсинг, маршалинг">Преобразование</dfn> входных данных: из сети в данные и типы Python. Чтение из:
* JSON.
* Параметров пути.
* Параметров запроса.
* HTTP-заголовков.
* Форм.
* Файлов.
-* <abbr title="также известное как: сериализация, парсинг, маршалинг">Преобразование</abbr> выходных данных: из данных и типов Python в данные сети (например, JSON):
+* <dfn title="также известное как: сериализация, парсинг, маршалинг">Преобразование</dfn> выходных данных: из данных и типов Python в данные сети (например, JSON):
* Преобразование типов Python (`str`, `int`, `float`, `bool`, `list` и т.д.).
* Объекты `datetime`.
* Объекты `UUID`.
* Объявление **параметров** из других источников: **HTTP-заголовки**, **cookies**, **поля формы** и **файлы**.
* Как задать **ограничения валидации** вроде `maximum_length` или `regex`.
-* Очень мощную и простую в использовании систему **<abbr title="также известна как: компоненты, ресурсы, провайдеры, сервисы, инъекции">внедрения зависимостей</abbr>**.
+* Очень мощную и простую в использовании систему **<dfn title="также известна как: компоненты, ресурсы, провайдеры, сервисы, инъекции">внедрения зависимостей</dfn>**.
* Безопасность и аутентификацию, включая поддержку **OAuth2** с **JWT токенами** и **HTTP Basic** аутентификацию.
* Более продвинутые (но столь же простые) приёмы объявления **глубоко вложенных JSON-моделей** (спасибо Pydantic).
* Интеграцию **GraphQL** с <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> и другими библиотеками.
* <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://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - обязателен, если вы хотите поддерживать <dfn title="преобразование строки, полученной из HTTP-запроса, в данные Python">«парсинг»</dfn> форм через `request.form()`.
Используется FastAPI:
- 🤖 Автоматически сгенерированный фронтенд‑клиент.
- 🧪 [Playwright](https://playwright.dev) для End‑to‑End тестирования.
- 🦇 Поддержка тёмной темы.
-- 🐋 [Docker Compose](https://www.docker.com) для разработки и продакшна.
+- 🐋 [Docker Compose](https://www.docker.com) для разработки и продакшн.
- 🔒 Безопасное хэширование паролей по умолчанию.
- 🔑 Аутентификация по JWT‑токенам.
- 📫 Восстановление пароля по электронной почте.
Python поддерживает необязательные «подсказки типов» (их также называют «аннотациями типов»).
-Эти **«подсказки типов»** или аннотации — это специальный синтаксис, позволяющий объявлять <abbr title="например: str, int, float, bool">тип</abbr> переменной.
+Эти **«подсказки типов»** или аннотации — это специальный синтаксис, позволяющий объявлять <dfn title="например: str, int, float, bool">тип</dfn> переменной.
Объявляя типы для ваших переменных, редакторы кода и инструменты смогут лучше вас поддерживать.
Давайте начнем с простого примера:
-{* ../../docs_src/python_types/tutorial001_py39.py *}
+{* ../../docs_src/python_types/tutorial001_py310.py *}
Вызов этой программы выводит:
* Принимает `first_name` и `last_name`.
* Преобразует первую букву каждого значения в верхний регистр с помощью `title()`.
-* <abbr title="Объединяет в одно целое. Содержимое одного — сразу после другого.">Соединяет</abbr> их пробелом посередине.
+* <dfn title="Соединяет их в одно. Содержимое одного — сразу после другого.">Соединяет</dfn> их пробелом посередине.
-{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
+{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
### Отредактируем пример { #edit-it }
Это и есть «подсказки типов»:
-{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
+{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
Это не то же самое, что объявление значений по умолчанию, как, например:
Посмотрите на эту функцию — у неё уже есть подсказки типов:
-{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
+{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
Так как редактор кода знает типы переменных, вы получаете не только автозавершение, но и проверки ошибок:
Теперь вы знаете, что нужно исправить — преобразовать `age` в строку с помощью `str(age)`:
-{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
+{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
## Объявление типов { #declaring-types }
* `bool`
* `bytes`
-{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
+{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
-### Generic-типы с параметрами типов { #generic-types-with-type-parameters }
+### Модуль `typing` { #typing-module }
-Ð\95Ñ\81Ñ\82Ñ\8c Ñ\81Ñ\82Ñ\80Ñ\83кÑ\82Ñ\83Ñ\80Ñ\8b даннÑ\8bÑ\85, коÑ\82оÑ\80Ñ\8bе могÑ\83Ñ\82 Ñ\81одеÑ\80жаÑ\82Ñ\8c дÑ\80Ñ\83гие знаÑ\87ениÑ\8f, напÑ\80имеÑ\80, `dict`, `list`, `set` и `tuple`. Ð\98 внÑ\83Ñ\82Ñ\80енние знаÑ\87ениÑ\8f Ñ\82оже могÑ\83Ñ\82 имеÑ\82Ñ\8c Ñ\81вой Ñ\82ип.
+Ð\94лÑ\8f некоÑ\82оÑ\80Ñ\8bÑ\85 дополниÑ\82елÑ\8cнÑ\8bÑ\85 Ñ\81Ñ\86енаÑ\80иев можеÑ\82 понадобиÑ\82Ñ\8cÑ\81Ñ\8f импоÑ\80Ñ\82иÑ\80оваÑ\82Ñ\8c Ñ\87Ñ\82о-Ñ\82о из Ñ\81Ñ\82андаÑ\80Ñ\82ного модÑ\83лÑ\8f `typing`. Ð\9dапÑ\80имеÑ\80, когда вÑ\8b Ñ\85оÑ\82иÑ\82е обÑ\8aÑ\8fвиÑ\82Ñ\8c, Ñ\87Ñ\82о Ñ\87Ñ\82о-Ñ\82о имееÑ\82 «лÑ\8eбой Ñ\82ип», можно иÑ\81полÑ\8cзоваÑ\82Ñ\8c `Any` из `typing`:
-Такие типы, которые содержат внутренние типы, называют «**generic**»-типами. И их можно объявлять, в том числе с указанием внутренних типов.
+```python
+from typing import Any
-Чтобы объявлять эти типы и их внутренние типы, вы можете использовать стандартный модуль Python `typing`. Он существует специально для поддержки подсказок типов.
-#### Новые версии Python { #newer-versions-of-python }
-
-Синтаксис с использованием `typing` **совместим** со всеми версиями, от Python 3.6 до самых новых, включая Python 3.9, Python 3.10 и т.д.
+def some_function(data: Any):
+ print(data)
+```
-По мере развития Python **новые версии** получают улучшенную поддержку этих аннотаций типов, и во многих случаях вам даже не нужно импортировать и использовать модуль `typing`, чтобы объявлять аннотации типов.
+### Generic-типы { #generic-types }
-Ð\95Ñ\81ли вÑ\8b можеÑ\82е вÑ\8bбÑ\80аÑ\82Ñ\8c более Ñ\81вежÑ\83Ñ\8e веÑ\80Ñ\81иÑ\8e Python длÑ\8f пÑ\80оекÑ\82а, вÑ\8b полÑ\83Ñ\87иÑ\82е дополниÑ\82елÑ\8cнÑ\83Ñ\8e пÑ\80оÑ\81Ñ\82оÑ\82Ñ\83.
+Ð\9dекоÑ\82оÑ\80Ñ\8bе Ñ\82ипÑ\8b могÑ\83Ñ\82 пÑ\80инимаÑ\82Ñ\8c «паÑ\80амеÑ\82Ñ\80Ñ\8b Ñ\82ипов» в квадÑ\80аÑ\82нÑ\8bÑ\85 Ñ\81кобкаÑ\85, Ñ\87Ñ\82обÑ\8b опÑ\80еделиÑ\82Ñ\8c иÑ\85 внÑ\83Ñ\82Ñ\80енние Ñ\82ипÑ\8b. Ð\9dапÑ\80имеÑ\80, «Ñ\81пиÑ\81ок Ñ\81Ñ\82Ñ\80ок» обÑ\8aÑ\8fвлÑ\8fеÑ\82Ñ\81Ñ\8f как `list[str]`.
-Ð\92о вÑ\81ей докÑ\83менÑ\82аÑ\86ии еÑ\81Ñ\82Ñ\8c пÑ\80имеÑ\80Ñ\8b, Ñ\81овмеÑ\81Ñ\82имÑ\8bе Ñ\81 каждой веÑ\80Ñ\81ией Python (когда еÑ\81Ñ\82Ñ\8c Ñ\80азлиÑ\87иÑ\8f).
+Такие Ñ\82ипÑ\8b, коÑ\82оÑ\80Ñ\8bе пÑ\80инимаÑ\8eÑ\82 паÑ\80амеÑ\82Ñ\80Ñ\8b Ñ\82ипов, назÑ\8bваÑ\8eÑ\82Ñ\81Ñ\8f **Generic-Ñ\82ипами** или **Generics**.
-Ð\9dапÑ\80имеÑ\80, «**Python 3.6+**» ознаÑ\87аеÑ\82 Ñ\81овмеÑ\81Ñ\82имоÑ\81Ñ\82Ñ\8c Ñ\81 Python 3.6 и вÑ\8bÑ\88е (вклÑ\8eÑ\87аÑ\8f 3.7, 3.8, 3.9, 3.10 и Ñ\82.д.). Ð\90 «**Python 3.9+**» â\80\94 Ñ\81овмеÑ\81Ñ\82имоÑ\81Ñ\82Ñ\8c Ñ\81 Python 3.9 и вÑ\8bÑ\88е (вклÑ\8eÑ\87аÑ\8f 3.10 и Ñ\82.п.).
+Ð\92Ñ\8b можеÑ\82е иÑ\81полÑ\8cзоваÑ\82Ñ\8c Ñ\82е же вÑ\81Ñ\82Ñ\80оеннÑ\8bе Ñ\82ипÑ\8b как generics (Ñ\81 квадÑ\80аÑ\82нÑ\8bми Ñ\81кобками и Ñ\82ипами внÑ\83Ñ\82Ñ\80и):
-Если вы можете использовать **последние версии Python**, используйте примеры для самой новой версии — у них будет **самый лучший и простой синтаксис**, например, «**Python 3.10+**».
+* `list`
+* `tuple`
+* `set`
+* `dict`
#### List { #list }
Так как список — это тип, содержащий внутренние типы, укажите их в квадратных скобках:
-{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
+{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
/// info | Информация
Аналогично вы бы объявили `tuple` и `set`:
-{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
+{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
Это означает:
Второй параметр типа — для значений `dict`:
-{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
+{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
Это означает:
Вы можете объявить, что переменная может быть **одним из нескольких типов**, например, `int` или `str`.
-В Python 3.6 и выше (включая Python 3.10) вы можете использовать тип `Union` из `typing` и перечислить в квадратных скобках все допустимые типы.
-
-В Python 3.10 также появился **новый синтаксис**, где допустимые типы можно указать через <abbr title='также называется «побитовый оператор OR», но это значение здесь нерелевантно'>вертикальную черту (`|`)</abbr>.
+Чтобы это определить, используйте <dfn title='также называется «побитовый оператор OR», но это значение здесь нерелевантно'>вертикальную черту (`|`)</dfn> для разделения обоих типов.
-//// tab | Python 3.10+
+Это называется «объединение» (union), потому что переменная может быть чем угодно из объединения этих двух множеств типов.
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial008b_py39.py!}
-```
-
-////
-
-В обоих случаях это означает, что `item` может быть `int` или `str`.
+Это означает, что `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_py39.py!}
-```
-
-Использование `Optional[str]` вместо просто `str` позволит редактору кода помочь вам обнаружить ошибки, когда вы предполагаете, что значение всегда `str`, хотя на самом деле оно может быть и `None`.
-
-`Optional[Something]` — это на самом деле сокращение для `Union[Something, None]`, они эквивалентны.
-
-Это также означает, что в Python 3.10 вы можете использовать `Something | None`:
-
//// tab | Python 3.10+
```Python hl_lines="1"
////
-//// tab | Python 3.9+
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial009_py39.py!}
-```
-
-////
-
-//// tab | Python 3.9+ альтернативный вариант
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial009b_py39.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_py39.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`
-
-И, как и в предыдущих версиях Python, из модуля `typing`:
-
-* `Union`
-* `Optional`
-* ...и другие.
-
-В Python 3.10, как альтернативу generics `Union` и `Optional`, можно использовать <abbr title='также называется «побитовый оператор OR», но это значение здесь нерелевантно'>вертикальную черту (`|`)</abbr> для объявления объединений типов — это гораздо лучше и проще.
-
-////
-
-//// tab | Python 3.9+
-
-Вы можете использовать те же встроенные типы как generics (с квадратными скобками и типами внутри):
-
-* `list`
-* `tuple`
-* `set`
-* `dict`
-
-И generics из модуля `typing`:
-
-* `Union`
-* `Optional`
-* ...и другие.
-
-////
+Использование `str | None` вместо просто `str` позволит редактору кода помочь вам обнаружить ошибки, когда вы предполагаете, что значение всегда `str`, хотя на самом деле оно может быть и `None`.
### Классы как типы { #classes-as-types }
Допустим, у вас есть класс `Person` с именем:
-{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
+{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
Тогда вы можете объявить переменную типа `Person`:
-{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
+{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
И снова вы получите полную поддержку редактора кода:
**FastAPI** целиком основан на Pydantic.
-Вы увидите намного больше всего этого на практике в [Руководстве пользователя](tutorial/index.md){.internal-link target=_blank}.
-
-/// 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>.
-
-///
+Вы увидите намного больше всего этого на практике в [Учебник - Руководство пользователя](tutorial/index.md){.internal-link target=_blank}.
## Подсказки типов с аннотациями метаданных { #type-hints-with-metadata-annotations }
-В Python также есть возможность добавлять **дополнительные <abbr title="Данные о данных, в данном случае — информация о типе, например описание.">метаданные</abbr>** к подсказкам типов с помощью `Annotated`.
+В Python также есть возможность добавлять **дополнительные <dfn title="Данные о данных, в данном случае — информация о типе, например описание.">метаданные</dfn>** к подсказкам типов с помощью `Annotated`.
-Ð\9dаÑ\87инаÑ\8f Ñ\81 Python 3.9, `Annotated` вÑ\85одиÑ\82 в Ñ\81Ñ\82андаÑ\80Ñ\82нÑ\83Ñ\8e библиоÑ\82екÑ\83, поÑ\8dÑ\82омÑ\83 вÑ\8b можеÑ\82е импоÑ\80Ñ\82иÑ\80оваÑ\82Ñ\8c его из `typing`.
+Ð\92Ñ\8b можеÑ\82е импоÑ\80Ñ\82иÑ\80оваÑ\82Ñ\8c `Annotated` из `typing`.
-{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
+{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
Сам Python ничего не делает с `Annotated`. А для редакторов кода и других инструментов тип по-прежнему `str`.
* **Документирования** API с использованием OpenAPI:
* что затем используется пользовательскими интерфейсами автоматической интерактивной документации.
-Ð\92Ñ\81Ñ\91 Ñ\8dÑ\82о можеÑ\82 звÑ\83Ñ\87аÑ\82Ñ\8c абÑ\81Ñ\82Ñ\80акÑ\82но. Ð\9dе волнÑ\83йÑ\82еÑ\81Ñ\8c. Ð\92Ñ\8b Ñ\83видиÑ\82е вÑ\81Ñ\91 Ñ\8dÑ\82о в дейÑ\81Ñ\82вии в [Ð Ñ\83ководÑ\81Ñ\82ве пользователя](tutorial/index.md){.internal-link target=_blank}.
+Ð\92Ñ\81Ñ\91 Ñ\8dÑ\82о можеÑ\82 звÑ\83Ñ\87аÑ\82Ñ\8c абÑ\81Ñ\82Ñ\80акÑ\82но. Ð\9dе волнÑ\83йÑ\82еÑ\81Ñ\8c. Ð\92Ñ\8b Ñ\83видиÑ\82е вÑ\81Ñ\91 Ñ\8dÑ\82о в дейÑ\81Ñ\82вии в [УÑ\87ебник - Ð Ñ\83ководÑ\81Ñ\82во пользователя](tutorial/index.md){.internal-link target=_blank}.
Важно то, что, используя стандартные типы Python в одном месте (вместо добавления дополнительных классов, декораторов и т.д.), **FastAPI** сделает за вас большую часть работы.
Сначала импортируйте `BackgroundTasks` и объявите параметр в вашей функции‑обработчике пути с типом `BackgroundTasks`:
-{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
+{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
**FastAPI** создаст объект типа `BackgroundTasks` для вас и передаст его через этот параметр.
Так как операция записи не использует `async` и `await`, мы определим функцию как обычную `def`:
-{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
+{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
## Добавление фоновой задачи { #add-the-background-task }
Внутри вашей функции‑обработчика пути передайте функцию задачи объекту фоновых задач методом `.add_task()`:
-{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
+{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
`.add_task()` принимает следующие аргументы:
## Встраивание зависимостей { #dependency-injection }
-Использование `BackgroundTasks` также работает с системой встраивания зависимостей, вы можете объявить параметр типа `BackgroundTasks` на нескольких уровнях: в функции‑обработчике пути, в зависимости (dependable), в подзависимости и т. д.
+Использование `BackgroundTasks` также работает с системой встраивания зависимостей, вы можете объявить параметр типа `BackgroundTasks` на нескольких уровнях: в функции‑обработчике пути, в зависимости (dependable), в подзависимости и т.д.
**FastAPI** знает, что делать в каждом случае и как переиспользовать один и тот же объект, так чтобы все фоновые задачи были объединены и затем выполнены в фоне:
## Предостережение { #caveat }
-Если вам нужно выполнять тяжелые вычисления в фоне, и при этом они не обязательно должны запускаться тем же процессом (например, вам не нужно делиться памятью, переменными и т. п.), вам могут подойти более мощные инструменты, такие как <a href="https://docs.celeryq.dev" class="external-link" target="_blank">Celery</a>.
+Если вам нужно выполнять тяжелые вычисления в фоне, и при этом они не обязательно должны запускаться тем же процессом (например, вам не нужно делиться памятью, переменными и т.п.), вам могут подойти более мощные инструменты, такие как <a href="https://docs.celeryq.dev" class="external-link" target="_blank">Celery</a>.
Они обычно требуют более сложной конфигурации, менеджера очереди сообщений/заданий (например, RabbitMQ или Redis), но позволяют запускать фоновые задачи в нескольких процессах и, что особенно важно, на нескольких серверах.
Точно так же, как и в случае с классом `FastAPI`, вам нужно импортировать и создать его «экземпляр»:
-{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[1,3] title["app/routers/users.py"] *}
+{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
### *Операции пути* с `APIRouter` { #path-operations-with-apirouter }
Используйте его так же, как вы использовали бы класс `FastAPI`:
-{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
+{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
Вы можете думать об `APIRouter` как об «мини-классе `FastAPI`».
Теперь мы воспользуемся простой зависимостью, чтобы прочитать кастомный HTTP-заголовок `X-Token`:
-{* ../../docs_src/bigger_applications/app_an_py39/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
+{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
/// tip | Подсказка
Таким образом, вместо того чтобы добавлять всё это в каждую *операцию пути*, мы можем добавить это в `APIRouter`.
-{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
+{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
Так как путь каждой *операции пути* должен начинаться с `/`, как здесь:
Поэтому мы используем относительный импорт с `..` для зависимостей:
-{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[3] title["app/routers/items.py"] *}
+{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
#### Как работает относительный импорт { #how-relative-imports-work }
Но мы всё равно можем добавить _ещё_ `tags`, которые будут применяться к конкретной *операции пути*, а также дополнительные `responses`, специфичные для этой *операции пути*:
-{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[30:31] title["app/routers/items.py"] *}
+{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
/// tip | Подсказка
И мы даже можем объявить [глобальные зависимости](dependencies/global-dependencies.md){.internal-link target=_blank}, которые будут объединены с зависимостями для каждого `APIRouter`:
-{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[1,3,7] title["app/main.py"] *}
+{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
### Импорт `APIRouter` { #import-the-apirouter }
Теперь мы импортируем другие подмодули, содержащие `APIRouter`:
-{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[4:5] title["app/main.py"] *}
+{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
Так как файлы `app/routers/users.py` и `app/routers/items.py` являются подмодулями, входящими в один и тот же Python-пакет `app`, мы можем использовать одну точку `.` для импорта через «относительные импорты».
Поэтому, чтобы иметь возможность использовать обе в одном файле, мы импортируем подмодули напрямую:
-{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[5] title["app/main.py"] *}
+{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
### Подключение `APIRouter` для `users` и `items` { #include-the-apirouters-for-users-and-items }
Теперь давайте подключим `router` из подмодулей `users` и `items`:
-{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[10:11] title["app/main.py"] *}
+{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
/// info | Примечание
Для этого примера всё будет очень просто. Но допустим, что поскольку он используется совместно с другими проектами в организации, мы не можем модифицировать его и добавить `prefix`, `dependencies`, `tags` и т.д. непосредственно в `APIRouter`:
-{* ../../docs_src/bigger_applications/app_an_py39/internal/admin.py hl[3] title["app/internal/admin.py"] *}
+{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
Но мы всё равно хотим задать пользовательский `prefix` при подключении `APIRouter`, чтобы все его *операции пути* начинались с `/admin`, хотим защитить его с помощью `dependencies`, которые у нас уже есть для этого проекта, и хотим включить `tags` и `responses`.
Мы можем объявить всё это, не изменяя исходный `APIRouter`, передав эти параметры в `app.include_router()`:
-{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[14:17] title["app/main.py"] *}
+{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
Таким образом исходный `APIRouter` не будет модифицирован, и мы сможем использовать файл `app/internal/admin.py` сразу в нескольких проектах организации.
Здесь мы делаем это... просто чтобы показать, что можем 🤷:
-{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[21:23] title["app/main.py"] *}
+{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
и это будет работать корректно вместе со всеми другими *операциями пути*, добавленными через `app.include_router()`.
}
```
-/// note | Ð\92нимание
+/// note | Ð\97амеÑ\82ка
Обратите внимание, что хотя параметр `item` был объявлен таким же способом, как и раньше, теперь предполагается, что он находится внутри тела с ключом `item`.
q: str | None = None
```
-Или в Python 3.9:
-
-```Python
-q: Union[str, None] = None
-```
-
Например:
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
например так:
-{* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *}
+{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
## Поддержка редактора кода везде { #editor-support-everywhere }
В этом случае вы принимаете любой `dict`, пока у него есть ключи типа `int` со значениями типа `float`:
-{* ../../docs_src/body_nested_models/tutorial009_py39.py hl[7] *}
+{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
/// tip | Совет
* Проведёт валидацию данных.
* Если данные некорректны, вернёт понятную и наглядную ошибку, указывающую, где именно и что было некорректно.
* Передаст полученные данные в параметр `item`.
- * Поскольку внутри функции вы объявили его с типом `Item`, у вас будет поддержка со стороны редактора кода (автозавершение и т. п.) для всех атрибутов и их типов.
+ * Поскольку внутри функции вы объявили его с типом `Item`, у вас будет поддержка со стороны редактора кода (автозавершение и т.п.) для всех атрибутов и их типов.
* Сгенерирует определения <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> для вашей модели; вы можете использовать их и в других местах, если это имеет смысл для вашего проекта.
* Эти схемы будут частью сгенерированной схемы OpenAPI и будут использоваться автоматической документацией <abbr title="User Interfaces - Пользовательские интерфейсы">UIs</abbr>.
Параметры функции будут распознаны следующим образом:
* Если параметр также объявлен в **пути**, он будет использоваться как path-параметр.
-* Если параметр имеет **скалярный тип** (например, `int`, `float`, `str`, `bool` и т. п.), он будет интерпретирован как параметр **запроса**.
+* Если параметр имеет **скалярный тип** (например, `int`, `float`, `str`, `bool` и т.п.), он будет интерпретирован как параметр **запроса**.
* Если параметр объявлен как тип **модели Pydantic**, он будет интерпретирован как **тело** запроса.
/// note | Заметка
FastAPI понимает, что значение `q` не является обязательным из-за значения по умолчанию `= None`.
-Аннотации типов `str | None` (Python 3.10+) или `Union` в `Union[str, None]` (Python 3.9+) не используются FastAPI для определения обязательности; он узнает, что параметр не обязателен, потому что у него есть значение по умолчанию `= None`.
+Аннотация типов `str | None` не используется FastAPI для определения обязательности; он узнает, что параметр не обязателен, потому что у него есть значение по умолчанию `= None`.
Но добавление аннотаций типов позволит вашему редактору кода лучше вас поддерживать и обнаруживать ошибки.
В некоторых случаях (не особо часто встречающихся) вам может понадобиться **ограничить** cookies, которые вы хотите получать.
-Теперь ваш API сам решает, <abbr title="Это шутка, на всякий случай. Это не имеет никакого отношения к согласию на использование cookie, но забавно, что даже API теперь может отклонять несчастные cookies. Съешьте печеньку. 🍪">принимать ли cookies</abbr>. 🤪🍪
+Теперь у вашего API есть возможность контролировать своё <dfn title="Это шутка, на всякий случай. Это не имеет никакого отношения к согласию на использование cookie, но забавно, что даже API теперь может отклонять несчастные cookies. Съешьте печеньку. 🍪">согласие на использование cookie</dfn>. 🤪🍪
Вы можете сконфигурировать Pydantic-модель так, чтобы запретить (`forbid`) любые дополнительные (`extra`) поля:
Если клиент попробует отправить **дополнительные cookies**, то в ответ он получит **ошибку**.
-Бедные баннеры cookies, они всеми силами пытаются получить ваше согласие — и всё ради того, чтобы <abbr title="Это ещё одна шутка. Не обращайте на меня внимания. Выпейте кофе со своей печенькой. ☕">API его отклонил</abbr>. 🍪
+Бедные баннеры cookies, они всеми силами пытаются получить ваше согласие — и всё ради того, чтобы <dfn title="Это ещё одна шутка. Не обращайте на меня внимания. Выпейте кофе со своей печенькой. ☕">API его отклонил</dfn>. 🍪
-Например, если клиент попытается отправить cookie `santa_tracker` со значением `good-list-please`, то в ответ он получит **ошибку**, сообщающую ему, что cookie `santa_tracker` <abbr title="Санта не одобряет пропажу печенья. 🎅 Ладно, больше никаких шуток про печенье.">не разрешён</abbr>:
+Например, если клиент попытается отправить cookie `santa_tracker` со значением `good-list-please`, то в ответ он получит **ошибку**, сообщающую ему, что `santa_tracker` <dfn title="Санта не одобряет пропажу печенья. 🎅 Ладно, больше никаких шуток про печенье.">cookie не разрешён</dfn>:
```json
{
## Заключение { #summary }
-Вы можете использовать **Pydantic-модели** для объявления <abbr title="Съешьте последнюю печеньку, прежде чем уйти. 🍪">**cookies**</abbr> в **FastAPI**. 😎
+Вы можете использовать **Pydantic-модели** для объявления <dfn title="Съешьте последнюю печеньку, прежде чем уйти. 🍪">**cookies**</dfn> в **FastAPI**. 😎
/// info | Дополнительная информация
-Имейте в виду, что, поскольку браузеры обрабатывают cookies особым образом и «за кулисами», они не позволяют JavaScript просто так получать к ним доступ.
+Имейте в виду, что, поскольку **браузеры обрабатывают cookies** особым образом и «за кулисами», они **не** позволяют **JavaScript** просто так получать к ним доступ.
-Если вы откроете интерфейс документации API на `/docs`, вы сможете увидеть документацию по cookies для ваших операций пути.
+Если вы откроете **интерфейс документации API** на `/docs`, вы сможете увидеть **документацию** по cookies для ваших *операции пути*.
-Но даже если вы заполните данные и нажмёте «Execute», поскольку UI документации работает с JavaScript, cookies отправлены не будут, и вы увидите сообщение об ошибке, как будто вы не указали никаких значений.
+Но даже если вы **заполните данные** и нажмёте «Execute», поскольку UI документации работает с **JavaScript**, cookies отправлены не будут, и вы увидите сообщение об **ошибке**, как будто вы не указали никаких значений.
///
* Отдельных HTTP-методов (`POST`, `PUT`) или всех вместе, используя `"*"`.
* Отдельных HTTP-заголовков или всех вместе, используя `"*"`.
-{* ../../docs_src/cors/tutorial001_py39.py hl[2,6:11,13:19] *}
+{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
`CORSMiddleware` использует "запрещающие" значения по умолчанию, поэтому вам нужно явным образом разрешить использование отдельных источников, методов или заголовков, чтобы браузеры могли использовать их в кросс-доменном контексте.
## Больше информации { #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 | Технические детали
В вашем FastAPI приложении, импортируйте и вызовите `uvicorn` напрямую:
-{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *}
+{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
### Описание `__name__ == "__main__"` { #about-name-main }
то встроенная переменная `__name__`, автоматически создаваемая Python в вашем файле, будет иметь значение строкового типа `"__main__"`.
-Тогда выполнится условие и эта часть кода:
+Тогда эта часть кода:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```Python
from myapp import app
-# Some more code
+# Еще немного кода
```
то автоматическая создаваемая внутри файла `myapp.py` переменная `__name__` будет иметь значение отличающееся от `"__main__"`.
---
-Если используете Pycharm, вы можете выполнить следующие шаги:
+Если используете PyCharm, вы можете выполнить следующие шаги:
* Открыть "Run" меню.
* Выбрать опцию "Debug...".
## `dict` из предыдущего примера { #a-dict-from-the-previous-example }
-В предыдущем примере мы возвращали `dict` из нашей зависимости:
+В предыдущем примере мы возвращали `dict` из нашей зависимости («dependable»):
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
Это относится и к вызываемым объектам без параметров. Работа с ними происходит точно так же, как и для *функций-обработчиков пути* без параметров.
-Теперь мы можем изменить зависимость `common_parameters`, указанную выше, на класс `CommonQueryParams`:
+Теперь мы можем изменить зависимость («dependable») `common_parameters`, указанную выше, на класс `CommonQueryParams`:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
Обратите внимание, что в приведенном выше коде мы два раза пишем `CommonQueryParams`:
-//// tab | Python 3.9+
+//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.9+ non-Annotated
+//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
В этом случае первый `CommonQueryParams`, в:
-//// tab | Python 3.9+
+//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, ...
////
-//// tab | Python 3.9+ non-Annotated
+//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
На самом деле можно написать просто:
-//// tab | Python 3.9+
+//// tab | Python 3.10+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
////
-//// tab | Python 3.9+ non-Annotated
+//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Но вы видите, что здесь мы имеем некоторое повторение кода, дважды написав `CommonQueryParams`:
-//// tab | Python 3.9+
+//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.9+ non-Annotated
+//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Вместо того чтобы писать:
-//// tab | Python 3.9+
+//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.9+ non-Annotated
+//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
...следует написать:
-//// tab | Python 3.9+
+//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends()]
////
-//// tab | Python 3.9+ non-Annotated
+//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Это должен быть `list` состоящий из `Depends()`:
-{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[19] *}
+{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
Зависимости из dependencies выполнятся так же, как и обычные зависимости. Но их значения (если они были) не будут переданы в *функцию операции пути*.
/// info | Примечание
-В этом примере мы используем выдуманные пользовательские заголовки `X-Key` и `X-Token`.
+В этом примере мы используем выдуманные пользовательские HTTP-заголовки `X-Key` и `X-Token`.
Но в реальных проектах, при внедрении системы безопасности, вы получите больше пользы используя интегрированные [средства защиты (следующая глава)](../security/index.md){.internal-link target=_blank}.
### Требования к зависимостям { #dependency-requirements }
-Они могут объявлять требования к запросу (например заголовки) или другие подзависимости:
+Они могут объявлять требования к запросу (например HTTP-заголовки) или другие подзависимости:
-{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[8,13] *}
+{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
### Вызов исключений { #raise-exceptions }
Зависимости из dependencies могут вызывать исключения с помощью `raise`, как и обычные зависимости:
-{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[10,15] *}
+{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
### Возвращаемые значения { #return-values }
Таким образом, вы можете переиспользовать обычную зависимость (возвращающую значение), которую вы уже используете где-то в другом месте, и хотя значение не будет использоваться, зависимость будет выполнена:
-{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[11,16] *}
+{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
## Зависимости для группы *операций путей* { #dependencies-for-a-group-of-path-operations }
# Зависимости с yield { #dependencies-with-yield }
-FastAPI поддерживает зависимости, которые выполняют некоторые <abbr title='иногда также называемые "exit code", "cleanup code", "teardown code", "closing code", "context manager exit code" и т.п.'>дополнительные шаги после завершения</abbr>.
+FastAPI поддерживает зависимости, которые выполняют некоторые <dfn title='иногда также называемые "код выхода", "код очистки", "код завершения", "код закрытия", "код выхода менеджера контекста" и т.п.'>дополнительные шаги после завершения</dfn>.
Для этого используйте `yield` вместо `return`, а дополнительные шаги (код) напишите после него.
Перед созданием ответа будет выполнен только код до и включая оператор `yield`:
-{* ../../docs_src/dependencies/tutorial007_py39.py hl[2:4] *}
+{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
Значение, полученное из `yield`, внедряется в *операции пути* и другие зависимости:
-{* ../../docs_src/dependencies/tutorial007_py39.py hl[4] *}
+{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
Код, следующий за оператором `yield`, выполняется после ответа:
-{* ../../docs_src/dependencies/tutorial007_py39.py hl[5:6] *}
+{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
/// tip | Подсказка
Точно так же можно использовать `finally`, чтобы убедиться, что обязательные шаги при выходе выполнены независимо от того, было ли исключение или нет.
-{* ../../docs_src/dependencies/tutorial007_py39.py hl[3,5] *}
+{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
## Подзависимости с `yield` { #sub-dependencies-with-yield }
Например, `dependency_c` может зависеть от `dependency_b`, а `dependency_b` — от `dependency_a`:
-{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[6,14,22] *}
+{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
И все они могут использовать `yield`.
И, в свою очередь, `dependency_b` нуждается в том, чтобы значение из `dependency_a` (здесь `dep_a`) было доступно для её кода выхода.
-{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[18:19,26:27] *}
+{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
Точно так же можно иметь часть зависимостей с `yield`, часть — с `return`, и какие-то из них могут зависеть друг от друга.
///
-{* ../../docs_src/dependencies/tutorial008b_an_py39.py hl[18:22,31] *}
+{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
Если вы хотите перехватывать исключения и формировать на их основе пользовательский ответ, создайте [Пользовательский обработчик исключений](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
Если вы ловите исключение с помощью `except` в зависимости с `yield` и не вызываете его снова (или не вызываете новое исключение), FastAPI не сможет заметить, что было исключение — так же, как это происходит в обычном Python:
-{* ../../docs_src/dependencies/tutorial008c_an_py39.py hl[15:16] *}
+{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
В этом случае клиент получит *HTTP 500 Internal Server Error*, как и должно быть, поскольку мы не вызываем `HTTPException` или что-то подобное, но на сервере **не будет никаких логов** или других указаний на то, какая была ошибка. 😱
Вы можете повторно вызвать то же самое исключение с помощью `raise`:
-{* ../../docs_src/dependencies/tutorial008d_an_py39.py hl[17] *}
+{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
Теперь клиент получит тот же *HTTP 500 Internal Server Error*, но на сервере в логах будет наше пользовательское `InternalError`. 😎
Но если вы знаете, что не будете использовать зависимость после возврата из *функции-обработчика пути*, вы можете использовать `Depends(scope="function")`, чтобы сообщить FastAPI, что он должен закрыть зависимость после возврата из *функции-обработчика пути*, но **до того**, как **ответ будет отправлен**.
-{* ../../docs_src/dependencies/tutorial008e_an_py39.py hl[12,16] *}
+{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
`Depends()` принимает параметр `scope`, который может быть:
Их также можно использовать внутри зависимостей **FastAPI** с `yield`, применяя операторы
`with` или `async with` внутри функции зависимости:
-{* ../../docs_src/dependencies/tutorial010_py39.py hl[1:9,13] *}
+{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
/// tip | Подсказка
В этом случае они будут применяться ко всем *операциям пути* в приложении:
-{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[17] *}
+{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
Все способы [добавления `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` для целой группы *операций пути*.
# Зависимости { #dependencies }
-**FastAPI** имеет очень мощную, но интуитивную систему **<abbr title="также известно как: компоненты, ресурсы, провайдеры, сервисы, внедряемые зависимости">Инъекция зависимостей</abbr>**.
+**FastAPI** имеет очень мощную, но интуитивную систему **<dfn title="также известно как: компоненты, ресурсы, провайдеры, сервисы, внедряемые зависимости">Инъекция зависимостей</dfn>**.
Она спроектирована так, чтобы быть очень простой в использовании и облегчать любому разработчику интеграцию других компонентов с **FastAPI**.
Если одна из ваших зависимостей объявлена несколько раз для одной и той же *функции операции пути*, например, несколько зависимостей имеют общую подзависимость, **FastAPI** будет знать, что вызывать эту подзависимость нужно только один раз за запрос.
-При этом возвращаемое значение будет сохранено в <abbr title="Система для хранения значений, сгенерированных компьютером, для их повторного использования вместо повторного вычисления.">"кэш"</abbr> и будет передано всем "зависимым" функциям, которые нуждаются в нем внутри этого конкретного запроса, вместо того, чтобы вызывать зависимость несколько раз для одного и того же запроса.
+При этом возвращаемое значение будет сохранено в <dfn title="Вспомогательная система для хранения вычисленных/сгенерированных значений, чтобы переиспользовать их вместо повторного вычисления.">«кэш»</dfn> и будет передано всем "зависимым" функциям, которые нуждаются в нем внутри этого конкретного запроса, вместо того, чтобы вызывать зависимость несколько раз для одного и того же запроса.
В расширенном сценарии, когда вы знаете, что вам нужно, чтобы зависимость вызывалась на каждом шаге (возможно, несколько раз) в одном и том же запросе, вместо использования "кэшированного" значения, вы можете установить параметр `use_cache=False` при использовании `Depends`:
-//// tab | Python 3.9+
+//// tab | Python 3.10+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
////
-//// tab | Python 3.9+ без Annotated
+//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Представим, что у вас есть база данных `fake_db`, которая принимает только JSON-совместимые данные.
-Например, он не принимает объекты `datetime`, так как они не совместимы с JSON.
+Например, она не принимает объекты `datetime`, так как они не совместимы с JSON.
-В таком случае объект `datetime` следует преобразовать в строку соответствующую <a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">формату ISO</a>.
+В таком случае объект `datetime` следует преобразовать в строку, соответствующую <a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">формату ISO</a>.
Точно так же эта база данных не может принять Pydantic-модель (объект с атрибутами), а только `dict`.
Таким же образом вы можете объявлять HTTP-ответы, возвращающие списки объектов.
-Для этого используйте стандартный `typing.List` в Python (или просто `list` в Python 3.9 и выше):
+Для этого используйте стандартный `list` в Python:
-{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *}
+{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
## Ответ с произвольным `dict` { #response-with-arbitrary-dict }
Это полезно, если вы заранее не знаете корректных названий полей/атрибутов (которые будут нужны при использовании Pydantic-модели).
-В этом случае вы можете использовать `typing.Dict` (или просто `dict` в Python 3.9 и выше):
+В этом случае вы можете использовать `dict`:
-{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *}
+{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
## Резюме { #recap }
Самый простой файл FastAPI может выглядеть так:
-{* ../../docs_src/first_steps/tutorial001_py39.py *}
+{* ../../docs_src/first_steps/tutorial001_py310.py *}
Скопируйте это в файл `main.py`.
### Шаг 1: импортируйте `FastAPI` { #step-1-import-fastapi }
-{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *}
+{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
`FastAPI` — это класс на Python, который предоставляет всю функциональность для вашего API.
### Шаг 2: создайте экземпляр `FastAPI` { #step-2-create-a-fastapi-instance }
-{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *}
+{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
Здесь переменная `app` будет экземпляром класса `FastAPI`.
#### Определите *декоратор операции пути (path operation decorator)* { #define-a-path-operation-decorator }
-{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *}
+{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
`@app.get("/")` сообщает **FastAPI**, что функция прямо под ним отвечает за обработку запросов, поступающих:
* по пути `/`
-* с использованием <abbr title="метод HTTP GET"><code>get</code> операции</abbr>
+* с использованием <dfn title="метод HTTP GET"><code>get</code> операции</dfn>
/// info | Информация о `@decorator`
* **операция**: `get`.
* **функция**: функция ниже «декоратора» (ниже `@app.get("/")`).
-{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *}
+{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
Это функция на Python.
Вы также можете определить её как обычную функцию вместо `async def`:
-{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *}
+{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
/// note | Примечание
### Шаг 5: верните содержимое { #step-5-return-the-content }
-{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *}
+{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
Вы можете вернуть `dict`, `list`, отдельные значения `str`, `int` и т.д.
* Элемент, к которому клиент пытался получить доступ, не существует.
* и т.д.
-В таких случаях обычно возвращается **HTTP-код статуса ответа** в диапазоне **400** (от 400 до 499).
+В таких случаях обычно возвращают **HTTP статус-код** в диапазоне **400** (от 400 до 499).
Они похожи на двухсотые HTTP статус-коды (от 200 до 299), которые означают, что запрос обработан успешно.
### Импортируйте `HTTPException` { #import-httpexception }
-{* ../../docs_src/handling_errors/tutorial001_py39.py hl[1] *}
+{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
### Вызовите `HTTPException` в своем коде { #raise-an-httpexception-in-your-code }
В данном примере, когда клиент запрашивает элемент по несуществующему ID, возникает исключение со статус-кодом `404`:
-{* ../../docs_src/handling_errors/tutorial001_py39.py hl[11] *}
+{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
### Возвращаемый ответ { #the-resulting-response }
## Добавление пользовательских заголовков { #add-custom-headers }
-В некоторых ситуациях полезно иметь возможность добавлять пользовательские заголовки к ошибке HTTP. Например, для некоторых типов безопасности.
+В некоторых ситуациях полезно иметь возможность добавлять пользовательские HTTP-заголовки к ошибке HTTP. Например, для некоторых типов безопасности.
Скорее всего, вам не потребуется использовать его непосредственно в коде.
Но в случае, если это необходимо для продвинутого сценария, можно добавить пользовательские заголовки:
-{* ../../docs_src/handling_errors/tutorial002_py39.py hl[14] *}
+{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
## Установка пользовательских обработчиков исключений { #install-custom-exception-handlers }
Можно добавить собственный обработчик исключений с помощью `@app.exception_handler()`:
-{* ../../docs_src/handling_errors/tutorial003_py39.py hl[5:7,13:18,24] *}
+{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
Здесь, если запросить `/unicorns/yolo`, то *операция пути* вызовет `UnicornException`.
Обработчик исключения получит объект `Request` и исключение.
-{* ../../docs_src/handling_errors/tutorial004_py39.py hl[2,14:19] *}
+{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
Теперь, если перейти к `/items/foo`, то вместо стандартной JSON-ошибки с:
Например, для этих ошибок можно вернуть обычный текстовый ответ вместо JSON:
-{* ../../docs_src/handling_errors/tutorial004_py39.py hl[3:4,9:11,25] *}
+{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
/// note | Технические детали
Вы можете использовать его при разработке приложения для регистрации тела и его отладки, возврата пользователю и т.д.
-{* ../../docs_src/handling_errors/tutorial005_py39.py hl[14] *}
+{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
Теперь попробуйте отправить недействительный элемент, например:
Если вы хотите использовать исключение вместе с теми же обработчиками исключений по умолчанию из **FastAPI**, вы можете импортировать и повторно использовать обработчики исключений по умолчанию из `fastapi.exception_handlers`:
-{* ../../docs_src/handling_errors/tutorial006_py39.py hl[2:5,15,21] *}
+{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
В этом примере вы просто `выводите в терминал` ошибку с очень выразительным сообщением, но идея вам понятна. Вы можете использовать исключение, а затем просто повторно использовать стандартные обработчики исключений.
Вы можете задать их следующим образом:
-{* ../../docs_src/metadata/tutorial001_py39.py hl[3:16, 19:32] *}
+{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
/// tip | Подсказка
К примеру:
-{* ../../docs_src/metadata/tutorial001_1_py39.py hl[31] *}
+{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
## Метаданные для тегов { #metadata-for-tags }
Создайте метаданные для ваших тегов и передайте их в параметре `openapi_tags`:
-{* ../../docs_src/metadata/tutorial004_py39.py hl[3:16,18] *}
+{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
Помните, что вы можете использовать Markdown внутри описания, к примеру "login" будет отображен жирным шрифтом (**login**) и "fancy" будет отображаться курсивом (_fancy_).
Используйте параметр `tags` с вашими *операциями пути* (и `APIRouter`ами), чтобы присвоить им различные теги:
-{* ../../docs_src/metadata/tutorial004_py39.py hl[21,26] *}
+{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
/// info | Дополнительная информация
К примеру, чтобы задать её отображение по адресу `/api/v1/openapi.json`:
-{* ../../docs_src/metadata/tutorial002_py39.py hl[3] *}
+{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
Если вы хотите отключить схему OpenAPI полностью, вы можете задать `openapi_url=None`, это также отключит пользовательские интерфейсы документации, которые её используют.
К примеру, чтобы задать отображение Swagger UI по адресу `/documentation` и отключить ReDoc:
-{* ../../docs_src/metadata/tutorial003_py39.py hl[3] *}
+{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}
# Middleware (Промежуточный слой) { #middleware }
-Вы можете добавить промежуточный слой (middleware) в **FastAPI** приложение.
-
-"Middleware" это функция, которая выполняется с каждым запросом до его обработки какой-либо конкретной *операцией пути*.
-А также с каждым ответом перед его возвращением.
+Вы можете добавить middleware (промежуточный слой) в **FastAPI** приложение.
+"Middleware" - это функция, которая выполняется с каждым **запросом** до его обработки какой-либо конкретной *операцией пути*. А также с каждым **ответом** перед его возвращением.
* Она принимает каждый поступающий **запрос**.
* Может что-то сделать с этим **запросом** или выполнить любой нужный код.
## Создание middleware { #create-a-middleware }
-Для создания middleware используйте декоратор `@app.middleware("http")`.
+Для создания middleware используйте декоратор `@app.middleware("http")` поверх функции.
Функция middleware получает:
-* `request` (объект запроса).
+* `request`.
* Функцию `call_next`, которая получает `request` в качестве параметра.
* Эта функция передаёт `request` соответствующей *операции пути*.
- * Затем она возвращает ответ `response`, сгенерированный *операцией пути*.
-* Также имеется возможность видоизменить `response`, перед тем как его вернуть.
+ * Затем она возвращает `response`, сгенерированный соответствующей *операцией пути*.
+* Также имеется возможность видоизменить `response` перед тем как его вернуть.
-{* ../../docs_src/middleware/tutorial001_py39.py hl[8:9,11,14] *}
+{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
-/// tip | Ð\9fÑ\80имеÑ\87ание
+/// tip | СовеÑ\82
-Имейте в виду, что можно добавлять свои собственные заголовки <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">при помощи префикса 'X-'</a>.
+Имейте в виду, что можно добавлять проприетарные HTTP-заголовки <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">с префиксом `X-`</a>.
-Ð\95Ñ\81ли же вÑ\8b Ñ\85оÑ\82иÑ\82е добавиÑ\82Ñ\8c Ñ\81обÑ\81Ñ\82веннÑ\8bе заголовки, коÑ\82оÑ\80Ñ\8bе клиенÑ\82 Ñ\81можеÑ\82 Ñ\83видеÑ\82Ñ\8c в бÑ\80аÑ\83зеÑ\80е, Ñ\82о вам поÑ\82Ñ\80ебÑ\83еÑ\82Ñ\81Ñ\8f добавиÑ\82Ñ\8c иÑ\85 в наÑ\81Ñ\82Ñ\80ойки CORS ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}), иÑ\81полÑ\8cзÑ\83Ñ\8f паÑ\80амеÑ\82Ñ\80 `expose_headers`, Ñ\81м. докÑ\83менÑ\82аÑ\86иÑ\8e <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">Starlette's CORS docs</a>.
+Ð\9dо еÑ\81ли вÑ\8b Ñ\85оÑ\82иÑ\82е, Ñ\87Ñ\82обÑ\8b клиенÑ\82 в бÑ\80аÑ\83зеÑ\80е мог видеÑ\82Ñ\8c ваÑ\88и полÑ\8cзоваÑ\82елÑ\8cÑ\81кие заголовки, необÑ\85одимо добавиÑ\82Ñ\8c иÑ\85 в наÑ\81Ñ\82Ñ\80ойки CORS ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}), иÑ\81полÑ\8cзÑ\83Ñ\8f паÑ\80амеÑ\82Ñ\80 `expose_headers`, опиÑ\81аннÑ\8bй в <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">докÑ\83менÑ\82аÑ\86ии по CORS Starlette</a>.
///
### До и после `response` { #before-and-after-the-response }
-Вы можете добавить код, использующий `request` до передачи его какой-либо *операции пути*.
+Вы можете добавить код, использующий `request`, до передачи его какой-либо *операции пути*.
А также после формирования `response`, до того, как вы его вернёте.
Например, вы можете добавить собственный заголовок `X-Process-Time`, содержащий время в секундах, необходимое для обработки запроса и генерации ответа:
-{* ../../docs_src/middleware/tutorial001_py39.py hl[10,12:13] *}
+{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
-/// tip | Ð\9fÑ\80имеÑ\87ание
+/// tip | СовеÑ\82
-Ð\9cÑ\8b иÑ\81полÑ\8cзÑ\83ем <a href="https://docs.python.org/3/library/time.html#time.perf_counter" class="external-link" target="_blank">`time.perf_counter()`</a> вмеÑ\81Ñ\82о `time.time()` длÑ\8f обеÑ\81пеÑ\87ениÑ\8f болÑ\8cÑ\88ей Ñ\82оÑ\87ноÑ\81Ñ\82и наÑ\88иÑ\85 пÑ\80имеÑ\80ов. 🤓
+Ð\9cÑ\8b иÑ\81полÑ\8cзÑ\83ем <a href="https://docs.python.org/3/library/time.html#time.perf_counter" class="external-link" target="_blank">`time.perf_counter()`</a> вмеÑ\81Ñ\82о `time.time()` длÑ\8f обеÑ\81пеÑ\87ениÑ\8f болÑ\8cÑ\88ей Ñ\82оÑ\87ноÑ\81Ñ\82и в Ñ\82акиÑ\85 Ñ\81лÑ\83Ñ\87аÑ\8fÑ\85. 🤓
///
О других 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.
/// warning | Внимание
-Ð\9fомниÑ\82е, Ñ\87Ñ\82о Ñ\8dÑ\82и паÑ\80амеÑ\82Ñ\80Ñ\8b пеÑ\80едаÑ\8eÑ\82Ñ\81Ñ\8f непоÑ\81Ñ\80едÑ\81Ñ\82венно *декоÑ\80аÑ\82оÑ\80Ñ\83 опеÑ\80аÑ\86ий пÑ\83Ñ\82и*, а не ваÑ\88ей *Ñ\84Ñ\83нкÑ\86ии-обÑ\80абоÑ\82Ñ\87икÑ\83 опеÑ\80аÑ\86ий пÑ\83Ñ\82и*.
+Помните, что эти параметры передаются непосредственно *декоратору операций пути*, а не вашей *функции-обработчику пути*.
///
**FastAPI** поддерживает это так же, как и в случае с обычными строками:
-{* ../../docs_src/path_operation_configuration/tutorial002b_py39.py hl[1,8:10,13,18] *}
+{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
## Краткое и развёрнутое содержание { #summary-and-description }
## Описание из строк документации { #description-from-docstring }
-Так как описания обычно длинные и содержат много строк, вы можете объявить описание *операции пути* в функции <abbr title="многострочный текст, первое выражение внутри функции (не присвоенный какой-либо переменной), используемый для документации">строки документации</abbr> и **FastAPI** прочитает её отсюда.
+Так как описания обычно длинные и содержат много строк, вы можете объявить описание *операции пути* в <dfn title="многострочная строка, первое выражение внутри функции (не присвоенное какой-либо переменной), используемая для документации">строке документации</dfn> функции, и **FastAPI** прочитает её оттуда.
Вы можете использовать <a href="https://en.wikipedia.org/wiki/Markdown" class="external-link" target="_blank">Markdown</a> в строке документации, и он будет интерпретирован и отображён корректно (с учетом отступа в строке документации).
## Обозначение *операции пути* как устаревшей { #deprecate-a-path-operation }
-Если вам необходимо пометить *операцию пути* как <abbr title="устаревшее, не рекомендовано к использованию">устаревшую</abbr>, при этом не удаляя её, передайте параметр `deprecated`:
+Если вам необходимо пометить *операцию пути* как <dfn title="устаревшая, рекомендуется не использовать">устаревшую</dfn>, при этом не удаляя её, передайте параметр `deprecated`:
-{* ../../docs_src/path_operation_configuration/tutorial006_py39.py hl[16] *}
+{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
Он будет четко помечен как устаревший в интерактивной документации:
И если вам больше ничего не нужно указывать для этого параметра, то нет необходимости использовать `Query`.
-Ð\9dо вам по-пÑ\80ежнемÑ\83 нÑ\83жно иÑ\81полÑ\8cзоваÑ\82Ñ\8c `Path` длÑ\8f path-паÑ\80амеÑ\82Ñ\80а `item_id`. Ð\98 еÑ\81ли по какой-либо пÑ\80иÑ\87ине вÑ\8b не Ñ\85оÑ\82иÑ\82е иÑ\81полÑ\8cзоваÑ\82Ñ\8c `Annotated`, Ñ\82о могÑ\83Ñ\82 возникнÑ\83Ñ\82Ñ\8c неболÑ\8cÑ\88ие Ñ\81ложноÑ\81Ñ\82и.
+Ð\9dо вам по-пÑ\80ежнемÑ\83 нÑ\83жно иÑ\81полÑ\8cзоваÑ\82Ñ\8c `Path` длÑ\8f path-паÑ\80амеÑ\82Ñ\80а `item_id`. Ð\98 по какой-либо пÑ\80иÑ\87ине вÑ\8b не Ñ\85оÑ\82иÑ\82е иÑ\81полÑ\8cзоваÑ\82Ñ\8c `Annotated`.
Если вы поместите параметр со значением по умолчанию перед другим параметром, у которого нет значения по умолчанию, то Python укажет на ошибку.
Поэтому вы можете определить функцию так:
-{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *}
+{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
Но имейте в виду, что если вы используете `Annotated`, вы не столкнётесь с этой проблемой, так как вы не используете значения по умолчанию параметров функции для `Query()` или `Path()`.
-{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *}
+{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
## Задайте нужный вам порядок параметров, полезные приёмы { #order-the-parameters-as-you-need-tricks }
Python не будет ничего делать с `*`, но он будет знать, что все следующие параметры являются именованными аргументами (парами ключ-значение), также известными как <abbr title="От: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>, даже если у них нет значений по умолчанию.
-{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *}
+{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
### Лучше с `Annotated` { #better-with-annotated }
Имейте в виду, что если вы используете `Annotated`, то, поскольку вы не используете значений по умолчанию для параметров функции, у вас не возникнет подобной проблемы и вам, вероятно, не придётся использовать `*`.
-{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py hl[10] *}
+{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
## Валидация числовых данных: больше или равно { #number-validations-greater-than-or-equal }
В этом примере при указании `ge=1`, параметр `item_id` должен быть целым числом "`g`reater than or `e`qual" — больше или равно `1`.
-{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *}
+{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
## Валидация числовых данных: больше и меньше или равно { #number-validations-greater-than-and-less-than-or-equal }
* `gt`: больше (`g`reater `t`han)
* `le`: меньше или равно (`l`ess than or `e`qual)
-{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py hl[10] *}
+{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
## Валидация числовых данных: числа с плавающей точкой, больше и меньше { #number-validations-floats-greater-than-and-less-than }
То же самое справедливо и для <abbr title="less than – меньше чем"><code>lt</code></abbr>.
-{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py hl[13] *}
+{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
## Резюме { #recap }
Вы можете определить "параметры" или "переменные" пути, используя синтаксис форматированных строк Python:
-{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}
+{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
Значение параметра пути `item_id` будет передано в функцию в качестве аргумента `item_id`.
Вы можете объявить тип параметра пути в функции, используя стандартные аннотации типов Python:
-{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}
+{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
Здесь, `item_id` объявлен типом `int`.
///
-## <abbr title="также известное как: сериализация, парсинг, маршаллинг">Преобразование</abbr> данных { #data-conversion }
+## <dfn title="также известно как: сериализация, парсинг, маршаллинг">Преобразование</dfn> данных { #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>, то увидите ответ:
Обратите внимание на значение `3`, которое получила (и вернула) функция. Это целочисленный Python `int`, а не строка `"3"`.
-Используя такое объявление типов, **FastAPI** выполняет автоматический <abbr title="преобразование строк из HTTP-запроса в типы данных Python">"парсинг"</abbr> запросов.
+Используя такое объявление типов, **FastAPI** выполняет автоматический HTTP-запрос <dfn title="преобразование строки, которая приходит из HTTP-запроса, в данные Python">"парсинг"</dfn>.
///
Поскольку *операции пути* выполняются в порядке их объявления, необходимо, чтобы путь для `/users/me` был объявлен раньше, чем путь для `/users/{user_id}`:
-{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}
+{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
Иначе путь для `/users/{user_id}` также будет соответствовать `/users/me`, "подразумевая", что он получает параметр `user_id` со значением `"me"`.
Аналогично, вы не можете переопределить операцию с путем:
-{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}
+{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
Первый будет выполняться всегда, так как путь совпадает первым.
Затем создайте атрибуты класса с фиксированными допустимыми значениями:
-{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}
+{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
/// tip | Подсказка
-Если интересно, то "AlexNet", "ResNet" и "LeNet" - это названия <abbr title="Технически, архитектуры моделей глубокого обучения">моделей</abbr> Машинного обучения.
+Если интересно, то "AlexNet", "ResNet" и "LeNet" - это названия <dfn title="Технически, архитектуры моделей глубокого обучения">моделей</dfn> Машинного обучения.
///
Определите *параметр пути*, используя в аннотации типа класс перечисления (`ModelName`), созданный ранее:
-{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}
+{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
### Проверьте документацию { #check-the-docs }
Вы можете сравнить это значение с *элементом перечисления* класса `ModelName`:
-{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}
+{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
#### Получение *значения перечисления* { #get-the-enumeration-value }
Можно получить фактическое значение (в данном случае - `str`) с помощью `model_name.value` или в общем случае `your_enum_member.value`:
-{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}
+{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
/// tip | Подсказка
Они будут преобразованы в соответствующие значения (в данном случае - строки) перед их возвратом клиенту:
-{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
-Ð\92Ñ\8b оÑ\82пÑ\80авиÑ\82е клиенÑ\82Ñ\83 такой JSON-ответ:
+{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
+Ð\9dа Ñ\81Ñ\82оÑ\80оне клиенÑ\82а вÑ\8b полÑ\83Ñ\87иÑ\82е такой JSON-ответ:
```JSON
{
Можете использовать так:
-{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}
+{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
/// tip | Подсказка
Используя **FastAPI** вместе со стандартными объявлениями типов Python (короткими и интуитивно понятными), вы получаете:
* Поддержку редактора кода (проверку ошибок, автозавершение и т.п.)
-* "<abbr title="преобразование строк из HTTP-запроса в типы данных Python">Парсинг</abbr>" данных
+* "<dfn title="преобразование строки, которая приходит из HTTP-запроса, в данные Python">Парсинг</dfn>" данных
* Валидацию данных
* Аннотации API и автоматическую документацию
/// note | Заметка
-Этот функционал доступен с версии `0.115.0`. 🤓
+Это поддерживается начиная с версии FastAPI `0.115.0`. 🤓
///
У нас была такая аннотация типа:
-//// tab | Python 3.10+
-
```Python
q: str | None = None
```
-////
-
-//// tab | Python 3.9+
-
-```Python
-q: Union[str, None] = None
-```
-
-////
-
Мы «обернём» это в `Annotated`, и получится:
-//// tab | Python 3.10+
-
```Python
q: Annotated[str | None] = None
```
-////
-
-//// tab | Python 3.9+
-
-```Python
-q: Annotated[Union[str, None]] = None
-```
-
-////
-
Обе версии означают одно и то же: `q` — параметр, который может быть `str` или `None`, и по умолчанию равен `None`.
А теперь к самому интересному. 🎉
## Альтернатива (устаревшее): `Query` как значение по умолчанию { #alternative-old-query-as-the-default-value }
-В предыдущих версиях FastAPI (до <abbr title="до 2023-03">0.95.0</abbr>) требовалось использовать `Query` как значение по умолчанию для параметра вместо помещения его в `Annotated`. Скорее всего вы ещё встретите такой код, поэтому поясню.
+В предыдущих версиях FastAPI (до <dfn title="до 2023-03">0.95.0</dfn>) требовалось использовать `Query` как значение по умолчанию для параметра вместо помещения его в `Annotated`. Скорее всего вы ещё встретите такой код, поэтому поясню.
/// tip | Подсказка
## Регулярные выражения { #add-regular-expressions }
-Вы можете определить <abbr title="Регулярное выражение (regex, regexp) - это последовательность символов, задающая шаблон поиска для строк.">регулярное выражение</abbr> `pattern`, которому должен соответствовать параметр:
+Вы можете определить <dfn title="Регулярное выражение (regex, regexp) — это последовательность символов, задающая шаблон поиска для строк.">регулярное выражение</dfn> `pattern`, которому должен соответствовать параметр:
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
Допустим, вы хотите объявить, что query-параметр `q` должен иметь `min_length` равный `3` и значение по умолчанию `"fixedquery"`:
-{* ../../docs_src/query_params_str_validations/tutorial005_an_py39.py hl[9] *}
+{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
/// note | Примечание
Поэтому, когда вам нужно объявить значение как обязательное при использовании `Query`, просто не указывайте значение по умолчанию:
-{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
+{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
### Обязательный, но может быть `None` { #required-can-be-none }
Можно также определить значение по умолчанию как `list`, если ничего не передано:
-{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *}
+{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
Если вы перейдёте по адресу:
Можно использовать `list` напрямую вместо `list[str]`:
-{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
+{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
/// note | Примечание
Предположим, этот параметр вам больше не нравится.
-Его нужно оставить на какое‑то время, так как клиенты его используют, но вы хотите, чтобы в документации он явно отображался как <abbr title="устаревший, не рекомендуется использовать">устаревший</abbr>.
+Его нужно оставить на какое‑то время, так как клиенты его используют, но вы хотите, чтобы в документации он явно отображался как <dfn title="устаревший, не рекомендуется использовать">устаревший</dfn>.
Тогда передайте параметр `deprecated=True` в `Query`:
///
-Например, эта кастомная проверка убеждается, что ID элемента начинается с `isbn-` для номера книги <abbr title="ISBN означает International Standard Book Number - Международный стандартный книжный номер">ISBN</abbr> или с `imdb-` для ID URL фильма на <abbr title="IMDB (Internet Movie Database) - веб‑сайт с информацией о фильмах">IMDB</abbr>:
+Например, эта кастомная проверка убеждается, что ID элемента начинается с `isbn-` для номера книги <abbr title="International Standard Book Number - Международный стандартный книжный номер">ISBN</abbr> или с `imdb-` для ID URL фильма на <abbr title="Internet Movie Database - Интернетная база данных фильмов: веб‑сайт с информацией о фильмах">IMDB</abbr>:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
#### Случайный элемент { #a-random-item }
-С помощью `data.items()` мы получаем <abbr title="Объект, по которому можно итерироваться циклом for, например список, множество и т. п.">итерируемый объект</abbr> с кортежами, содержащими ключ и значение для каждого элемента словаря.
+С помощью `data.items()` мы получаем <dfn title="Объект, по которому можно итерироваться циклом for, например список, множество и т.п.">итерируемый объект</dfn> с кортежами, содержащими ключ и значение для каждого элемента словаря.
Мы превращаем этот итерируемый объект в обычный `list` через `list(data.items())`.
Когда вы объявляете параметры функции, которые не являются параметрами пути, они автоматически интерпретируются как "query"-параметры.
-{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *}
+{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
Query-параметры представляют из себя набор пар ключ-значение, которые идут после знака `?` в URL-адресе, разделенные символами `&`.
Все те же правила, которые применяются к path-параметрам, также применяются и query-параметрам:
* Поддержка от редактора кода (очевидно)
-* <abbr title="преобразование строки, полученной из HTTP запроса в Python данные">"Парсинг"</abbr> данных
+* <dfn title="преобразование строки, полученной из HTTP-запроса в данные Python">"Парсинг"</dfn> данных
* Проверка на соответствие данных (Валидация)
* Автоматическая документация
## Обязательные query-параметры { #required-query-parameters }
-Когда вы объявляете значение по умолчанию для параметра, который не является path-параметром (в этом разделе, мы пока что познакомились только с path-параметрами), то он не является обязательным.
+Когда вы объявляете значение по умолчанию для параметра, который не является path-параметром (в этом разделе мы пока что рассмотрели только query-параметры), то он не является обязательным.
Если вы не хотите задавать конкретное значение, но хотите сделать параметр необязательным, вы можете установить значение по умолчанию равным `None`.
Но если вы хотите сделать query-параметр обязательным, вы можете просто не указывать значение по умолчанию:
-{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *}
+{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
Здесь параметр запроса `needy` является обязательным параметром с типом данных `str`.
Импортируйте `File` и `UploadFile` из модуля `fastapi`:
-{* ../../docs_src/request_files/tutorial001_an_py39.py hl[3] *}
+{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
## Определите параметры `File` { #define-file-parameters }
Создайте параметры `File` так же, как вы это делаете для `Body` или `Form`:
-{* ../../docs_src/request_files/tutorial001_an_py39.py hl[9] *}
+{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
/// info | Дополнительная информация
Определите параметр файла с типом `UploadFile`:
-{* ../../docs_src/request_files/tutorial001_an_py39.py hl[14] *}
+{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
Использование `UploadFile` имеет ряд преимуществ перед `bytes`:
Но когда форма включает файлы, она кодируется как 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 – Сеть разработчиков Mozilla">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>.
///
Вы также можете использовать `File()` вместе с `UploadFile`, например, для установки дополнительных метаданных:
-{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}
+{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
## Загрузка нескольких файлов { #multiple-file-uploads }
Для этого необходимо объявить список `bytes` или `UploadFile`:
-{* ../../docs_src/request_files/tutorial002_an_py39.py hl[10,15] *}
+{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
Вы получите, как и было объявлено, список `list` из `bytes` или `UploadFile`.
Так же, как и раньше, вы можете использовать `File()` для задания дополнительных параметров, даже для `UploadFile`:
-{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}
+{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
## Резюме { #recap }
Вам просто нужно объявить **Pydantic-модель** с полями, которые вы хотите получить как **поля формы**, а затем объявить параметр как `Form`:
-{* ../../docs_src/request_form_models/tutorial001_an_py39.py hl[9:11,15] *}
+{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
**FastAPI** **извлечёт** данные для **каждого поля** из **данных формы** в запросе и выдаст вам объявленную Pydantic-модель.
Вы можете сконфигурировать Pydantic-модель так, чтобы запретить (`forbid`) все дополнительные (`extra`) поля:
-{* ../../docs_src/request_form_models/tutorial002_an_py39.py hl[12] *}
+{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
Если клиент попробует отправить дополнительные данные, то в ответ он получит **ошибку**.
## Импортируйте `File` и `Form` { #import-file-and-form }
-{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[3] *}
+{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
## Определите параметры `File` и `Form` { #define-file-and-form-parameters }
Создайте параметры файла и формы таким же образом, как для `Body` или `Query`:
-{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[10:12] *}
+{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
Файлы и поля формы будут загружены в виде данных формы, и вы получите файлы и поля формы.
Импортируйте `Form` из `fastapi`:
-{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[3] *}
+{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
## Определение параметров `Form` { #define-form-parameters }
Создайте параметры формы так же, как это делается для `Body` или `Query`:
-{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[9] *}
+{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
-Например, в одном из способов использования спецификации OAuth2 (называемом «потоком пароля») требуется отправить `username` и `password` в виде полей формы.
+Например, в одном из способов использования спецификации OAuth2 (называемом «password flow» - аутентификация по паролю) требуется отправить `username` и `password` в виде полей формы.
-<abbr title="specification – спецификация">spec</abbr> требует, чтобы поля были строго названы `username` и `password` и отправлялись как поля формы, а не JSON.
+<dfn title="спецификация">спецификация</dfn> требует, чтобы поля были строго названы `username` и `password` и отправлялись как поля формы, а не JSON.
С помощью `Form` вы можете объявить те же настройки, что и с `Body` (и `Query`, `Path`, `Cookie`), включая валидацию, примеры, псевдоним (например, `user-name` вместо `username`) и т.д.
Но когда форма содержит файлы, она кодируется как `multipart/form-data`. О работе с файлами вы прочтёте в следующей главе.
-Если вы хотите узнать больше про эти кодировки и поля формы, обратитесь к <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>.
+Если вы хотите узнать больше про эти кодировки и поля формы, обратитесь к <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>.
///
Самый распространённый случай — [возвращать Response напрямую, как описано далее в разделах документации для продвинутых](../advanced/response-directly.md){.internal-link target=_blank}.
-{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *}
+{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
Этот простой случай обрабатывается FastAPI автоматически, потому что аннотация возвращаемого типа — это класс (или подкласс) `Response`.
Вы также можете использовать подкласс `Response` в аннотации типа:
-{* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *}
+{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
Это тоже сработает, так как `RedirectResponse` — подкласс `Response`, и FastAPI автоматически обработает этот простой случай.
Но когда вы возвращаете произвольный объект, не являющийся валидным типом Pydantic (например, объект базы данных), и аннотируете его таким образом в функции, FastAPI попытается создать модель ответа Pydantic из этой аннотации типа и потерпит неудачу.
-То же произойдёт, если у вас будет что-то вроде <abbr title='Объединение нескольких типов означает «любой из этих типов».'>union</abbr> разных типов, где один или несколько не являются валидными типами Pydantic, например, это приведёт к ошибке 💥:
+То же произойдёт, если у вас будет что-то вроде <dfn title="Объединение нескольких типов означает «любой из этих типов».">union</dfn> разных типов, где один или несколько не являются валидными типами Pydantic, например, это приведёт к ошибке 💥:
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
* `@app.delete()`
* и других.
-{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
+{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
/// note | Примечание
/// tip | Подсказка
-Чтобы узнать больше о 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>.
+Чтобы узнать больше о 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>.
///
Рассмотрим предыдущий пример еще раз:
-{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
+{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
`201` – это код статуса "Создано".
Для удобства вы можете использовать переменные из `fastapi.status`.
-{* ../../docs_src/response_status_code/tutorial002_py39.py hl[1,6] *}
+{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
Они содержат те же числовые значения, но позволяют использовать автозавершение редактора кода для выбора кода статуса:
/// note | Технические детали
-Вы также можете использовать `from starlette import status` вместо `from fastapi import status`.
+Вы также можете использовать `from starlette import status`.
**FastAPI** позволяет использовать как `starlette.status`, так и `fastapi.status` исключительно для удобства разработчиков. Но поставляется fastapi.status непосредственно из Starlette.
Когда вы делаете это, примеры становятся частью внутренней **JSON Schema** для данных тела запроса.
-Тем не менее, на <abbr title="2023-08-26">момент написания этого</abbr> Swagger UI, инструмент, отвечающий за отображение UI документации, не поддерживает показ нескольких примеров для данных в **JSON Schema**. Но ниже есть обходной путь.
+Тем не менее, на <dfn title="2023-08-26">момент написания этого</dfn> Swagger UI, инструмент, отвечающий за отображение UI документации, не поддерживает показ нескольких примеров для данных в **JSON Schema**. Но ниже есть обходной путь.
### Специфические для OpenAPI `examples` { #openapi-specific-examples }
Скопируйте пример в файл `main.py`:
-{* ../../docs_src/security/tutorial001_an_py39.py *}
+{* ../../docs_src/security/tutorial001_an_py310.py *}
## Запуск { #run-it }
При создании экземпляра класса `OAuth2PasswordBearer` мы передаем параметр `tokenUrl`. Этот параметр содержит URL, который клиент (фронтенд, работающий в браузере пользователя) будет использовать для отправки `username` и `password`, чтобы получить токен.
-{* ../../docs_src/security/tutorial001_an_py39.py hl[8] *}
+{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}
/// tip | Подсказка
Теперь вы можете передать `oauth2_scheme` как зависимость с `Depends`.
-{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
+{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
Эта зависимость предоставит `str`, который будет присвоен параметру `token` *функции-обработчика пути*.
В предыдущей главе система безопасности (основанная на системе внедрения зависимостей) передавала *функции-обработчику пути* `token` типа `str`:
-{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
+{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
Но это всё ещё не слишком полезно.
-# Ð\9dаÑ\81Ñ\82Ñ\80ойка авÑ\82оÑ\80изаÑ\86ии { #security }
+# Ð\91езопаÑ\81ноÑ\81Ñ\82Ñ\8c { #security }
Существует множество способов обеспечения безопасности, аутентификации и авторизации.
-Обычно эта тема является достаточно сложной и трудной.
+Обычно эта тема является достаточно сложной и «трудной».
-Ð\92о многиÑ\85 Ñ\84Ñ\80еймвоÑ\80каÑ\85 и Ñ\81иÑ\81Ñ\82емаÑ\85 Ñ\82олÑ\8cко Ñ\80абоÑ\82а Ñ\81 опÑ\80еделением доÑ\81Ñ\82Ñ\83пов к пÑ\80иложениÑ\8e и аÑ\83Ñ\82енÑ\82иÑ\84икаÑ\86ией Ñ\82Ñ\80ебÑ\83еÑ\82 знаÑ\87иÑ\82елÑ\8cнÑ\8bÑ\85 заÑ\82Ñ\80аÑ\82 Ñ\83Ñ\81илий и напиÑ\81аниÑ\8f множеÑ\81Ñ\82ва кода (во многиÑ\85 Ñ\81лÑ\83Ñ\87аÑ\8fÑ\85 его обÑ\8aÑ\91м можеÑ\82 Ñ\81оÑ\81Ñ\82авлÑ\8fÑ\82Ñ\8c более 50% от всего написанного кода).
+Ð\92о многиÑ\85 Ñ\84Ñ\80еймвоÑ\80каÑ\85 и Ñ\81иÑ\81Ñ\82емаÑ\85 Ñ\82олÑ\8cко Ñ\80абоÑ\82а Ñ\81 безопаÑ\81ноÑ\81Ñ\82Ñ\8cÑ\8e и аÑ\83Ñ\82енÑ\82иÑ\84икаÑ\86ией Ñ\82Ñ\80ебÑ\83еÑ\82 знаÑ\87иÑ\82елÑ\8cнÑ\8bÑ\85 заÑ\82Ñ\80аÑ\82 Ñ\83Ñ\81илий и напиÑ\81аниÑ\8f множеÑ\81Ñ\82ва кода (во многиÑ\85 Ñ\81лÑ\83Ñ\87аÑ\8fÑ\85 его обÑ\8aÑ\91м можеÑ\82 Ñ\81оÑ\81Ñ\82авлÑ\8fÑ\82Ñ\8c 50% или более от всего написанного кода).
-**FastAPI** предоставляет несколько инструментов, которые помогут вам настроить **Авторизацию** легко, быстро, стандартным способом, без необходимости изучать все её тонкости.
+**FastAPI** предоставляет несколько инструментов, которые помогут вам работать с **безопасностью** легко, быстро, стандартным способом, без необходимости изучать и разбираться во всех спецификациях по безопасности.
-Но сначала давайте рассмотрим некоторые небольшие концепции.
+Но сначала давайте рассмотрим несколько небольших концепций.
-## Ð\9aÑ\83да-Ñ\82о Ñ\82оÑ\80опиÑ\88Ñ\8cÑ\81Ñ\8f? { #in-a-hurry }
+## Ð\9dеÑ\82 вÑ\80емени? { #in-a-hurry }
-Ð\95Ñ\81ли вам не нÑ\83жна инÑ\84оÑ\80маÑ\86иÑ\8f о какиÑ\85-либо из Ñ\81ледÑ\83Ñ\8eÑ\89иÑ\85 Ñ\82еÑ\80минов и вам пÑ\80оÑ\81Ñ\82о нÑ\83жно добавиÑ\82Ñ\8c заÑ\89иÑ\82Ñ\83 Ñ\81 аÑ\83Ñ\82енÑ\82иÑ\84икаÑ\86ией на оÑ\81нове логина и паÑ\80олÑ\8f *пÑ\80Ñ\8fмо Ñ\81ейÑ\87аÑ\81*, переходите к следующим главам.
+Ð\95Ñ\81ли вам не важнÑ\8b какие-либо из Ñ\8dÑ\82иÑ\85 Ñ\82еÑ\80минов и вам пÑ\80оÑ\81Ñ\82о нÑ\83жно добавиÑ\82Ñ\8c заÑ\89иÑ\82Ñ\83 Ñ\81 аÑ\83Ñ\82енÑ\82иÑ\84икаÑ\86ией на оÑ\81нове имени полÑ\8cзоваÑ\82елÑ\8f и паÑ\80олÑ\8f пÑ\80Ñ\8fмо Ñ\81ейÑ\87аÑ\81, переходите к следующим главам.
## OAuth2 { #oauth2 }
-OAuth2 - это протокол, который определяет несколько способов обработки аутентификации и авторизации.
+OAuth2 - это спецификация, которая определяет несколько способов обработки аутентификации и авторизации.
-Ð\9eн доволÑ\8cно обÑ\88иÑ\80ен и оÑ\85ваÑ\82Ñ\8bваеÑ\82 несколько сложных вариантов использования.
+ÐÑ\82о доволÑ\8cно обÑ\88иÑ\80наÑ\8f Ñ\81пеÑ\86иÑ\84икаÑ\86иÑ\8f, оÑ\85ваÑ\82Ñ\8bваÑ\8eÑ\89аÑ\8f несколько сложных вариантов использования.
-OAuth2 включает в себя способы аутентификации с использованием "третьей стороны".
+Она включает способы аутентификации с использованием «третьей стороны».
-ÐÑ\82о Ñ\82о, Ñ\87Ñ\82о иÑ\81полÑ\8cзÑ\83Ñ\8eÑ\82 под Ñ\81обой вÑ\81е кнопки "вÑ\85од Ñ\81 помоÑ\89Ñ\8cÑ\8e Facebook, Google, X (Twitter), GitHub" на Ñ\81Ñ\82Ñ\80аниÑ\86аÑ\85 авÑ\82оÑ\80изаÑ\86ии.
+Ð\98менно Ñ\8dÑ\82о иÑ\81полÑ\8cзÑ\83еÑ\82Ñ\81Ñ\8f во вÑ\81еÑ\85 Ñ\81иÑ\81Ñ\82емаÑ\85 Ñ\81 кнопками «войÑ\82и Ñ\81 помоÑ\89Ñ\8cÑ\8e Facebook, Google, X (Twitter), GitHub».
### OAuth 1 { #oauth-1 }
-Ранее использовался протокол OAuth 1, который сильно отличается от OAuth2 и является более сложным, поскольку он включал прямые описания того, как шифровать сообщение.
+Ранее использовался OAuth 1, который сильно отличается от OAuth2 и является более сложным, поскольку он включал прямые спецификации того, как шифровать обмен данными.
В настоящее время он не очень популярен и не используется.
-OAuth2 не указывает, как шифровать сообщение, он ожидает, что ваше приложение будет обслуживаться по протоколу HTTPS.
+OAuth2 не указывает, как шифровать обмен данными, он ожидает, что ваше приложение будет обслуживаться по протоколу HTTPS.
/// tip | Подсказка
-В разделе **Развертывание** вы увидите как настроить протокол HTTPS бесплатно, используя Traefik и Let's Encrypt.
+В разделе о **развертывании** вы увидите, как настроить HTTPS бесплатно, используя Traefik и Let's Encrypt.
///
## OpenID Connect { #openid-connect }
-OpenID Connect - это еще один протокол, основанный на **OAuth2**.
+OpenID Connect — это ещё одна спецификация, основанная на **OAuth2**.
-Он просто расширяет OAuth2, уточняя некоторые вещи, не имеющие однозначного определения в OAuth2, в попытке сделать его более совместимым.
+Она просто расширяет OAuth2, уточняя некоторые вещи, которые относительно неоднозначны в OAuth2, стараясь сделать его более совместимым.
-Например, для входа в Google используется OpenID Connect (который под собой использует OAuth2).
+Например, для входа в Google используется OpenID Connect (который под капотом использует OAuth2).
Но вход в Facebook не поддерживает OpenID Connect. У него есть собственная вариация OAuth2.
-### OpenID (не "OpenID Connect") { #openid-not-openid-connect }
+### OpenID (не «OpenID Connect») { #openid-not-openid-connect }
-Также ранее использовался стандарт "OpenID", который пытался решить ту же проблему, что и **OpenID Connect**, но не был основан на OAuth2.
+Также ранее использовалась спецификация «OpenID», которая пыталась решить ту же задачу, что и **OpenID Connect**, но не была основана на OAuth2.
Таким образом, это была полноценная дополнительная система.
**FastAPI** основан на **OpenAPI**.
-Это то, что делает возможным наличие множества автоматических интерактивных интерфейсов документирования, сгенерированного кода и т.д.
+Это то, что делает возможными несколько автоматических интерактивных интерфейсов документации, генерацию кода и т.д.
-Ð\92 OpenAPI еÑ\81Ñ\82Ñ\8c Ñ\81поÑ\81об иÑ\81полÑ\8cзоваÑ\82Ñ\8c неÑ\81колÑ\8cко "Ñ\81Ñ\85ем" безопасности.
+Ð\92 OpenAPI еÑ\81Ñ\82Ñ\8c Ñ\81поÑ\81об опÑ\80еделиÑ\82Ñ\8c неÑ\81колÑ\8cко «Ñ\81Ñ\85ем» безопасности.
-Таким обÑ\80азом, вÑ\8b можеÑ\82е воÑ\81полÑ\8cзоваÑ\82Ñ\8cÑ\81Ñ\8f пÑ\80еимÑ\83Ñ\89еÑ\81Ñ\82вами Ð\92Ñ\81еÑ\85 Ñ\8dÑ\82иÑ\85 Ñ\81Ñ\82андаÑ\80Ñ\82нÑ\8bÑ\85 инÑ\81Ñ\82Ñ\80Ñ\83менÑ\82ов, включая интерактивные системы документирования.
+Ð\98Ñ\81полÑ\8cзÑ\83Ñ\8f иÑ\85, вÑ\8b можеÑ\82е воÑ\81полÑ\8cзоваÑ\82Ñ\8cÑ\81Ñ\8f пÑ\80еимÑ\83Ñ\89еÑ\81Ñ\82вами вÑ\81еÑ\85 Ñ\8dÑ\82иÑ\85 инÑ\81Ñ\82Ñ\80Ñ\83менÑ\82ов, оÑ\81нованнÑ\8bÑ\85 на Ñ\81Ñ\82андаÑ\80Ñ\82аÑ\85, включая интерактивные системы документирования.
-OpenAPI можеÑ\82 иÑ\81полÑ\8cзоваÑ\82Ñ\8c Ñ\81ледÑ\83Ñ\8eÑ\89ие Ñ\81Ñ\85емÑ\8b авÑ\82оÑ\80изаÑ\86ии:
+OpenAPI опÑ\80еделÑ\8fеÑ\82 Ñ\81ледÑ\83Ñ\8eÑ\89ие Ñ\81Ñ\85емÑ\8b безопаÑ\81ноÑ\81Ñ\82и:
-* `apiKey`: Ñ\83никалÑ\8cнÑ\8bй иденÑ\82иÑ\84икаÑ\82оÑ\80 длÑ\8f пÑ\80иложениÑ\8f, коÑ\82оÑ\80Ñ\8bй можеÑ\82 бÑ\8bÑ\82Ñ\8c полÑ\83Ñ\87ен из:
- * Ð\9fаÑ\80амеÑ\82Ñ\80ов запроса.
- * Заголовка.
- * Cookies.
-* `http`: стандартные системы аутентификации по протоколу HTTP, включая:
- * `bearer`: заголовок `Authorization` со значением `Bearer {уникальный токен}`. Это унаследовано от OAuth2.
- * Базовая аутентификация по протоколу HTTP.
+* `apiKey`: Ñ\81пеÑ\86иÑ\84иÑ\87нÑ\8bй длÑ\8f пÑ\80иложениÑ\8f клÑ\8eÑ\87, коÑ\82оÑ\80Ñ\8bй можеÑ\82 поÑ\81Ñ\82Ñ\83паÑ\82Ñ\8c из:
+ * паÑ\80амеÑ\82Ñ\80а запроса.
+ * HTTP-заголовка.
+ * cookie.
+* `http`: стандартные системы аутентификации по HTTP, включая:
+ * `bearer`: HTTP-заголовок `Authorization` со значением `Bearer ` плюс токен. Это унаследовано от OAuth2.
+ * Базовая аутентификация по HTTP.
* HTTP Digest и т.д.
-* `oauth2`: все способы обеспечения безопасности OAuth2 называемые "потоки" (англ. "flows").
- * Некоторые из этих "потоков" подходят для реализации аутентификации через сторонний сервис использующий OAuth 2.0 (например, Google, Facebook, X (Twitter), GitHub и т.д.):
+* `oauth2`: все способы OAuth2 для обеспечения безопасности (называются «потоками»).
+ * Несколько из этих «потоков» подходят для построения провайдера аутентификации OAuth 2.0 (например, Google, Facebook, X (Twitter), GitHub и т.д.):
* `implicit`
* `clientCredentials`
* `authorizationCode`
- * Но есть один конкретный "поток", который может быть идеально использован для обработки аутентификации непосредственно в том же приложении:
- * `password`: в некоторых следующих главах будут рассмотрены примеры этого.
-* `openIdConnect`: способ определить, как автоматически обнаруживать данные аутентификации OAuth2.
- * Ðто автоматическое обнаружение определено в спецификации OpenID Connect.
+ * Но есть один конкретный «поток», который можно идеально использовать для обработки аутентификации непосредственно в этом же приложении:
+ * `password`: в некоторых следующих главах будут приведены примеры.
+* `openIdConnect`: имеет способ определить, как автоматически обнаруживать данные аутентификации OAuth2.
+ * Ð\98менно Ñ\8dто автоматическое обнаружение определено в спецификации OpenID Connect.
/// tip | Подсказка
-Интеграция сторонних сервисов для аутентификации/авторизации таких как Google, Facebook, X (Twitter), GitHub и т.д. осуществляется достаточно легко.
+Интеграция сторонних провайдеров аутентификации/авторизации, таких как Google, Facebook, X (Twitter), GitHub и т.д., также возможна и относительно проста.
-Самой Ñ\81ложной пÑ\80облемой Ñ\8fвлÑ\8fеÑ\82Ñ\81Ñ\8f Ñ\81оздание Ñ\82акого пÑ\80овайдеÑ\80а аÑ\83Ñ\82енÑ\82иÑ\84икаÑ\86ии/авÑ\82оÑ\80изаÑ\86ии, но **FastAPI** пÑ\80едоÑ\81Ñ\82авлÑ\8fеÑ\82 вам инÑ\81Ñ\82Ñ\80Ñ\83менÑ\82Ñ\8b, позволÑ\8fÑ\8eÑ\89ие легко Ñ\8dÑ\82о Ñ\81делаÑ\82Ñ\8c, вÑ\8bполнÑ\8fÑ\8f пÑ\80и Ñ\8dÑ\82ом вÑ\81Ñ\8e Ñ\82Ñ\8fжелую работу за вас.
+Самой Ñ\81ложной задаÑ\87ей Ñ\8fвлÑ\8fеÑ\82Ñ\81Ñ\8f Ñ\81оздание Ñ\82акого пÑ\80овайдеÑ\80а аÑ\83Ñ\82енÑ\82иÑ\84икаÑ\86ии/авÑ\82оÑ\80изаÑ\86ии, но **FastAPI** пÑ\80едоÑ\81Ñ\82авлÑ\8fеÑ\82 вам инÑ\81Ñ\82Ñ\80Ñ\83менÑ\82Ñ\8b, позволÑ\8fÑ\8eÑ\89ие легко Ñ\8dÑ\82о Ñ\81делаÑ\82Ñ\8c, вÑ\8bполнÑ\8fÑ\8f пÑ\80и Ñ\8dÑ\82ом вÑ\81Ñ\8e Ñ\82Ñ\8fжÑ\91лую работу за вас.
///
-## Ð\9fÑ\80еимÑ\83Ñ\89еÑ\81Ñ\82ва **FastAPI** { #fastapi-utilities }
+## Ð\98нÑ\81Ñ\82Ñ\80Ñ\83менÑ\82Ñ\8b **FastAPI** { #fastapi-utilities }
-Fast API предоставляет несколько инструментов для каждой из этих схем безопасности в модуле `fastapi.security`, которые упрощают использование этих механизмов безопасности.
+FastAPI предоставляет несколько инструментов для каждой из этих схем безопасности в модуле `fastapi.security`, которые упрощают использование этих механизмов безопасности.
-Ð\92 Ñ\81ледÑ\83Ñ\8eÑ\89иÑ\85 главаÑ\85 вÑ\8b Ñ\83видиÑ\82е, как обезопаÑ\81иÑ\82Ñ\8c Ñ\81вой API, используя инструменты, предоставляемые **FastAPI**.
+Ð\92 Ñ\81ледÑ\83Ñ\8eÑ\89иÑ\85 главаÑ\85 вÑ\8b Ñ\83видиÑ\82е, как добавиÑ\82Ñ\8c безопаÑ\81ноÑ\81Ñ\82Ñ\8c в ваÑ\88 API, используя инструменты, предоставляемые **FastAPI**.
-И вы также увидите, как он автоматически интегрируется в систему интерактивной документации.
+И вы также увидите, как это автоматически интегрируется в систему интерактивной документации.
# OAuth2 с паролем (и хешированием), Bearer с JWT-токенами { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens }
-Теперь, когда у нас определен процесс обеспечения безопасности, давайте сделаем приложение действительно безопасным, используя токены <abbr title="JSON Web Tokens – веб‑токены JSON">JWT</abbr> и безопасное хеширование паролей.
+Теперь, когда у нас определен процесс обеспечения безопасности, давайте сделаем приложение действительно безопасным, используя токены <abbr title="JSON Web Tokens - веб‑токены JSON">JWT</abbr> и безопасное хеширование паролей.
Этот код можно реально использовать в своем приложении, сохранять хэши паролей в базе данных и т.д.
-Мы продолжим разбираться, начиная с того места, на котором остановились в предыдущей главе.
+Мы продолжим разбираться, начиная с того места, на котором остановились в предыдущей главе, и расширим его.
## Про JWT { #about-jwt }
</div>
/// info | Дополнительная информация
+
Если вы планируете использовать алгоритмы цифровой подписи, такие как RSA или ECDSA, вам следует установить зависимость библиотеки криптографии `pyjwt[crypto]`.
Подробнее об этом можно прочитать в <a href="https://pyjwt.readthedocs.io/en/latest/installation.html" class="external-link" target="_blank">документации по установке PyJWT</a>.
+
///
## Хеширование паролей { #password-hashing }
</div>
/// tip | Подсказка
+
С помощью `pwdlib` можно даже настроить его на чтение паролей, созданных **Django**, плагином безопасности **Flask** или многими другими библиотеками.
Таким образом, вы сможете, например, совместно использовать одни и те же данные из приложения Django в базе данных с приложением FastAPI. Или постепенно мигрировать Django-приложение, используя ту же базу данных.
При этом пользователи смогут одновременно входить в систему как из приложения Django, так и из приложения **FastAPI**.
+
///
## Хеширование и проверка паролей { #hash-and-verify-the-passwords }
Создайте экземпляр PasswordHash с рекомендованными настройками — он будет использоваться для хэширования и проверки паролей.
/// tip | Подсказка
+
pwdlib также поддерживает алгоритм хеширования bcrypt, но не включает устаревшие алгоритмы — для работы с устаревшими хэшами рекомендуется использовать библиотеку passlib.
Например, вы можете использовать ее для чтения и проверки паролей, сгенерированных другой системой (например, Django), но хэшировать все новые пароли другим алгоритмом, например Argon2 или Bcrypt.
И при этом быть совместимым со всеми этими системами.
+
///
Создайте служебную функцию для хэширования пароля, поступающего от пользователя.
И еще одну — для аутентификации и возврата пользователя.
-{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,56:57,60:61,70:76] *}
+{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *}
+
+Когда `authenticate_user` вызывается с именем пользователя, которого нет в базе данных, мы все равно запускаем `verify_password` с использованием фиктивного хэша.
+
+Это гарантирует, что эндпоинт отвечает примерно за одно и то же время вне зависимости от того, существует имя пользователя или нет, предотвращая тайминговые атаки (атака по времени), с помощью которых можно было бы перечислять существующие имена пользователей.
/// note | Технические детали
+
Если проверить новую (фальшивую) базу данных `fake_users_db`, то можно увидеть, как теперь выглядит хэшированный пароль: `"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`.
+
///
## Работа с JWT токенами { #handle-jwt-tokens }
Создайте служебную функцию для генерации нового токена доступа.
-{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,79:87] *}
+{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *}
## Обновление зависимостей { #update-the-dependencies }
Если токен недействителен, то сразу же верните HTTP-ошибку.
-{* ../../docs_src/security/tutorial004_an_py310.py hl[90:107] *}
+{* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *}
## Обновление *операции пути* `/token` { #update-the-token-path-operation }
Создайте реальный токен доступа JWT и верните его
-{* ../../docs_src/security/tutorial004_an_py310.py hl[118:133] *}
+{* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *}
### Технические подробности о JWT ключе `sub` { #technical-details-about-the-jwt-subject-sub }
Password: `secret`
/// check | Проверка
+
Обратите внимание, что нигде в коде не используется открытый текст пароля "`secret`", мы используем только его хэшированную версию.
+
///
<img src="/img/tutorial/security/image08.png">
<img src="/img/tutorial/security/image10.png">
/// note | Техническая информация
+
Обратите внимание на HTTP-заголовок `Authorization`, значение которого начинается с `Bearer `.
+
///
## Продвинутое использование `scopes` { #advanced-usage-with-scopes }
<img src="/img/tutorial/security/image06.png">
-Ð\95Ñ\81ли Ñ\89Ñ\91лкнÑ\83Ñ\82Ñ\8c на знаÑ\87ке замка и вÑ\8bйÑ\82и из Ñ\81иÑ\81Ñ\82емÑ\8b, а заÑ\82ем попÑ\8bÑ\82аÑ\82Ñ\8cÑ\81Ñ\8f выполнить ту же операцию ещё раз, будет выдана ошибка HTTP 401:
+Ð\95Ñ\81ли Ñ\89Ñ\91лкнÑ\83Ñ\82Ñ\8c на знаÑ\87ке замка и вÑ\8bйÑ\82и из Ñ\81иÑ\81Ñ\82емÑ\8b, а заÑ\82ем попÑ\80обоваÑ\82Ñ\8c выполнить ту же операцию ещё раз, будет выдана ошибка HTTP 401:
```JSON
{
/// tip | Подсказка
-Вы можете использовать любую другую библиотеку для работы с SQL или NoSQL базами данных (иногда их называют <abbr title="Object Relational Mapper – Объектно-реляционный маппер: термин для библиотеки, где некоторые классы представляют SQL-таблицы, а экземпляры представляют строки в этих таблицах">"ORMs"</abbr>), FastAPI ничего не навязывает. 😎
+Вы можете использовать любую другую библиотеку для работы с SQL или NoSQL базами данных (иногда их называют <abbr title="Object Relational Mapper - Объектно-реляционный маппер: термин для библиотеки, где некоторые классы представляют SQL-таблицы, а экземпляры представляют строки в этих таблицах">"ORMs"</abbr>), FastAPI ничего не навязывает. 😎
///
</div>
-Если вы перейдёте в UI API `/docs`, вы увидите, что он обновился: теперь при создании героя он не ожидает получить `id` от клиента и т. д.
+Если вы перейдёте в UI API `/docs`, вы увидите, что он обновился: теперь при создании героя он не ожидает получить `id` от клиента и т.д.
<div class="screenshot">
<img src="/img/tutorial/sql-databases/image02.png">
Вы можете использовать <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>. 🚀
* Импортируйте `StaticFiles`.
* "Примонтируйте" экземпляр `StaticFiles()` к определённому пути.
-{* ../../docs_src/static_files/tutorial001_py39.py hl[2,6] *}
+{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *}
/// note | Технические детали
Напишите простое утверждение с `assert` дабы проверить истинность Python-выражения (это тоже стандарт `pytest`).
-{* ../../docs_src/app_testing/tutorial001_py39.py hl[2,12,15:18] *}
+{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *}
/// tip | Подсказка
В файле `main.py` находится Ваше приложение **FastAPI**:
-{* ../../docs_src/app_testing/app_a_py39/main.py *}
+{* ../../docs_src/app_testing/app_a_py310/main.py *}
### Файл тестов { #testing-file }
Так как оба файла находятся в одной директории, для импорта объекта приложения из файла `main` в файл `test_main` Вы можете использовать относительный импорт:
-{* ../../docs_src/app_testing/app_a_py39/test_main.py hl[3] *}
+{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *}
...и писать дальше тесты, как и раньше.
Ещё есть операция `POST`, и она может вернуть несколько ошибок.
-Обе *операции пути* требуют наличия в запросе заголовка `X-Token`.
+Обе *операции пути* требуют наличия в запросе HTTP-заголовка `X-Token`.
{* ../../docs_src/app_testing/app_b_an_py310/main.py *}
* Передаёте *path*-параметры или *query*-параметры, вписав их непосредственно в строку URL.
* Передаёте JSON в теле запроса, передав Python-объект (например: `dict`) через именованный параметр `json`.
* Если же Вам необходимо отправить *форму с данными* вместо JSON, то используйте параметр `data` вместо `json`.
-* Для передачи *заголовков*, передайте объект `dict` через параметр `headers`.
+* Для передачи *HTTP-заголовков*, передайте объект `dict` через параметр `headers`.
* Для передачи *cookies* также передайте `dict`, но через параметр `cookies`.
Для получения дополнительной информации о передаче данных на бэкенд с помощью `httpx` или `TestClient` ознакомьтесь с <a href="https://www.python-httpx.org" class="external-link" target="_blank">документацией HTTPX</a>.
## Создание виртуального окружения { #create-a-virtual-environment }
-Когда вы начинаете работать над Python‑проектом **впервые**, создайте виртуальное окружение **<abbr title="есть и другие опции, но это простой ориентир">внутри вашего проекта</abbr>**.
+Когда вы начинаете работать над Python‑проектом **впервые**, создайте виртуальное окружение **<dfn title="есть и другие опции, но это простой ориентир">внутри вашего проекта</dfn>**.
/// tip | Подсказка
Каждый раз, когда вы устанавливаете **новый пакет** в это окружение, **активируйте** окружение снова.
-Это гарантирует, что если вы используете **программу терминала (<abbr title="command line interface – интерфейс командной строки">CLI</abbr>)**, установленную этим пакетом, вы будете использовать именно ту, что из вашего виртуального окружения, а не какую‑то глобально установленную, возможно другой версии, чем вам нужна.
+Это гарантирует, что если вы используете **программу терминала (<abbr title="command line interface - интерфейс командной строки">CLI</abbr>)**, установленную этим пакетом, вы будете использовать именно ту, что из вашего виртуального окружения, а не какую‑то глобально установленную, возможно другой версии, чем вам нужна.
///