Подробности о синтаксисе `async def` для *функций обработки пути* и некоторые сведения об асинхронном коде, конкурентности и параллелизме.
Здесь приведена подробная информация об использовании синтаксиса `async def` при написании *функций-обработчиков пути*, а также рассмотрены основы асинхронного программирования, конкурентности и параллелизма.
<abbrtitle="too long; didn't read (очень длинный; не прочитал)"><strong>TL;DR:</strong></abbr>
Если вы используете сторонние библиотеки, которые требуют вызова с ключевым словом `await`, как:
Если вы используете сторонние библиотеки, которые требуют вызова с ключевым словом `await`, как, например:
```Python
results = await some_library()
```
Тогда объявляйте свои *функции обработки пути* с использованием `async def`, как:
В этом случае *функции-обработчики пути* необходимо объявлять с использованием синтаксиса `async def`:
```Python hl_lines="2"
@app.get('/')
@ -29,7 +29,7 @@ async def read_results():
---
Если вы используете стороннюю библиотеку, которая взаимодействует с чем-то (база данных, API, файловая система и т. д.) и не поддерживает использование `await`, (это в настоящее время относится к большинству библиотек для работы с базами данных), то объявляйте свои *функции обработки пути* как обычно, просто с `def`, как:
Если вы обращаетесь к сторонней библиотеке, которая с чем-то взаимодействует (с базой данных, API, файловой системой и т. д.), и не имеет поддержки синтаксиса `await` (что относится сейчас к большинству библиотек для работы с базами данных), то объявляйте *функции-обработчики пути* обычным образом с помощью `def`, например:
```Python hl_lines="2"
@app.get('/')
@ -40,7 +40,7 @@ def results():
---
Если вашему приложению (каким-то образом) не нужно взаимодействовать ни с чем и ждать ответа, используйте `async def`, даже если вам не нужно использовать `await` внутри.
Если вашему приложению (странным образом) не нужно ни с чем взаимодействовать и, соответственно, ожидать ответа, используйте `async def`, даже если внутри нет необходимости в `await`.
---
@ -48,7 +48,7 @@ def results():
---
**Примечание**: Вы можете смешивать `def` и `async def` в своих *функциях обработки пути* столько, сколько вам нужно, и определять каждую с использованием наилучшего для вас варианта. FastAPI сделает с ними всё как надо.
**Примечание**: при необходимости можно смешивать `def` и `async def` в *функциях-обработчиках пути* и использовать в каждом случае наиболее подходящий синтаксис. А FastAPI сделает с этим всё, что нужно.
В любом случае, в любой из упомянутых выше ситуаций, FastAPI всё равно будет работать асинхронно и с высокой скоростью.
@ -66,7 +66,7 @@ def results():
## Асинхронный код
Асинхронный код просто значит, что язык 💬 имеет способ сообщить компьютеру / программе 🤖, что в какой-то момент в коде, ему 🤖 нужно будет дождаться, пока *что-то ещё* завершится где-то ещё. Пусть это *что-то ещё* называется "медленный файл" 📝.
Асинхронный код означает, что в языке 💬 есть возможность сообщить машине / программе 🤖, что в определённой точке кода ей 🤖 нужно будет ожидать завершения выполнения *чего-то ещё* в другом месте. Допустим это *что-то ещё* называется "медленный файл" 📝.
Так что, в это время, компьютер может выполнять какую-то другую работу, пока "медленный файл" 📝 заканчивается.
@ -85,13 +85,13 @@ def results():
* ожидание, когда запрос к базе данных вернёт результаты
* и т. д.
Поскольку основное время выполнения тратится на ожидание операций <abbrtitle="Ввод и вывод">I/O</abbr>, их называют операциями, "связанными с вводом-выводом" (<abbrtitle="I/O bound">I/O bound</abbr>).
Поскольку в основном время тратится на ожидание выполнения операций <abbrtitle="Ввода-вывода">I/O</abbr>, их обычно называют операциями, <abbrtitle="I/O bound">ограниченными скоростью ввода-вывода</abbr>.
Это называется "асинхронным", потому что компьютер / программе не нужно "синхронизироваться" с медленной задачей и ожидать момент, когда задача завершится, ничего не делая, чтобы затем взять результат задачи и продолжить работу.
Код называют "асинхронным", потому что компьютеру / программе не требуется "синхронизироваться" с медленной задачей и, будучи в простое, ожидать момента её завершения, с тем чтобы забрать результат и продолжить работу.
Вместо этого, в "асинхронной" системе завершённая задача может чуть-чуть подождать (несколько микросекунд), пока компьютер / программа завершит выполнение того, что она занималась, а потом вернуться, чтобы взять результаты и продолжить их обработку.
Вместо этого в "асинхронной" системе завершённая задача может немного подождать (буквально несколько микросекунд), пока компьютер / программа занимается другими важными вещами, с тем чтобы потом вернуться, забрать результаты выполнения и начать их обрабатывать.
Для "синхронного" (в отличие от "асинхронного") часто используют термин "последовательный", потому что компьютер / программа проходит все шаги последовательно перед переключением на другую задачу, даже если они включают в себя ожидание.
"Синхронное" исполнение (в противовес "асинхронному") также называют <abbrtitle="sequential">"последовательным"</abbr>, потому что компьютер / программа последовательно выполняет все требуемые шаги перед тем, как перейти к следующей задаче, даже если в процессе приходится ждать.
### Конкурентность и бургеры
@ -105,63 +105,65 @@ def results():
### Конкурентные бургеры
Вы идёте с вашей возлюбленной в фастфуд, становитесь в очередь, пока кассир принимает заказы у людей перед вами. 😍
Вы идёте со своей возлюбленной 😍 в фастфуд 🍔 и становитесь в очередь, в это время кассир 💁 принимает заказы у посетителей перед вами.
Кассир что-то говорит повару на кухне, чтобы они знали, что надо приготовить ваши бургеры (даже если они в данный момент готовят бургеры для предыдущих клиентов).
Кассир 💁 что-то говорит поварам на кухне 👨🍳, теперь они знают, какие бургеры нужно будет приготовить 🍔 (но пока они заняты бургерами предыдущих клиентов).
В ожидании еды, вы идёте с вашей возлюбленной выбрать столик, садитесь и долго разговариваете с вашей возлюбленной (поскольку ваши бургеры очень вкусные и их приготовление занимает некоторое время).
В ожидании еды вы идёте со своей возлюбленной 😍 выбрать столик, садитесь и довольно продолжительное время общаетесь 😍 (поскольку ваши бургеры самые навороченные, готовятся они не так быстро ✨🍔✨).
Сидя за столиком с вашей возлюбленной, пока вы ждёте бургеры, вы тратите это время, чтобы восхищаться тем, насколько ваша возлюбленная замечательная, милая и умная ✨😍✨.
Сидя за столиком с возлюбленной 😍 в ожидании бургеров 🍔, вы отлично проводите время, восхищаясь её великолепием, красотой и умом ✨😍✨.
Во время ожидания и разговора с вашей возлюбленной время от времени вы проверяете номер, отображаемый на стойке, чтобы увидеть, не наступило ли ваше ожидание.
Всё ещё ожидая заказ и болтая со своей возлюбленной 😍, время от времени вы проверяете, какой номер горит над прилавком, и не подошла ли уже ваша очередь.
Затем в какой-то момент это наконец ваша очередь. Вы идёте к стойке, забираете свои бургеры и возвращаетесь к столу.
И вот наконец настаёт этот момент, и вы идёте к стойке, чтобы забрать бургеры 🍔 и вернуться за столик.
Прекрасные иллюстрации от <ahref="https://www.instagram.com/ketrinadrawsalot"class="external-link"target="_blank">Кетрины Томпсон</a>. 🎨
Прекрасные иллюстрации от <ahref="https://www.instagram.com/ketrinadrawsalot"class="external-link"target="_blank">Ketrina Thompson</a>. 🎨
///
---
Представьте, что в этой истории вы — компьютер / программа 🤖.
А теперь представьте, что в этой небольшой истории вы компьютер / программа 🤖.
В очереди вы бездействуете 😴, ожидая своей очереди, особо ничего "продуктивного" не делая. Но очередь быстро движется, так как кассир только берёт заказы (а не готовит их), так что это нормально.
В очереди вы просто глазеете по сторонам 😴, ждёте и ничего особо "продуктивного" не делаете. Но очередь движется довольно быстро, поскольку кассир 💁 только принимает заказы (а не занимается приготовлением еды), так что ничего страшного.
Затем, когда наступает ваша очередь, вы делаете настоящую "продуктивную" работу, обрабатываете меню, решаете, что хотите, узнаёте выбор вашей возлюбленной, оплачиваете, проверяете, что даёте правильную сумму или карту, проверяете, правильно ли с вас взяли оплату, содержится ли в заказе всё, что нужно, и так далее.
Но потом, хотя вы всё еще не получили свои бургеры, ваша работа с кассиром "на паузе" ⏸, потому что вы должны ждать 🕙, пока приготовятся ваши бургеры.
И хотя вы всё ещё не получили бургеры 🍔, ваша работа с кассиром 💁 ставится "на паузу" ⏸, поскольку теперь нужно ждать 🕙, когда заказ приготовят.
Но уходя от стойки и садясь за столик с номером вашего заказа, вы можете переключить 🔀 своё внимание на свою возлюбленную, и "работать" ⏯ 🤓 над этим. Таким образом, вы снова делаете что-то очень "продуктивное", как флирт с вашей возлюбленной 😍.
Но отойдя с номерком от прилавка, вы садитесь за столик и можете переключить 🔀 внимание на свою возлюбленную 😍 и "работать" ⏯ 🤓 уже над этим. И вот вы снова очень "продуктивны" 🤓, мило болтаете вдвоём и всё такое 😍.
Кассир 💁 говорит "я закончил делать бургеры" через показ номера вашего заказа на табло, но вы не вскакиваете, как сумасшедший, когда отображаемый номер меняется на номер вашей очереди. Вы знаете, что никто не украдёт у вас бургеры, потому что у вас есть номер вашего заказа, а у других — свои.
Поэтому вы ждёте, пока ваша возлюбленная закончит рассказывать историю (закончите текущую работу ⏯ / задачу в обработке 🤓), мило улыбаетесь и говорите, что идёте забирать бургеры ⏸.
Поэтому вы подождёте, пока возлюбленная 😍 закончит рассказывать историю (закончите текущую работу ⏯ / задачу в обработке 🤓) и мило улыбнувшись, скажете, что идёте забирать заказ ⏸.
Затем вы идёте к стойке 🔀, к первоначальной задаче, которая завершена ⏯, берете бургеры, говорите спасибо и возвращаетесь за столик. Это завершает эту часть / задание взаимодействия с кассиром ⏹. Это, в свою очередь, создаёт новую задачу — "есть бургеры" 🔀 ⏯, но предыдущая — "получить бургеры" завершена ⏹.
И вот вы подходите к стойке 🔀, к первоначальной задаче, которая уже завершена ⏯, берёте бургеры 🍔, говорите спасибо и относите заказ за столик. На этом заканчивается этап / задача взаимодействия с кассой ⏹. В свою очередь порождается задача "поедание бургеров" 🔀 ⏯, но предыдущая ("получение бургеров") завершена ⏹.
### Параллельные бургеры
@ -169,15 +171,19 @@ def results():
Вы идёте с вашей возлюбленной попробовать параллельный фастфуд.
Вы стоите в очереди, пока несколько (скажем, 8) кассиров, которые одновременно являются поварами, принимают заказы у людей перед вами.
Вы становитесь в очередь, пока несколько (пусть будет 8) кассиров, которые по совместительству ещё и повары 👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳, принимают заказы у посетителей перед вами.
Все перед вами ожидают, когда их бургеры будут готовы до того, как они покинут кассу, поскольку каждый из 8 кассиров сразу идет и готовит бургер, прежде чем получить следующий заказ.
При этом клиенты не отходят от стойки и ждут 🕙 получения еды, поскольку каждый из 8 кассиров идёт на кухню готовить бургеры 🍔, а только потом принимает следующий заказ.
Поскольку вы и ваша возлюбленная заняты тем, чтобы никто не встал перед вами и не забрал ваши бургеры, как только они прибудут, вы не можете уделить внимания вашей возлюбленной. 😞
Поскольку вы с возлюбленной 😍 хотите получить заказ вовремя 🕙, и следите за тем, чтобы никто не вклинился в очередь, у вас не получается уделять должного внимание своей даме сердца 😞.
Это "синхронная" работа: вы "синхронизированы" с кассиром/поваром 👨🍳. Вам приходится ждать 🕙 и быть там в точный момент, когда кассир/повар 👨🍳 завершает приготовление бургеров и отдаёт их вам, иначе кто-то может их взять.
Это "синхронная" работа, вы "синхронизированы" с кассиром/поваром 👨🍳. Приходится ждать 🕙 у стойки, когда кассир/повар 👨🍳 закончит делать бургеры 🍔 и вручит вам заказ, иначе его случайно может забрать кто-то другой.
Обменяться словами или пофлиртовать не очень получилось, так как больше времени было потрачено на ожидание 🕙 перед стойкой. 😞
Вам не особо удалось пообщаться, потому что большую часть времени 🕙 пришлось провести у кассы 😞.
/// info | Информация
Прекрасные иллюстрации от <ahref="https://www.instagram.com/ketrinadrawsalot"class="external-link"target="_blank">Кетрины Томпсон</a>. 🎨
Прекрасные иллюстрации от <ahref="https://www.instagram.com/ketrinadrawsalot"class="external-link"target="_blank">Ketrina Thompson</a>. 🎨
///
---
В этом сценарии с параллельными бургерами вы — это компьютер / программа 🤖 с двумя процессорами (вы и ваша возлюбленная), оба долгое время ожидающие 🕙 и посвящающие своё внимание ⏯ быть "ожидающими у кассы" 🕙.
В описанном сценарии вы компьютер / программа 🤖 с двумя исполнителями (вы и ваша возлюбленная 😍), на протяжении долгого времени 🕙 вы оба уделяете всё внимание ⏯ задаче "ждать на кассе".
В этом фастфуд-магазине 8 процессоров (кассиров/поваров). Хотя в магазине конкурентных бургеров могло быть только 2 (один кассир и один повар).
В этом ресторане быстрого питания 8 исполнителей (кассиров/поваров) 👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳. Хотя в бургерной конкурентного типа было всего два (один кассир и один повар) 💁 👨🍳.
Но всё же, окончательный опыт не самый лучший. 😞
@ -235,7 +243,7 @@ def results():
### Заключение о бургерах
В этом сценарии "быстрого питания с бургерами с вашей возлюбленной", так как там много ожидания 🕙, имеет много больше смысла иметь конкурентную систему ⏸🔀⏯.
В нашей истории про поход в фастфуд за бургерами приходится много ждать 🕙, поэтому имеет смысл организовать конкурентную систему ⏸🔀⏯.
И это и есть случай для большинства веб-приложений.
@ -243,21 +251,25 @@ def results():
И потом снова ждёт 🕙, чтобы ответы вернулись.
Это "ожидание" 🕙 измеряется в микросекундах, но в итоге, если всё сложить, это много времени ожидания.
Это ожидание 🕙 измеряется микросекундами, но если всё сложить, то набегает довольно много времени.
Вот почему есть смысл использовать асинхронное ⏸🔀⏯ программирование при построении веб-API.
Большинство популярных фреймворков (включая Flask и Django) создавались до появления в Python новых возможностей асинхронного программирования. Поэтому их можно разворачивать с поддержкой параллельного исполнения или асинхронного программирования старого типа, которое не настолько эффективно.
Вот почему имеет смысл использовать асинхронный код ⏸🔀⏯ для веб-API.
При том, что основная спецификация асинхронного взаимодействия Python с веб-сервером (<ahref="https://asgi.readthedocs.io"class="external-link"target="_blank">ASGI</a>) была разработана командой Django для внедрения поддержки веб-сокетов.
Эта форма асинхронности является тем, что сделало NodeJS популярным (хотя NodeJS не параллелен) и это то, что делает Go мощным языком программирования.
Именно асинхронность сделала NodeJS таким популярным (несмотря на то, что он не параллельный), и в этом преимущество Go как языка программирования.
И это тот же уровень производительности, который вы получаете с **FastAPI**.
И так как у вас может быть параллелизм и асинхронность одновременно, вы получаете более высокую производительность, чем большинство протестированных NodeJS фреймворков, и сопоставимо с Go, который является компилируемым языком, ближе к C <ahref="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1"class="external-link"target="_blank">(всё это благодаря Starlette)</a>.
Поскольку можно использовать преимущества параллелизма и асинхронности вместе, вы получаете производительность лучше, чем у большинства протестированных NodeJS фреймворков и на уровне с Go, который является компилируемым языком близким к C <ahref="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1"class="external-link"target="_blank">(всё благодаря Starlette)</a>.
### Конкуренция лучше параллелизма?
Нет! Это не мораль истории.
Конкурентность отличается от параллелизма. И она лучше в **конкретных** сценариях, которые включают много ожидания. Из-за этого, обычно она намного лучше, чем параллелизм для разработки веб-приложений. Но не всегда и не для всего.
Конкурентность отличается от параллелизма. Она лучше в **конкретных** случаях, где много времени приходится на ожидание. Вот почему она зачастую лучше параллелизма при разработке веб-приложений. Но это не значит, что конкурентность лучше в любых сценариях.
Так что, чтобы уравновесить это, представьте себе следующий короткий рассказ:
@ -269,15 +281,15 @@ def results():
Тут нет ожидания 🕙 нигде, просто много работы по разным местам дома.
Вы могли бы иметь очереди как в примере с бургерами, сначала гостиная, затем кухня, но так как вы не ждете 🕙 чего-либо, а просто убираете и убираете, очереди никак не повлияют на это.
Можно организовать очередь как в примере с бургерами, сначала гостиная, потом кухня, но это ни на что не повлияет, поскольку вы нигде не ждёте 🕙, а просто трёте да моете.
Добавление или отсутствие очередей (конкурентности) не увеличит или уменьшит время завершения работы, и вы выполните то же количество работы.
И понадобится одинаковое количество времени с очередью (конкурентностью) и без неё, и работы будет сделано тоже одинаковое количество.
Но в этом случае, если бы вы могли пригласить 8 бывших кассиров/поваров/теперь-уборщиков, и каждый из них (вместе с вами) мог бы взяться за зону дома, чтобы ее убрать, вы могли бы сделать всю работу **параллельно**, с дополнительной помощью, и закончить намного быстрее.
Однако в случае, если бы вы могли привести 8 бывших кассиров/поваров, а ныне уборщиков 👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳, и каждый из них (вместе с вами) взялся бы за свой участок дома, с такой помощью вы бы закончили намного быстрее, делая всю работу **параллельно**.
В этой ситуации каждый из уборщиков (включая вас) будет процессором, выполняющим свою часть работы.
И поскольку основная часть времени выполнения уходит на реальную работу (вместо ожидания), и работа в компьютере выполняется<abbrtitle="Центральный процессор">ЦП</abbr>, такие задачи называют "ограниченными производительностью процессора" (<abbrtitle="CPU bound">CPU bound</abbr>).
И поскольку большую часть времени выполнения занимает реальная работа (а не ожидание), а работу в компьютере делает<abbrtitle="Центральный процессор (CPU)">ЦП</abbr>, такие задачи называют <abbrtitle="CPU bound">ограниченными производительностью процессора</abbr>.
---
@ -286,33 +298,33 @@ def results():
Например:
* Обработка **звука** или **изображений**.
* **Компьютерное зрение**: изображение состоит из миллионов пикселей, каждый пиксель имеет 3 значения / цвета, обработка этого обычно требует вычисления чего-то на этих пикселях, все одновременно.
* **Машинное обучение**: оно обычно требует множества умножений "матриц" и "векторов". Представьте огромную таблицу с числами в Экселе и умножение всех их вместе одновременно.
* **Глубокое обучение**: это подполе Машинного обучения, так что, то же самое применимо. Просто здесь не одна таблица чисел для умножения, а огромное их множество, и в многих случаях используется специальный процессор для создания и / или использования этих моделей.
* **Компьютерное зрение**: изображение состоит из миллионов пикселей, в каждом пикселе 3 составляющих цвета, обработка обычно требует проведения расчётов по всем пикселям сразу.
* **Машинное обучение**: здесь обычно требуется умножение "матриц" и "векторов". Представьте гигантскую таблицу с числами в Экселе, и все их надо одновременно перемножить.
* **Глубокое обучение**: это область *машинного обучения*, поэтому сюда подходит то же описание. Просто у вас будет не одна таблица в Экселе, а множество. В ряде случаев используется специальный процессор для создания и / или использования построенных таким образом моделей.
### Конкурентность + параллелизм: Веб + Машинное обучение
С **FastAPI** вы можете использовать преимуществ конкурентности, который очень распространён в веб-разработке (та же главная привлекательность NodeJS).
**FastAPI** предоставляет возможности конкуретного программирования, которое очень распространено в веб-разработке (именно этим славится NodeJS).
Но вы также можете использовать преимущества параллелизма и многопроцессорности (имеющих несколько процессов, работающих параллельно) для рабочих нагрузок, зависимых от **производительности процессора**, таких как в системах Машинного обучения.
Кроме того, вы сможете использовать все преимущества параллелизма и <abbrtitle="multiprocessing">многопроцессорности</abbr> (когда несколько процессов работают параллельно), если рабочая нагрузка предполагает **ограничение по процессору**, как, например, в системах машинного обучения.
Это, плюс простой факт, что Python является главным языком для **Дата-сайенс**, Машинного обучения и особенно глубокого обучения, делают FastAPI очень хорошим выбором для Дата-сайенс / Машинного обучения веб-API и приложений (среди многих других).
Необходимо также отметить, что Python является главным языком в области **дата-сайенс** (наука о данных), машинного обучения и, особенно, глубокого обучения. Всё это делает FastAPI отличным вариантом (среди многих других) для разработки веб-API и приложений в области дата-сайенс / машинного обучения.
Чтобы увидеть, как достичь этого параллелизма в эксплуатации, смотрите раздел о [Развёртывании](deployment/index.md){.internal-link target=_blank}.
Как добиться такого параллелизма в продакшне описано в разделе [Развёртывание](deployment/index.md){.internal-link target=_blank}.
## `async` и `await`
Современные версии Python предлагают очень удобный способ создания асинхронного кода. Это делает код похожим на обычный "последовательный" код и автоматически вставляет "ожидание", когда это необходимо.
Современные версии Python реализуют асинхронное программирование интуитивно просто. Это похоже на обычный "последовательный" код с автоматизированным ожиданием в нужных моментах.
Если есть операция, которая требует ожидания перед тем, как результаты будут готовы, и она поддерживает эти новые возможности Python, вы можете написать её как:
При необходимости ожидания результата, если операция поддерживает современные возможности Python, код может быть написан следующим образом:
```Python
burgers = await get_burgers(2)
```
Ключ здесь — `await`. Он сообщает Python, что нужно дождаться ⏸, пока `get_burgers(2)` завершит своё выполнение 🕙, прежде чем сохранить результаты в `burgers`. Основываясь на этом, Python сможет временно перейти к выполнению других задач 🔀 ⏯ (например, получение нового запроса).
Ключевое здесь — `await`. Оно сообщает Python, что необходимо дождаться ⏸ выполнения `get_burgers(2)` 🕙 прежде чем сохранять результат в `burgers`. Python понимает, что может переключиться на выполнение других задач 🔀 ⏯ (например, получение следующего запроса).
Чтобы `await` работал, он должен находиться внутри функции, поддерживающей асинхронность. Для этого просто объявите её с помощью`async def`:
Для работы `await`, оно должно находиться внутри функции, поддерживающей асинхронность. Это достигается объявлением с`async def`:
С `async def`, Python знает, что внутри этой функции ему нужно ожидать выражений `await`, и что выполнение такой функции можно будет "поставить на паузу" ⏸ и перейти к чему-то другому 🔀, прежде чем вернуться.
`async def` указывает интерпретатору на наличие ожидания `await` в функции, с возможностью "паузы" ⏸ и переключения на другие задачи 🔀 до возврата в исходное место.
Когда вы хотите вызвать функцию `async def`, вы должны "ожидать" её. Поэтому, это не сработает:
Если требуется вызвать `async def` функцию, нужно "ожидать" её. Поэтому такое не сработает:
```Python
# Это не сработает, потому что get_burgers была определена с использованием: async def
@ -340,7 +352,7 @@ burgers = get_burgers(2)
---
Так, если вы используете библиотеку, которая сообщает вам, что можете вызвать её с помощью `await`, вам нужно создать *функции обработки пути*, которые её используют с `async def`, как в:
Так что, если вы используете библиотеку, которая требует вызова с `await`, создавайте *функции-обработчики пути* с использованием `async def`, например:
```Python hl_lines="2-3"
@app.get('/burgers')
@ -351,94 +363,94 @@ async def read_burgers():
### Более технические подробности
Как вы могли заметить, `await` можно использовать только внутри функций, определённых с помощью`async def`.
Вы могли заметить, что `await` может применяться только в функциях, объявленных с использованием`async def`.
Но в то же время функции, определённые с помощью `async def`, должны быть "ожиданы". Поэтому, функции с `async def` могут вызываться только внутри функций, определённых с `async def`.
Но выполнение такой функции необходимо "ожидать" через `await`. Это означает, что её можно вызвать только из другой функции, также объявленной с `async def`.
Так что как же насчёт яйца и курицы, как вызвать первую `async` функцию?
Итак, как же появилась первая *курица*? В смысле... как нам вызвать первую асинхронную функцию?
Если вы работаете с **FastAPI**, вы можете не заботиться об этом, потому что эта "первая" функция будет вашей *функцией обработки пути*, и FastAPI будет знать, как сделать всё правильно.
При работе с **FastAPI** не надо думать об этом, потому что "первой" функцией будет ваша *функция-обработчик пути*, и FastAPI будет знать, что делать.
Но если вы хотите использовать `async` / `await` без FastAPI, вы тоже можете это сделать.
Если хотите использовать `async` / `await` без FastAPI, у вас также есть такая возможность.
### Пишите свой асинхронный код
Starlette (и **FastAPI**) базируются на <ahref="https://anyio.readthedocs.io/en/stable/"class="external-link"target="_blank">AnyIO</a>, что делает их совместимыми как со стандартной библиотекой Python <ahref="https://docs.python.org/3/library/asyncio-task.html"class="external-link"target="_blank">asyncio</a>, так и с <ahref="https://trio.readthedocs.io/en/stable/"class="external-link"target="_blank">Trio</a>.
Starlette (и **FastAPI**) основаны на <ahref="https://anyio.readthedocs.io/en/stable/"class="external-link"target="_blank">AnyIO</a>, что делает их совместимыми как со стандартной библиотекой <ahref="https://docs.python.org/3/library/asyncio-task.html"class="external-link"target="_blank">asyncio</a> в Python, так и с <ahref="https://trio.readthedocs.io/en/stable/"class="external-link"target="_blank">Trio</a>.
В частности, вы можете напрямую использовать <ahref="https://anyio.readthedocs.io/en/stable/"class="external-link"target="_blank">AnyIO</a> для ваших продвинутых случаев использования конкурентности, которые требуют более сложных паттернов в вашем собственном коде.
В частности, вы можете напрямую использовать <ahref="https://anyio.readthedocs.io/en/stable/"class="external-link"target="_blank">AnyIO</a> для сложных случаев использования конкурентности, требующих более сложных паттернов в вашем коде.
И даже если вы не использовали FastAPI, вы всё равно могли бы написать свои собственные асинхронные приложения с помощью <ahref="https://anyio.readthedocs.io/en/stable/"class="external-link"target="_blank">AnyIO</a>, чтобы они были высоко совместимыми и получали все его преимущества (например, структурированная конкурентность).
Даже если вы не используете FastAPI, вы можете писать асинхронные приложения с помощью <ahref="https://anyio.readthedocs.io/en/stable/"class="external-link"target="_blank">AnyIO</a>, чтобы они были максимально совместимыми и получали его преимущества (например *структурная конкурентность*).
Я создал другую библиотеку поверх AnyIO, как тонкий слой поверх него, чтобы немного улучшить аннотации типов и получить лучшую **автодополнения**, **ошибки в строках кода** и т. д. Она также имеет дружный вводный материал и учебник, который поможет вам **понять** и писать **свой собственный асинхронный код**: <ahref="https://asyncer.tiangolo.com/"class="external-link"target="_blank">Asyncer</a>. Он будет особенно полезен, если вам нужно **сочетать асинхронный код с обычным** (блокирующим/синхронным) кодом.
Я создал другую библиотеку поверх AnyIO, как тонкий слой, чтобы немного улучшить аннотации типов и получить лучшее **автозавершение**, **встроенные ошибки** и т. д. У неё также есть дружелюбное введение и учебник, которые помогут вам **понять** и писать **свой асинхронный код**: <ahref="https://asyncer.tiangolo.com/"class="external-link"target="_blank">Asyncer</a>. Это будет особенно полезно, если вам нужно **комбинировать асинхронный код с обычным** (блокирующим/синхронным) кодом.
### Другие формы асинхронного кода
Этот стиль использования `async` и `await` относительно новый в языке.
Но он сильно упрощает работу с асинхронным кодом.
Но он значительно облегчает работу с асинхронным кодом.
Этот же синтаксис (или почти идентичный) также был недавно включен в современные версии JavaScript (в браузерах и NodeJS).
Очень похожий синтаксис недавно был включён в современные версии JavaScript (в браузере и NodeJS).
Но раньше обработка асинхронного кода была намного более сложной и трудной.
До этого поддержка асинхронного кода была реализована намного сложнее и труднее воспринималась.
В предыдущих версиях Python, вы могли использовать потоки или <ahref="https://www.gevent.org/"class="external-link"target="_blank">Gevent</a>. Но код в этом случае был намного сложнее для понимания, отладки и осмысления.
В предыдущих версиях Python для этого использовались потоки или <ahref="https://www.gevent.org/"class="external-link"target="_blank">Gevent</a>. Но такой код сложнее для понимания, отладки и размышлений.
В предыдущих версиях NodeJS / Javascript для браузеров, для этого использовались "обратные вызовы". Что приводило к <ahref="http://callbackhell.com/"class="external-link"target="_blank">аду обратных вызовов</a>.
В предыдущих версиях NodeJS/JavaScript использовали "обратные вызовы", что приводило к <ahref="http://callbackhell.ru/"class="external-link"target="_blank">аду обратных вызовов</a>.
## Сопрограммы
**Сопрограмма** — это просто очень крутое слово для обозначения того, что возвращается функцией `async def`. Python знает, что это что-то вроде функции, которую можно запустить, и которая закончится в какой-то момент, но которая может быть также поставлена на паузу ⏸, когда внутри встречается`await`.
**Корутина** — это крутое словечко для именования той сущности, которая возвращается функцией `async def`. Python знает, что это что-то наподобие функции, что её можно запустить и она закончится в какой-то момент, но что её выполнение может быть остановлено ⏸ внутренне с помощью`await`.
Но вся эта функциональность использования асинхронного кода с `async` и `await` часто суммируется как использование "сопрограмм". Это сопоставимо с основной ключевой особенностью Go, "Горутинами".
Вся функциональность асинхронного программирования с использованием `async` и `await` обобщается под термином "корутины". Они схожи с ключевой особенностью Go — "горутинами".
## Заключение
Давайте увидим ту же фразу сверху:
> Современные версии Python поддерживают **"асинхронный код"** с использованием чего-то, называемого **"сопрограммами"**, с синтаксисом**`async` и `await`**.
> Современные версии Python поддерживают разработку так называемого **"асинхронного кода"** посредством написания **"сопрограмм"** с использованием синтаксиса**`async` и `await`**.
Теперь это должно звучать более понятно. ✨
Всё это то, на чём работает FastAPI (через Starlette) и что делает его настолько невероятно производительным.
На этом основана работа FastAPI (посредством Starlette), и именно это обеспечивает его высокую производительность.
## Очень технические подробности
/// warning | Предупреждение
Вы, вероятно, можете это пропустить.
Этот раздел можно пропустить.
Это очень технические подробности того, как **FastAPI** работает внутри.
Здесь приводятся очень технические подробности устройства **FastAPI** внутри.
Если у вас есть технические знания (сопрограммы, потоки, блокировки и т. д.) и вы интересуетесь тем, как FastAPI обрабатывает `async def` в отличие от обычного `def`, продолжайте.
Но если вы обладаете техническими знаниями (корутины, потоки, блокировка и т. д.) и вам интересно, как FastAPI обрабатывает `async def` в отличие от обычных `def`, читайте дальше.
///
### Функции обработки пути
### Функции-обработчики пути
Когда вы объявляете *функцию обработки пути* обычным образом с ключевым словом `def` вместо `async def`, она выполняется во внешнем пуле потоков, который затем ожидается, вместо того, чтобы быть вызванной напрямую (так как это заблокировало бы сервер).
Когда вы объявляете *функцию-обработчик пути* обычным образом с ключевым словом `def` вместо `async def`, FastAPI ожидает её выполнения, запустив функцию во внешнем <abbrtitle="threadpool">пуле потоков</abbr>, а не напрямую (это бы заблокировало сервер).
Если вы приходите из другого асинхронного фреймворка, который работает не так, как описано выше, и вы привыкли определять тривиальные вычислительные *функции обработки пути* с простым `def` для маленького прироста производительности (около 100 наносекунд), обратите внимание, что в **FastAPI** эффект будет полностью противоположным. В этих случаях лучше использовать `async def`, если только ваши *функции обработки пути* не используют код, который выполняет блокирующий<abbrtitle="Ввод/вывод: чтение или запись на диск, сетевые коммуникации.">I/O</abbr>.
Если ранее вы использовали другой асинхронный фреймворк, который работает иначе, и привыкли объявлять простые вычислительные *функции* через `def` ради незначительного прироста скорости (порядка 100 наносекунд), обратите внимание, что с **FastAPI** эффект будет обратный. В таких случаях лучше использовать `async def`, если только *функция-обработчик пути* не выполняет код, приводящий к блокировке<abbrtitle="Ввод/вывод: чтение и запись на диск, сетевые соединения.">I/O</abbr>.
Тем не менее, в обеих ситуациях есть вероятность, что **FastAPI** [всё еще будет быстрее](index.md#performance){.internal-link target=_blank}, чем (или, по крайней мере, сравним с) ваш предыдущий фреймворк.
Но в любом случае велика вероятность, что **FastAPI** [окажется быстрее](index.md#performance){.internal-link target=_blank}, чем (или хотя бы на уровне с) ваш предыдущий фреймворк.
### Зависимости
Тоже самое относится к [зависимостям](tutorial/dependencies/index.md){.internal-link target=_blank}. Если зависимость является стандартной функцией `def` вместо `async def`, она запускается во внешнем пуле потоков.
Тоже самое относится и к [зависимостям](tutorial/dependencies/index.md){.internal-link target=_blank}. Если зависимость объявлена с использованием стандартной функции `def`, она будет запущена во внешнем пуле потоков.
### Подзависимости
Вы можете иметь множество зависимостей и [подзависимостей](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank}, требующих друг друга (как параметры определения функций), некоторые из них могут быть созданы с помощью `async def`, а некоторые с обычным `def`. Это всё равно будет работать, и те, которые созданы с обычным `def`, будут вызываться на внешнем потоке (из пула), а не "ожидаться".
Вы можете объявить множество ссылающихся друг на друга зависимостей и [подзависимостей](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank} (в виде параметров при определении функции). Какие-то будут созданы с помощью `async def`, другие обычным образом через `def`, и такая схема вполне работоспособна. Функции, объявленные с помощью `def` будут запускаться на внешнем потоке (из пула), а не с помощью `await`.
### Другие служебные функции
Любая другая служебная функция, которую вы вызываете напрямую, может быть создана с обычным `def` или `async def`, и FastAPI не будет влиять на то, как вы её вызываете.
Любые другие служебные функции, которые вы вызываете напрямую, можно объявлять с использованием `def` или `async def`. FastAPI не будет влиять на то, как вы их запускаете.
Это в отличие от функций, которые FastAPI вызывает для вас: *функции обработки пути* и зависимости.
Этим они отличаются от функций, которые FastAPI вызывает самостоятельно: *функции-обработчики пути* и зависимости.
Если ваша служебная функция — это обычная функция с `def`, она будет вызвана напрямую (как вы пишите это в коде), не в пуле потоков, если функция создана с `async def`, тогда вам нужно `await` для этой функции, когда вы вызываете её в вашем коде.
Если служебная функция объявлена с помощью `def`, она будет вызвана напрямую (как вы и написали в коде), а не в отдельном потоке. Если же она объявлена с помощью `async def`, её вызов должен осуществляться с ожиданием через `await`.
---
Еще раз повторим, что все эти технические подробности вероятно будут полезны, если вы специально искали их.
Теперь, когда у вас есть представление о больших технических подробностях, это может быть полезно, если вы специально их искали.
В противном случае вы должны обратиться к руководящим принципам из раздела выше: <ahref="#in-a-hurry">В спешке?</a>.
В противном случае просто ознакомьтесь с основными принципами в разделе выше: <ahref="#in-a-hurry">Нет времени?</a>.
Независимые тесты производительности TechEmpower показывают, что приложения на**FastAPI**, работающие под управлением Uvicorn, это<ahref="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7"class="external-link"target="_blank">один из самых быстрых Python-фреймворков</a>, уступающий лишь Starlette и самому Uvicorn (которые используются внутри FastAPI).
Независимые тесты производительности от TechEmpower показывают, что приложения **FastAPI**, работающие под управлением Uvicorn, являются<ahref="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7"class="external-link"target="_blank">одними из самых быстрых Python-фреймворков</a>, уступая только самим Starlette и Uvicorn (которые используются внутри FastAPI).
Однако просматривая результаты тестов и сравнения, следует учитывать следующее.
Однако при просмотре бенчмарков и сравнений следует учитывать следующее.
## Результаты тестов и скорость
## Бенчмарки и скорость
Когда вы изучаете тесты производительности, часто можно увидеть, что сравниваются несколько инструментов разных типов как эквивалентные.
Когда вы изучаете бенчмарки, часто можно увидеть сравнение нескольких инструментов разного типа, как если бы они были эквивалентны.
В частности, можно увидеть, что сравниваются Uvicorn, Starlette и FastAPI (среди многих других инструментов).
В частности, можно увидеть, как сравниваются Uvicorn, Starlette и FastAPI (среди многих других инструментов).
Чем проще задача, решаемая инструментом, тем лучше будут показатели производительности. И большинство тестов не проверяют дополнительные функции, предоставляемые инструментом.
Чем проще задача, которую решает инструмент, тем лучше у него производительность. И большинство бенчмарков не проверяют дополнительные функции, предоставляемые инструментом.
* **FastAPI**: (использует Starlette) API-микрофреймворк с несколькими дополнительными функциями для создания API, включая валидацию данных и т.д.
* **FastAPI**: (использует Starlette) API-микрофреймворк с множеством дополнительных функций для создания API, таких как валидация данных и т.д.
* **Uvicorn**:
* Будет показывать наилучшую производительность, так как не содержит много дополнительного кода, кроме самого сервера.
* Вы бы не писали приложение напрямую на Uvicorn. Это означало бы, что ваш код должен содержать как минимум весь код, предоставляемый Starlette (или **FastAPI**). И если бы вы это сделали, ваше итоговое приложение имело бы такую же нагрузку, как если бы вы использовали фреймворк, минимизируя количество кода и ошибок.
* Если вы сравниваете Uvicorn, сравнивайте его с Daphne, Hypercorn, uWSGI и другими серверами приложений.
* Имеет наилучшую производительность, так как не содержит многого дополнительного кода, кроме самого сервера.
* Вы не будете писать приложение на Uvicorn напрямую. Это означало бы, что в вашем коде пришлось бы включать, по крайней мере, весь код, предоставляемый Starlette (или **FastAPI**). И если бы вы так сделали, то конечное приложение имело бы те же накладные расходы, что и при использовании фреймворка, минимизирующего код вашего приложения и ваши ошибки.
* Если сравнивать Uvicorn, сравнивайте его с Daphne, Hypercorn, uWSGI и другими серверами приложений.
* **Starlette**:
* Будет иметь производительность после Uvicorn. Фактически Starlette использует Uvicorn для работы. Поэтому он, вероятно, может только "замедлиться" по сравнению с Uvicorn, выполняя больше кода.
* Но он предоставляет вам инструменты для создания простых веб-приложений с маршрутизацией на основе путей и т.д.
* Если вы сравниваете Starlette, сравнивайте его с Sanic, Flask, Django и другими веб-фреймворками (или микрофреймворками).
* Будет иметь следующую по производительности после Uvicorn. Фактически, Starlette использует Uvicorn для выполнения. Поэтому, вероятно, он может стать "медленнее" Uvicorn, так как выполняет больше кода.
* Однако он предоставляет вам инструменты для создания простых веб-приложений с маршрутизацией, основанной на путях и т.д.
* Если сравнивать Starlette, сравнивайте его с Sanic, Flask, Django и другими веб-фреймворками (или микрофреймворками).
* **FastAPI**:
* Так же, как Starlette использует Uvicorn и не может быть быстрее его, **FastAPI** использует Starlette, поэтому не может быть быстрее него.
* FastAPI предоставляет больше возможностей поверх Starlette. Возможности, которые почти всегда необходимы при создании API, такие как проверка и сериализация данных. И, используя его, вы получаете автоматическую документацию бесплатно (автоматическая документация не добавляет нагрузки к работающим приложениям, она создается при запуске).
* Если бы вы не использовали FastAPI и использовали Starlette напрямую (или другой инструмент, такой как Sanic, Flask, Responder и т.д.), вам пришлось бы самостоятельно реализовать всю проверку и сериализацию данных. Так что ваше конечное приложение все равно имело бы такую же нагрузку, как если бы оно было создано с использованием FastAPI. И во многих случаях, проверка и сериализация данных - это самая большая часть кода, написанного в приложениях.
* Таким образом, используя FastAPI, вы экономите время на разработку, снижаете количество ошибок, уменьшаете количество строк кода и, вероятно, получите такую же производительность (или лучше), как если бы вы не использовали его (так как вам пришлось бы реализовать все это в своем коде).
* Если вы сравниваете FastAPI, сравнивайте его с фреймворками веб-приложений (или набором инструментов), которые обеспечивают проверку данных, сериализацию и документацию, такими как Flask-apispec, NestJS, Molten и т.п. Фреймворками с интегрированной автоматической проверкой данных, сериализацией и документацией.
* Так же как Starlette использует Uvicorn и не может быть быстрее его, **FastAPI** использует Starlette, поэтому не может быть быстрее его.
* FastAPI предоставляет больше возможностей поверх Starlette. Возможностей, которые почти всегда нужны при создании API, таких как валидация данных и сериализация. Используя его, вы получаете автоматическую документацию бесплатно (автоматическая документация даже не добавляет накладных расходов при выполнении приложений, она генерируется при запуске).
* Если бы вы не использовали FastAPI, а использовали Starlette напрямую (или другой инструмент, такой как Sanic, Flask, Responder и т.д.), вам пришлось бы самостоятельно реализовывать всю валидацию и сериализацию данных. Таким образом, ваше конечное приложение все равно имело бы те же накладные расходы, как если бы оно было создано с использованием FastAPI. И во многих случаях валидация и сериализация данных представляют собой самый большой объём кода, написанного в приложениях.
* Таким образом, используя FastAPI, вы экономите время разработки, уменьшаете количество ошибок, количество строк кода и, вероятно, получаете ту же производительность (или лучше), как если бы не использовали его (поскольку вам пришлось бы реализовать все это в своем коде).
* Если вы сравниваете FastAPI, сравнивайте его с фреймворком веб-приложений (или набором инструментов), который предоставляет валидацию данных, сериализацию и документацию, такими как Flask-apispec, NestJS, Molten и им подобные. Фреймворки с интегрированной автоматической валидацией данных, сериализацией и документацией.
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> server </font></span>Сервер запущен на<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://0.0.0.0:8000</u></font>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> server </font></span>Документация доступна по адресу<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> server </font></span>Server started at<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://0.0.0.0:8000</u></font>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> server </font></span>Documentation at<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
Логи:
Logs:
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Запущен серверный процесс<b>[</b><fontcolor="#34E2E2"><b>2306215</b></font><b>]</b>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Ожидание запуска приложения.
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Запуск приложения завершён.
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Uvicorn работает на<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://0.0.0.0:8000</u></font><b>(</b>нажмите CTRL+C
для завершения<b>)</b>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span> Started server process<b>[</b><fontcolor="#34E2E2"><b>2306215</b></font><b>]</b>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span> Waiting for application startup.
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span> Application startup complete.
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span> Uvicorn running on<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://0.0.0.0:8000</u></font><b>(</b>Press CTRL+C
to quit<b>)</b>
```
</div>
Это подойдёт для большинства случаев. 😎
Это подходящий вариант для большинства случаев. 😎
Вы можете использовать эту команду, чтобы например запустить ваше **FastAPI**-приложение в контейнере, на сервере и т.д.
Вы можете использовать эту команду, чтобы, например, запустить ваше приложение **FastAPI** в контейнере, на сервере и т.д.
## ASGI-сервера
## ASGI-серверы
Давайте углубимся в детали.
FastAPI использует стандарт для создания Python веб-фреймворков и серверов, называемый <abbrtitle="Asynchronous Server Gateway Interface">ASGI</abbr>. FastAPI — это ASGI веб-фреймворк.
FastAPI использует стандарт для создания веб-фреймворков и серверов в Python, называемый <abbrtitle="Asynchronous Server Gateway Interface">ASGI</abbr>. FastAPI — это ASGI веб-фреймворк.
Основное, что вам нужно для запуска **FastAPI** приложения (или любого другого ASGI приложения) на удалённой серверной машине — это программа ASGI сервера, такая как **Uvicorn**, именно она используется по умолчанию в команде`fastapi`.
Основное, что вам нужно для запуска приложения **FastAPI** (или любого другого ASGI приложения) на удалённой серверной машине — это программа сервера ASGI, такая как **Uvicorn**, которая поставляется по умолчанию с командой`fastapi`.
* <ahref="https://hypercorn.readthedocs.io/"class="external-link"target="_blank">Hypercorn</a>: ASGI сервер, поддерживающий HTTP/2 и Trio среди прочего.
* <ahref="https://github.com/django/daphne"class="external-link"target="_blank">Daphne</a>: ASGIсервер, созданный для Django Channels.
* <ahref="https://github.com/emmett-framework/granian"class="external-link"target="_blank">Granian</a>: HTTP сервер на Rust для Python приложений.
* <ahref="https://unit.nginx.org/howto/fastapi/"class="external-link"target="_blank">NGINX Unit</a>: лёгкая и многофункциональная среда выполнения веб-приложений от NGINX.
* <ahref="https://hypercorn.readthedocs.io/"class="external-link"target="_blank">Hypercorn</a>: ASGI-сервер, совместимый с HTTP/2 и Trio, среди прочих функций.
* <ahref="https://github.com/django/daphne"class="external-link"target="_blank">Daphne</a>: ASGI-сервер, созданный для Django Channels.
* <ahref="https://github.com/emmett-framework/granian"class="external-link"target="_blank">Granian</a>: сервер HTTP на Rust для Python приложений.
* <ahref="https://unit.nginx.org/howto/fastapi/"class="external-link"target="_blank">NGINX Unit</a>: лёгкая и универсальная среда выполнения веб-приложений.
## Сервер как машина и сервер как программа
Есть небольшая деталь о названиях, которую нужно иметь в виду. 💡
Есть небольшой нюанс в терминологии, который стоит помнить. 💡
Слово "**сервер**" обычно используется для обозначения как удалённого/облачного компьютера (физическая или виртуальная машина), так и программы, запущенной на этой машине (например, Uvicorn).
Слово "**сервер**" часто используется для обозначения как удалённого/облачного компьютера (физической или виртуальной машины), так и программы, работающей на этой машине (например, Uvicorn).
Просто помните, что когда вы видите "сервер" в общем случае, это может обозначать что-то из этих двух вещей.
Просто держите в уме, что, когда вы видите слово "сервер", это может относиться к одному из этих двух понятий.
Когда речь идёт о удалённой машине, её часто называют**сервер**, но также **машина**, **ВМ** (виртуальная машина), **нода**. Все они относятся к каким-то удалённым машинам, обычно под управлением Linux, на которых вы запускаете программы.
Когда говорят о удалённой машине, часто упоминают просто**сервер**, но также могут называть его **машина**, **ВМ** (виртуальная машина), **нода**. Все эти термины обозначают какой-то тип удалённого компьютера, обычно с операционной системой Linux, на котором вы запускаете программы.
## Установка программы сервера
Когда вы устанавливаете FastAPI, он поставляется с сервером для продакшена, Uvicorn, и вы можете запустить его с помощью команды`fastapi run`.
При установке FastAPI, он поставляется с продакшн-сервером, Uvicorn, и вы можете запустить его с командой`fastapi run`.
Но вы также можете установить ASGI-сервер вручную.
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его, и тогда можете установить серверное приложение.
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его, и затем вы можете установить серверное приложение.
Аналогичный процесс будет применен для любой другой программы ASGI сервера.
Аналогичный процесс применим и к любой другой программе ASGI-сервера.
/// tip | Подсказка
Добавив `standard`, Uvicorn установит и будет использовать некоторые рекомендованные дополнительные зависимости.
Добавив `standard`, Uvicorn установит и будет использовать некоторые рекомендуемые дополнительные зависимости.
Среди них`uvloop`, высокопроизводительная замена для `asyncio`, которая предоставляет значительное увеличение производительности при использовании асинхронности.
В их числе`uvloop`, высокопроизводительная замена `asyncio`, которая обеспечивает ощутимый прирост скорости обработки запросов с большими объемами данных.
Когда вы устанавливаете FastAPI чем-то вроде `pip install "fastapi[standard]"`, вы также получаете `uvicorn[standard]`.
Когда вы устанавливаете FastAPI с чем-то вроде `pip install "fastapi[standard]"`, вы уже получаете `uvicorn[standard]`.
///
## Запуск программы сервера
Если вы установили ASGI-сервер вручную, вам обычно нужно будет передать строку импорта в специальном формате, чтобы он импортировал ваше FastAPI-приложение:
Если вы установили ASGI-сервер вручную, вам обычно потребуется указать строку для импорта в специальном формате, чтобы он мог импортировать ваше FastAPI-приложение:
* `app`: объект, созданный внутри `main.py` строкой `app = FastAPI()`.
Это эквивалентно:
@ -129,29 +129,29 @@ from main import app
///
Каждая альтернативная программа ASGI сервера будет иметь аналогичную команду, вы можете узнать больше в их соответствующей документации.
Каждая из альтернатив программ ASGI-сервера будет иметь схожую команду, вы можете узнать больше в их документации.
/// warning | Предупреждение
Uvicorn и другие серверы поддерживают опцию `--reload`, которая полезна во время разработки.
Uvicorn и другие серверы поддерживают опцию `--reload`, которая полезна в процессе разработки.
Опция `--reload` потребляет значительно больше ресурсов, более нестабильна и т.д.
Опция `--reload` потребляет намного больше ресурсов, она менее стабильна и так далее.
Она очень полезна во время **разработки**, но **не следует** использовать её в **продакшене**.
Она очень помогает во время **разработки**, но **не рекомендуется** использовать её в **продакшн**.
///
## Концепции развёртывания
В этих примерах выполняется запуск серверной программы (например, Uvicorn), создавая **один процесс**, который слушает на всех IP (`0.0.0.0`) на предустановленном порту (например, `80`).
Эти примеры запускают серверные программы (например, Uvicorn), начиная с **одного процесса**, слушающего все IP-адреса (`0.0.0.0`) на определенном порту (например, `80`).
Это базовая идея. Но, вероятно, вам захочется позаботиться о некоторых дополнительных вещах, таких как:
Это основная идея. Но вы, вероятно, захотите позаботиться о некоторых дополнительных вещах, таких как:
* Безопасность - HTTPS
* Запуск при старте системы
* Перезапуски
* Репликация (число запущенных процессов)
* Репликация (количество запущенных процессов)
* Память
* Предыдущие шаги перед стартом
* Предыдущие шаги перед запуском
Я расскажу вам больше о каждой из этих концепций, как их рассматривать и приведу конкретные примеры стратегий для их решения в следующих главах. 🚀
Я расскажу вам больше о каждой из этих концепций, о том, как их воспринимать, и дам конкретные примеры стратегий для работы с ними в следующих главах. 🚀
Вы можете подписаться на (редкую) [рассылку **FastAPI и друзья**](newsletter.md){.internal-link target=_blank}, чтобы быть в курсе следующих событий:
* Новости о FastAPI и его друзьях 🚀
* Руководства 📝
* Возможности ✨
* Изменения 🚨
* Советы и хитрости ✅
* Новостях о FastAPI и его друзьях 🚀
* Руководствах 📝
* Возможностях ✨
* Изменениях, нарушающих совместимость 🚨
* Подсказках и хитростях ✅
## Подписаться на FastAPI в Twitter
@ -40,22 +40,22 @@
## Связаться с автором
Вы можете связаться со мной<ahref="https://tiangolo.com"class="external-link"target="_blank">(Себастьян Рамирес / `tiangolo`)</a>, автором.
Можно связаться со<ahref="https://tiangolo.com"class="external-link"target="_blank">мной (Себаcтиян Рамирез / `tiangolo`)</a>, автором FastAPI.
Вы можете:
* <ahref="https://github.com/tiangolo"class="external-link"target="_blank">Подписаться на меня на **GitHub**</a>.
* Ознакомиться с другими проектами с открытым исходным кодом, которые я создал и которые могут быть полезны вам.
* Подписывайтесь на меня, чтобы узнавать о моих новых проектах с открытым исходным кодом.
* Посмотреть другие мои проекты с открытым кодом, которые могут быть полезны Вам.
* Подписавшись на меня Вы сможете получать уведомления, что я создал новый проект с открытым кодом.
* <ahref="https://twitter.com/tiangolo"class="external-link"target="_blank">Подписаться на меня в **Twitter**</a> или в <ahref="https://fosstodon.org/@tiangolo"class="external-link"target="_blank">Mastodon</a>.
* Расскажите мне, как вы используете FastAPI (мне нравится об этом слышать).
* Узнавать о моих объявлениях или новых инструментах.
* Вы также можете <ahref="https://twitter.com/fastapi"class="external-link"target="_blank">подписаться на @fastapi в Twitter</a> (отдельный аккаунт).
* <ahref="https://www.linkedin.com/in/tiangolo/"class="external-link"target="_blank">Следите за мной на**LinkedIn**</a>.
* Узнавайте о моих объявлениях или новых инструментах (хотя я чаще использую Twitter 🤷♂).
* Читайте, что я пишу (или подписывайтесь на меня) на<ahref="https://dev.to/tiangolo"class="external-link"target="_blank">**Dev.to**</a> или на<ahref="https://medium.com/@tiangolo"class="external-link"target="_blank">**Medium**</a>.
* Читайте другие идеи, статьи и узнавайте о созданных мной инструментах.
* Подпишитесь на меня, чтобы знать, когда я публикую что-то новое.
* Поделиться со мной, как Вы используете FastAPI (я обожаю читать про это).
* Получать уведомления, когда я делаю объявления и представляю новые инструменты.
* Вы также можете <ahref="https://twitter.com/fastapi"class="external-link"target="_blank">подписаться на @fastapi в Twitter</a> (это отдельный аккаунт).
* <ahref="https://www.linkedin.com/in/tiangolo/"class="external-link"target="_blank">Подписаться на меня в**LinkedIn**</a>.
* Получать уведомления, когда я делаю объявления и представляю новые инструменты (правда чаще всего я использую Twitter 🤷♂).
* Читать, что я пишу (или подписаться на меня) в<ahref="https://dev.to/tiangolo"class="external-link"target="_blank">**Dev.to**</a> или в<ahref="https://medium.com/@tiangolo"class="external-link"target="_blank">**Medium**</a>.
* Читать другие идеи, статьи и читать об инструментах созданных мной.
* Подпишитесь на меня, чтобы читать, когда я публикую что-нибудь новое.
## Написать твит о **FastAPI**
@ -65,28 +65,28 @@
## Оставить голос за FastAPI
* <ahref="https://www.slant.co/options/34241/~fastapi-review"class="external-link"target="_blank">Голосуйте за **FastAPI**на Slant</a>.
* <ahref="https://alternativeto.net/software/fastapi/about/"class="external-link"target="_blank">Голосуйте за **FastAPI**на AlternativeTo</a>.
* <ahref="https://stackshare.io/pypi-fastapi"class="external-link"target="_blank">Создайте отзыв о**FastAPI** на StackShare</a>.
* <ahref="https://www.slant.co/options/34241/~fastapi-review"class="external-link"target="_blank">Голосуйте за **FastAPI**в Slant</a>.
* <ahref="https://alternativeto.net/software/fastapi/about/"class="external-link"target="_blank">Голосуйте за **FastAPI**в AlternativeTo</a>.
* <ahref="https://stackshare.io/pypi-fastapi"class="external-link"target="_blank">Расскажите, как Вы используете**FastAPI** на StackShare</a>.
## Помогите другим с вопросами на GitHub
## Помочь другим с их вопросами на GitHub
Вы можете попытаться помочь другим с их вопросами в:
Во многих случаях вы уже можете знать ответы на эти вопросы. 🤓
Во многих случаях, весьма вероятно, Вы уже знаете ответ на эти вопросы. 🤓
Если вы будете помогать многим людям с их вопросами, вы станете официальным [Экспертом FastAPI](fastapi-people.md#fastapi-experts){.internal-link target=_blank}. 🎉
Если Вы будете много помогать людям с решением их вопросов, Вы можете стать официальным [Экспертом FastAPI](fastapi-people.md#fastapi-experts){.internal-link target=_blank}. 🎉
Просто помните, самое важное: старайтесь быть добрыми. Люди приходят со своими разочарованиями и часто не задают вопросы так, как следовало бы, но старайтесь быть как можно добрее. 🤗
Идея сообщества **FastAPI**заключается в том, чтобы быть добрыми и гостеприимными. В то же время, не терпите издевательства или неуважительное поведение по отношению к другим. Мы должны заботиться друг о друге.
Идея сообщества **FastAPI**в том, чтобы быть добродушным и гостеприимным. Не допускайте издевательств или неуважительного поведения по отношению к другим. Мы должны заботиться друг о друге.
---
Вот как помочь другим с вопросами (в обсуждениях или проблемах):
Как помочь другим с их вопросами (в обсуждениях или issues):
### Понять вопрос
@ -102,7 +102,7 @@
В большинстве случаев и вопросов что-то связано с **исходным кодом** пользователя.
Во многих случаях они скопируют только фрагмент кода, но этого недостаточно для того, чтобы **воспроизвести проблему**.
В большинстве случаев и по большинству вопросов есть что-то связанное с **исходным кодом** вопрошающего.
* Вы можете попросить их предоставить <ahref="https://stackoverflow.com/help/minimal-reproducible-example"class="external-link"target="_blank">минимальный, воспроизводимый пример</a>, который вы можете **копировать и вставить** и запустить локально, чтобы увидеть ту же ошибку или поведение, которое они наблюдают, или чтобы лучше понять их контекст.
@ -116,49 +116,51 @@
### Попросить закрыть
Если они ответят, с большой вероятностью вы решили их проблему, поздравляю, **вы герой**! 🦸
### Попросить закрыть вопрос
* Теперь, если это решило их проблему, вы можете попросить их:
Если Вам ответили, высоки шансы, что Вам удалось решить их проблему, поздравляю, **Вы - герой**! 🦸
* В обсуждениях на GitHub: отметить комментарий как **ответ**.
* В проблемах на GitHub: **закрыть** проблему.
* В таком случае, если вопрос решён, попросите их:
* В GitHub Discussions: отметить ваш комментарий как **ответ**.
* В GitHub Issues: **закрыть** тикет.
## Отслеживать репозиторий на GitHub
Вы можете "отслеживать" FastAPI на GitHub (нажав на кнопку "watch" вверху справа): <ahref="https://github.com/fastapi/fastapi"class="external-link"target="_blank">https://github.com/fastapi/fastapi</a>. 👀
Если вы выберете "Watching" вместо "Releases only", вы получите уведомления, когда кто-то создаёт новую проблему или задаёт вопрос. Вы также можете уточнить, что хотите получать уведомления только о новых проблемах, обсуждениях или PR и т.д.
Если Вы выберете "Watching" вместо "Releases only", то будете получать уведомления, когда кто-либо создаст новый тикет или вопрос. Вы также можете указать, что хотите получать уведомления только о новых тикетах, обсуждениях или пулл-реквестах и т.д.
Затем вы можете попытаться помочь решить эти вопросы.
Тогда Вы можете попробовать решить эти вопросы.
## Задавать вопросы
## Задать вопросы
Вы можете <ahref="https://github.com/fastapi/fastapi/discussions/new?category=questions"class="external-link"target="_blank">создать новый вопрос</a> в репозитории GitHub, чтобы:
Вы можете <ahref="https://github.com/fastapi/fastapi/discussions/new?category=questions"class="external-link"target="_blank">создать новый вопрос</a> в репозитории на GitHub, например:
* Задать **вопрос** или попросить о **проблеме**.
* Предложить новую **функцию**.
* Задать **вопрос** или попросить помощи в решении **проблемы**.
* Предложить новую **возможность**.
**Примечание**: если вы это сделаете, я попрошу вас также помочь другим. 😉
**Заметка**: если Вы создаёте подобные вопросы, то я попрошу Вас также оказывать аналогичную помощь другим. 😉
## Проверка пул-реквестов
## Проверять пулл-реквесты
Вы можете помочь мне с проверкой пул-реквестов от других участников.
Вы можете помочь мне проверять пулл-реквесты других участников.
Снова повторяю, пожалуйста, старайтесь быть добрыми. 🤗
---
Вот что нужно иметь в виду и как проверять пул-реквесты:
О том, что нужно иметь в виду при проверке пулл-реквестов:
### Понять проблему
* Во-первых, убедитесь, что вы **поняли проблему**, которую пытается решить пул-реквест. Возможно, это обсуждается в обсуждениях GitHub или проблеме.
* Во-первых, убедитесь, что **поняли проблему**, которую пулл-реквест пытается решить. Для этого может потребоваться продолжительное обсуждение в GitHub Discussion или ticket.
* Также существует вероятность, что пул-реквест не требуется, поскольку проблему можно решить **по-другому**. Затем вы можете предложить или спросить об этом.
* Также есть вероятность, что пулл-реквест не актуален, так как проблему можно решить **другим путём**. В таком случае Вы можете указать на этот факт.
### Не беспокойтесь о стиле
* Не слишком беспокойтесь о таких вещах, как стиль сообщений о коммитах, я буду принимать и сливать с учетом мануальной настройки.
* Не стоит слишком беспокоиться о таких вещах, как стиль сообщений в коммитах. При слиянии пулл-реквеста с основной веткой, я буду сжимать и настраивать всё вручную.
* Также не беспокойтесь о правилах стиля, уже есть автоматизированные инструменты, которые это проверяют.
@ -168,47 +170,47 @@
* Проверьте и прочтите код, посмотрев, имеет ли он смысл, **запустите его локально** и посмотрите, действительно ли он решает задачу.
* Затем **комментируйте**, что вы это сделали, так я узнаю, что вы действительно проверили код.
* Затем **прокомментируйте**, что Вы сделали эту проверку, тогда я буду знать, что Вы действительно проверили код.
/// info | Информация
К сожалению, я не могу просто доверять PR, у которых имеется несколько положительных отзывов.
К сожалению, я не могу так просто доверять пулл-реквестам, у которых уже есть несколько одобрений.
Бывали случаи, когда PR имели 3, 5 или больше положительных отзывов, вероятно из-за привлекательного описания, но когда я проверял такие PR, они оказывались некорректными, имели ошибку или не решали заявленную проблему. 😅
Бывали случаи, что пулл-реквесты имели 3, 5 или больше одобрений, вероятно из-за привлекательного описания, но когда я проверял эти пулл-реквесты, они оказывались сломаны, содержали ошибки или вовсе не решали проблему, которую, как они утверждали, должны были решить. 😅
Поэтому действительно важно, чтобы вы прочли и исполнили код, и сообщили мне в комментариях, что вы это сделали. 🤓
///
* Если PR можно упростить каким-то образом, вы можете попросить об этом, но не стоит быть слишком придирчивыми, может быть множество субъективных точек зрения (и у меня также будет своя 🙈), поэтому лучше сосредоточиться на фундаментальных вещах.
* Если Вы считаете, что пулл-реквест можно упростить, то можете попросить об этом, но не нужно быть слишком придирчивым, может быть много субъективных точек зрения (и у меня тоже будет своя 🙈), поэтому будет лучше, если Вы сосредоточитесь на фундаментальных вещах.
### Тестирование
* Помогите мне проверить, что у PR есть **тесты**.
* Помогите мне проверить, что у пулл-реквеста есть **тесты**.
* Проверьте, что тесты **падали** до PR. 🚨
* Проверьте, что тесты **падали** до пулл-реквеста. 🚨
* Затем проверьте, что тесты **не валятся** после PR. ✅
* Затем проверьте, что тесты **не падают** после пулл-реквеста. ✅
* Многие PR не имеют тестов, вы можете **напомнить** им об их добавлении или даже **предложить** свои тесты. Это одна из тех вещей, которые отнимают больше всего времени, и вы можете существенно помочь с ними.
* Многие пулл-реквесты не имеют тестов, Вы можете **напомнить** о необходимости добавления тестов или даже **предложить**какие-либо свои тесты. Это одна из тех вещей, которые отнимают много времени и Вы можете помочь с этим.
* Затем также укажите в комментариях, что вы проверили, так я узнаю, что вы это сделали. 🤓
* Затем также прокомментируйте, что Вы испробовали в ходе проверки. Таким образом я буду знать, как Вы произвели проверку. 🤓
## Создайте пул-реквест
## Создать пулл-реквест
Вы можете [внести вклад](contributing.md){.internal-link target=_blank} в исходный код с помощью Pull Requests, например:
Вы можете [сделать вклад](contributing.md){.internal-link target=_blank} в кодовую базу, предложив пулл-реквесты, например:
* Чтобы исправить ошибку в документации.
* Чтобы поделиться статьей, видео или подкастом, который вы создали или нашли, о FastAPI, <ahref="https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml"class="external-link"target="_blank">редактируя этот файл</a>.
* Убедитесь, что добавили ссылку в начало соответствующего раздела.
* Чтобы помочь [перевести документацию](contributing.md#translations){.internal-link target=_blank} на ваш язык.
* Вы также можете помочь рецензировать переводы, созданные другими.
* Чтобы предложить новые разделы документации.
* Чтобы исправить существующую проблему или баг.
* Исправить опечатку в документации, которую Вы нашли.
* Поделиться статьёй, видео или подкастом о FastAPI, которые Вы создали или нашли, <ahref="https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml"class="external-link"target="_blank">изменив этот файл</a>.
* Убедитесь, что Вы добавили свою ссылку в начало соответствующего раздела.
* Помочь с [переводом документации](contributing.md#translations){.internal-link target=_blank} на Ваш язык.
* Вы также можете проверять переводы, сделанные другими.
* Предложить новые разделы документации.
* Исправить существующую проблему/баг.
* Убедитесь, что добавили тесты.
* Чтобы добавить новую возможность.
* Убедитесь, что добавили тесты.
* Убедитесь, что добавили документацию, если это актуально.
* Убедитесь, что добавили документацию, если это необходимо.
## Помогите поддерживать FastAPI
@ -218,20 +220,20 @@
Основные задачи, которые вы можете сделать прямо сейчас:
* [Помочь другим с вопросами на GitHub](#help-others-with-questions-in-github){.internal-link target=_blank} (см. раздел выше).
* [Проверка пул-реквестов](#review-pull-requests){.internal-link target=_blank} (см. раздел выше).
* [Помочь другим с их вопросами на GitHub](#help-others-with-questions-in-github){.internal-link target=_blank} (смотрите вышестоящую секцию).
* [Проверить пул-реквесты](#review-pull-requests){.internal-link target=_blank} (смотрите вышестоящую секцию).
Эти две задачи **занимают больше всего времени**. Это основная работа по поддержке FastAPI.
Если вы можете помочь мне с этим, **вы помогаете поддерживать FastAPI**, обеспечивая его дальнейшее **развитие быстрее и лучше**. 🚀
Если Вы можете помочь мне с этим, **Вы помогаете поддерживать FastAPI** и следить за тем, чтобы он продолжал **развиваться быстрее и лучше**. 🚀
## Присоединяйтесь к чату
Присоединяйтесь к 👥 <ahref="https://discord.gg/VQjSZaeJmf"class="external-link"target="_blank">чат-серверу Discord</a> 👥 и общайтесь с другими в сообществе FastAPI.
Подключайтесь к 👥 <ahref="https://discord.gg/VQjSZaeJmf"class="external-link"target="_blank">чат-серверу в Discord</a> 👥 и общайтесь с другими участниками сообщества FastAPI.
/// tip | Подсказка
Задавайте вопросы в <ahref="https://github.com/fastapi/fastapi/discussions/new?category=questions"class="external-link"target="_blank">Обсуждениях GitHub</a>, так у вас будет намного больше шансов получить помощь от [Экспертов FastAPI](fastapi-people.md#fastapi-experts){.internal-link target=_blank}.
Для вопросов, задавайте их в <ahref="https://github.com/fastapi/fastapi/discussions/new?category=questions"class="external-link"target="_blank">GitHub Discussions</a>, там больше шансов, что Вы получите помощь от [Экспертов FastAPI](fastapi-people.md#fastapi-experts){.internal-link target=_blank}.
Используйте чат только для других общих разговоров.
@ -241,16 +243,16 @@
Помните, что в чатах легче задаются вопросы, которые носят слишком общий характер и на них труднее найти ответы, поэтому вы можете не получить ответы.
На GitHub шаблон поможет вам задать правильный вопрос, чтобы вы могли легче получить хороший ответ, или даже решить проблему самостоятельно до того, как вы зададите его. На GitHub я могу быть уверен, что всегда отвечаю на всё, даже если это занимает некоторое время. Я лично не могу сделать то же самое с системами чатов. 😅
В разделе обсуждений на GitHub, шаблон поможет Вам написать вопрос правильно, чтобы Вам было легче получить хороший ответ или даже решить проблему самостоятельно, прежде чем Вы зададите вопрос. В GitHub я могу быть уверен, что всегда отвечаю на всё, даже если это займет какое-то время. И я не могу гарантировать те же ответы в чатах. 😅
Разговоры в чатах также не так легко доступны для поиска, как в GitHub, поэтому вопросы и ответы могут затеряться. И только те, что на GitHub, учитываются в получении статуса [Эксперт FastAPI](fastapi-people.md#fastapi-experts){.internal-link target=_blank}, так что вы, скорее всего, получите больше внимания на GitHub.
Кроме того, общение в чатах не так легкодоступно для поиска, как в GitHub, потому вопросы и ответы могут потеряться среди другого общения. И только вопросы решаемые на GitHub учитываются в получении титула [Эксперт FastAPI](fastapi-people.md#fastapi-experts){.internal-link target=_blank}, так что весьма вероятно, что Вы получите больше внимания на GitHub.
С другой стороны, в чаты подключены тысячи пользователей, поэтому есть большая вероятность, что вы найдёте там кого-то, с кем могли бы поговорить почти в любое время. 😄
С другой стороны, в чатах тысячи пользователей, а значит есть большие шансы в любое время trouver кого-то, с кем можно поговорить. 😄
## Спонсировать автора
Если ваш **продукт/компания** зависит от **FastAPI** или связана с ним и вы хотите достичь его пользователей, вы можете спонсировать автора (меня) через <ahref="https://github.com/sponsors/tiangolo"class="external-link"target="_blank">спонсоров GitHub</a>. В зависимости от уровня, вы можете получить некоторые дополнительные преимущества, такие как награда в документации. 🎁
Если ваш **продукт/компания** зависит от или связан с **FastAPI** и вы хотите донести до его пользователей, вы можете спонсировать автора (меня) через <ahref="https://github.com/sponsors/tiangolo"class="external-link"target="_blank">GitHub sponsors</a>. В зависимости от уровня, вы можете получать некоторые дополнительные преимущества, такие как значок в документации. 🎁
@ -37,16 +37,16 @@ FastAPI — это современный, быстрый (высокопрои
Ключевые особенности:
* **Быстрый**: Очень высокая производительность, на уровне **NodeJS** и **Go** (благодаря Starlette и Pydantic). [Один из самых быстрых фреймворков на Python](#performance).
* **Быстрая разработка**: Повышает скорость разработки примерно на 200–300%. *
* **Меньше ошибок**: Уменьшает количество ошибок, вносимых разработчиком, на 40%. *
* **Интуитивно понятный**: Отличная поддержка редактора. <abbrtitle="также известны как автодополнение, IntelliSense">Автозаполнение</abbr> везде. Меньше времени на отладку.
* **Простой**: Разработан для простоты использования и изучения. Меньше времени на чтение документации.
* **Краткий**: Сведите к минимуму дублирование кода. Множество функций из каждого объявления параметра. Меньше ошибок.
* **Надежный**: Получите готовый к производству код. С автоматической интерактивной документацией.
* **На основе стандартов**: Основан на (и полностью совместим с) открытых стандартах API: <ahref="https://github.com/OAI/OpenAPI-Specification"class="external-link"target="_blank">OpenAPI</a> (ранее известный как Swagger) и <ahref="https://json-schema.org/"class="external-link"target="_blank">JSON Schema</a>.
* **Скорость**: Очень высокая производительность, на уровне **NodeJS** и **Go** (благодаря Starlette и Pydantic). [Один из самых быстрых фреймворков Python](#performance).
* **Быстрота разработки**: Увеличьте скорость разработки примерно на 200–300%. *
* **Меньше ошибок**: Сократите примерно на 40% количество ошибок, вызванных человеком (разработчиком). *
* **Интуитивно понятный**: Отличная поддержка редактора. <abbrtitle="также известное как автозаполнение, автодополнение, IntelliSense">Автозавершение</abbr> везде. Меньше времени на отладку.
* **Лёгкость**: Разработан так, чтобы его было легко использовать и осваивать. Меньше времени на чтение документации.
* **Краткость**: Сведите к минимуму дублирование кода. Каждый объявленный параметр - определяет несколько функций. Меньше ошибок.
* **Надежность**: Получите готовый к работе код. С автоматической интерактивной документацией.
* **На основе стандартов**: Основан на открытых стандартах API и полностью совместим с ними: <ahref="https://github.com/OAI/OpenAPI-Specification"class="external-link"target="_blank">OpenAPI</a> (ранее известном как Swagger) и <ahref="https://json-schema.org/"class="external-link"target="_blank">JSON Schema</a>.
<small>* оценка, основанная на тестах внутренней команды разработчиков, создающих производственные приложения.</small>
<small>* оценка на основе тестов внутренней команды разработчиков, создающих продакшн приложения.</small>
## Спонсоры
@ -101,7 +101,13 @@ FastAPI — это современный, быстрый (высокопрои
"_Мы перешли на **FastAPI** для наших **API** [...] Думаю, он вам понравится [...]_"
"_Если кто-то хочет создать продакшн Python API, я бы настоятельно рекомендовал **FastAPI**. Он **прекрасно разработан**, **прост в использовании** и **высокомасштабируем**, он стал **ключевым компонентом** нашей стратегии разработки API-first и управляет множеством автоматизаций и сервисов, таких как наш виртуальный инженер TAC._"
Если вы не уверены, ознакомьтесь с разделом _"Торопитесь?"_ о<ahref="https://fastapi.tiangolo.com/async/#in-a-hurry"target="_blank">`async` и `await` в документации</a>.
Если вы не знаете, проверьте раздел _"Нет времени?"_<ahref="https://fastapi.tiangolo.com/async/#in-a-hurry"target="_blank">в документации об `async` и `await`</a>.
<summary>О команде <code>fastapi dev main.py</code>...</summary>
Команда `fastapi dev` читает ваш файл `main.py`, определяет **FastAPI** приложение в нем и запускает сервер, используя<ahref="https://www.uvicorn.org"class="external-link"target="_blank">Uvicorn</a>.
Команда `fastapi dev`считывает файл `main.py`, определяет в нем приложение **FastAPI** и запускает сервер с использованием<ahref="https://www.uvicorn.org"class="external-link"target="_blank">Uvicorn</a>.
По умолчанию,`fastapi dev` запускается с активированной функцией автообновления для локальной разработки.
По умолчанию `fastapi dev` запускает приложение с включенной авто-перезагрузкой для локальной разработки.
Вы можете узнать больше об этом в <ahref="https://fastapi.tiangolo.com/fastapi-cli/"target="_blank">документации FastAPI CLI</a>.
@ -404,7 +409,7 @@ item: Item
---
Мы только начали изучать, как все это работает, но вы уже поняли основную идею.
Мы только немного поверхностно ознакомились, но вы уже поняли, как все это работает.
Попробуйте изменить строку:
@ -442,14 +447,14 @@ item: Item
* **WebSockets**
* невероятно простые тесты, основанные на HTTPX и `pytest`
* **CORS**
* **Сеансы Cookies**
* **Сессии с использованием Cookie**
* ...и многое другое.
## Производительность
Независимые бенчмарки TechEmpower демонстрируют приложения **FastAPI**, работающие под Uvicorn, как <ahref="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7"class="external-link"target="_blank">один из самых быстрых доступных фреймворков на Python</a>, уступающий только самим Starlette и Uvicorn (используемых внутри FastAPI). (*)
Чтобы узнать больше об этом, см. раздел <ahref="https://fastapi.tiangolo.com/benchmarks/"class="internal-link"target="_blank">Бенчмарки</a>.
Чтобы узнать больше об этом, см. раздел <ahref="https://fastapi.tiangolo.com/benchmarks/"class="internal-link"target="_blank">Тесты производительности</a>.
## Зависимости
@ -457,7 +462,7 @@ FastAPI зависит от Pydantic и Starlette.
### Зависимости `standard`
Когда вы устанавливаете FastAPI с использованием `pip install "fastapi[standard]"`, он поставляется с `standard` группой необязательных зависимостей:
Когда вы устанавливаете FastAPI с помощью `pip install "fastapi[standard]"`, он включает группу зависимостей `standard`:
Используется Pydantic:
@ -467,30 +472,30 @@ FastAPI зависит от Pydantic и Starlette.
* <ahref="https://www.python-httpx.org"target="_blank"><code>httpx</code></a> - Обязательно, если вы хотите использовать `TestClient`.
* <ahref="https://jinja.palletsprojects.com"target="_blank"><code>jinja2</code></a> - Обязательно, если вы хотите использовать конфигурацию шаблона по умолчанию.
* <ahref="https://github.com/Kludex/python-multipart"target="_blank"><code>python-multipart</code></a> - Обязательно, если вы хотите поддерживать <abbrtitle="преобразование строки, полученной из HTTP-запроса, в данные Python">"парсинг"</abbr>форм, с`request.form()`.
* <ahref="https://github.com/Kludex/python-multipart"target="_blank"><code>python-multipart</code></a> - Обязательно, если вы хотите поддерживать форму <abbrtitle="преобразование строки, полученной из HTTP-запроса, в данные Python">"парсинга"</abbr>с помощью`request.form()`.
Используется FastAPI:
* <ahref="https://www.uvicorn.org"target="_blank"><code>uvicorn</code></a> - сервер, который загружает и обслуживает ваше приложение. Это включает `uvicorn[standard]`, который включает некоторые зависимости (например, `uvloop`), нужные для высокопроизводительного сервиса.
* <ahref="https://www.uvicorn.org"target="_blank"><code>uvicorn</code></a> - для сервера, который загружает и обслуживает ваше приложение. Это включает `uvicorn[standard]`, который включает некоторые зависимости (например, `uvloop`), необходимые для высокопроизводительного обслуживания.
* `fastapi-cli[standard]` - для предоставления команды `fastapi`.
* Это включает `fastapi-cloud-cli`, который позволяет развернуть ваше FastAPI приложение на<ahref="https://fastapicloud.com"class="external-link"target="_blank">FastAPI Cloud</a>.
* Это включает `fastapi-cloud-cli`, который позволяет разворачивать ваше приложение FastAPI в<ahref="https://fastapicloud.com"class="external-link"target="_blank">FastAPI Cloud</a>.
### Без `standard` зависимостей
### Без зависимостей `standard`
Если вы не хотите включать `standard` необязательные зависимости, вы можете установить FastAPI с `pip install fastapi` вместо `pip install "fastapi[standard]"`.
Если вы не хотите включать необязательные зависимости `standard`, вы можете установить FastAPI с помощью`pip install fastapi` вместо `pip install "fastapi[standard]"`.
### Без `fastapi-cloud-cli`
Если вы хотите установить FastAPI с стандартными зависимостями, но без `fastapi-cloud-cli`, вы можете установить с `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
Если вы хотите установить FastAPI с стандартными зависимостями, но без `fastapi-cloud-cli`, вы можете установить с помощью `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
### Дополнительные необязательные зависимости
Существуют некоторые дополнительные зависимости, которые вы можете добавить.
Существуют некоторые дополнительные зависимости, которые вы можете захотеть установить.
Дополнительные необязательные зависимости Pydantic:
* <ahref="https://docs.pydantic.dev/latest/usage/pydantic_settings/"target="_blank"><code>pydantic-settings</code></a> - для управления настройками.
* <ahref="https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/"target="_blank"><code>pydantic-extra-types</code></a> - для добавления типов, которые могут быть использованы с Pydantic.
* <ahref="https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/"target="_blank"><code>pydantic-extra-types</code></a> - для дополнительных типов, которые могут быть использованы с Pydantic.
Дополнительные необязательные зависимости FastAPI:
Шаблоны, хотя обычно они поставляются с определенной конфигурацией, разработаны для гибкости и настройки. Это позволяет вам изменять и адаптировать их в соответствии с требованиями вашего проекта, что делает их отличной отправной точкой. 🏁
Шаблоны обычно идут с определенной настройкой, но они разработаны гибкими и настраиваемыми. Это позволяет вам модифицировать и адаптировать их в соответствии с требованиями вашего проекта, превращая их в отличную отправную точку. 🏁
Вы можете использовать этот шаблон, чтобы начать работу, так как он включает множество начальных настроек, безопасности, базы данных и некоторые API-эндпоинты, которые уже выполнены за вас.
Вы можете использовать этот шаблон, чтобы начать, так как он включает множество начальных настроек, безопасности, базу данных и некоторые API эндпоинты уже готовы.
Репозиторий на GitHub: <ahref="https://github.com/tiangolo/full-stack-fastapi-template"class="external-link"target="_blank">Full Stack FastAPI Template</a>
## Full Stack FastAPI Шаблон - Технологический стек и функции
## Full Stack FastAPI Шаблон - Технологии и Особенности
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com) для Python бэкенд API.
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com) для взаимодействия с SQL базой данных Python (ORM).
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com) для Python бэкенда API.
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com) для взаимодействия Python с SQL базой данных (ORM).
- 🔍 [Pydantic](https://docs.pydantic.dev), используемый FastAPI, для валидации данных и управления настройками.
- 💾 [PostgreSQL](https://www.postgresql.org) в качестве SQL базы данных.
- 🚀 [React](https://react.dev) для фронтенда.
- 💃 Используются TypeScript, хуки, [Vite](https://vitejs.dev), и другие элементы современного фронтенд стека.
- 🎨 [Chakra UI](https://chakra-ui.com) для фронтенд-компонентов.
- 🤖 Автоматически сгенерированный фронтенд-клиент.
- 💃 Использование TypeScript, hooks, [Vite](https://vitejs.dev), и других компонентов современного стека фронтенда.
- 🎨 [Chakra UI](https://chakra-ui.com) для компонентов фронтенда.
- 🤖 Автоматически сгенерированный фронтендклиент.
- 🧪 [Playwright](https://playwright.dev) для End-to-End тестирования.
- 🦇 Поддержка темного режима.
- 🐋 [Docker Compose](https://www.docker.com) для разработки и производства.
- 🐋 [Docker Compose](https://www.docker.com) для разработки и продакшна.
- 🔒 Безопасное хеширование паролей по умолчанию.
- 🔑 JWT токены аутентификации.
- 📫 Восстановление пароля по электронной почте.
- ✅ Тесты с [Pytest](https://pytest.org).
- 📞 [Traefik](https://traefik.io) в качестве обратного прокси / балансировщика нагрузки.
- 🚢 Инструкции по развёртыванию с использованием Docker Compose, включая настройку фронтенд-прокси Traefik для автоматической обработки HTTPS-сертификатов.
- 🏭 CI (непрерывная интеграция) и CD (непрерывное развертывание), основанные на GitHub Actions.
- 🔑 Аутентификация с помощью JWT токенов.
- 📫 Восстановление пароля через email.
- ✅ Тесты с использованием [Pytest](https://pytest.org).
- 📞 [Traefik](https://traefik.io) в качестве реверс-прокси / балансировщика нагрузки.
- 🚢 Инструкции по развёртыванию с Docker Compose, включая настройку фронтенд прокси Traefik для автоматического управления HTTPS сертификатами.
- 🏭 CI (непрерывная интеграция) и CD (непрерывное развёртывание) на базе GitHub Actions.
Теперь, когда мы увидели, как использовать `Path` и `Query`, давайте рассмотрим более продвинутые способы объявления тела запроса.
Теперь, когда мы увидели, как использовать `Path` и `Query`, давайте рассмотрим более продвинутые примеры объявления тела запроса.
## Объединение `Path`, `Query` и параметров тела
## Объединение `Path`, `Query` и параметров тела запроса
Во-первых, конечно, вы можете свободно объединять параметры `Path`, `Query` и объявления параметров тела, и **FastAPI** поймет, что с ними делать.
Во-первых, конечно, вы можете свободно объединять параметры `Path`, `Query` и объявления параметров тела запроса, и **FastAPI** автоматически поймёт, что с ними делать.
Вы также можете объявлять параметры тела как необязательные, установив значение по умолчанию `None`:
@ -12,13 +12,13 @@
/// note | Заметка
Обратите внимание, что в этом случае `item`, который будет взят из тела, является необязательным, так как имеет значение по умолчанию `None`.
Заметьте, что в данном случае параметр `item`, который будет взят из тела запроса, необязателен. Так как у него установлено значение `None` по умолчанию.
///
## Множественные параметры тела
В предыдущем примере *операции пути* ожидали, что тело запроса будет в формате JSON с атрибутами объекта`Item`, например:
В предыдущем примере,*операции пути* ожидали JSON-тело с атрибутами `Item`, например:
```JSON
{
@ -29,13 +29,13 @@
}
```
Но вы также можете объявить несколько параметров тела, например,`item` и `user`:
Но вы также можете объявить множество параметров тела запроса, например параметры`item` и `user`:
В этом случае **FastAPI** заметит, что в функции более одного параметра тела (оба являются моделями Pydantic).
В этом случае **FastAPI** заметит, что в функции больше одного параметра тела (два параметра, которые являются моделями Pydantic).
Следовательно, имена параметров будут использоваться как ключи (имена полей) в теле, и будет ожидаться тело такого вида:
Таким образом, в теле запроса будут использоваться имена параметров в качестве ключей (имен полей), и будет ожидаться запрос следующего формата:
```JSON
{
@ -52,13 +52,13 @@
}
```
/// note | Примечание
/// note | Заметка
Обратите внимание, что хотя `item` был объявлен так же, как и раньше, теперь он ожидается внутри тела с ключом `item`.
Обратите внимание, что хотя параметр `item` был объявлен таким же образом, как и раньше, теперь предполагается, что он находится внутри тела запроса с ключом `item`.
///
**FastAPI** выполнит автоматическое преобразование из запроса, так что параметр `item` получит своё конкретное содержимое, и то же самое произойдет с `user`.
**FastAPI** сделает автоматическое преобразование из запроса, так что параметр `item` получит своё конкретное содержимое, и то же самое произойдет с параметром`user`.
Он выполнит проверку составных данных и задокументирует это в схеме OpenAPI и автоматической документации.
@ -66,11 +66,11 @@
Так же, как существуют `Query` и `Path` для определения дополнительных данных для query и path параметров, **FastAPI** предоставляет эквивалент для тела - `Body`.
Например, расширяя предыдущую модель, вы можете решить, что вам нужен еще один ключ `importance` в том же теле, кроме параметров `item` и `user`.
Например, расширяя предыдущую модель, вы можете решить, что вам нужен ещё один ключ `importance` в том же теле запроса, помимо параметров `item` и `user`.
Если вы объявите это без указания объекта (Path, Query, Body и т.д.), то, поскольку это является простым типом данных, **FastAPI** рассмотрит это как query-параметр.
Если вы объявите его как есть, поскольку это простой тип данных, **FastAPI** будет считать, что это query-параметр.
Но вы можете указать **FastAPI** обрабатывать его, как ещё один ключ тела, используя `Body`:
Но вы можете указать **FastAPI** обрабатывать его как ещё один ключ тела запроса, используя `Body`:
И снова, он преобразует типы данных, проверит, задокументирует и т.д.
## Множественные параметры тела и query
## Множество body-параметров и query
Конечно, вы также можете объявить дополнительные query-параметры, когда это необходимо, дополнительно к любым параметрам тела.
Конечно, вы также можете объявлять дополнительные query-параметры в любое время, дополнительно к любым body-параметрам.
Поскольку по умолчанию, отдельные значения интерпретируются как query-параметры, вам не нужно явно добавлять `Query`, вы можете просто сделать так:
@ -116,7 +116,7 @@ q: str | None = None
/// info | Информация
`Body` также имеет все те же дополнительные параметры валидации и метаданные, как `Query`, `Path` и другие, которые вы увидите позже.
`Body` также имеет все те же дополнительные параметры валидации и метаданных, как у `Query`, `Path` и других, которые вы увидите позже.
///
@ -126,13 +126,13 @@ q: str | None = None
По умолчанию **FastAPI** ожидает получить тело запроса напрямую.
Но если вы хотите, чтобы он ожидал JSON с ключом `item`, внутри которого будет содержимое модели, как это происходит при объявлении дополнительных параметров тела, вы можете использовать специальный параметр `embed` типа `Body`:
Но если вы хотите, чтобы он ожидал JSON с ключом `item` с содержимым модели внутри, как это происходит при объявлении дополнительных body-параметров, вы можете использовать специальный параметр `embed` у типа `Body`:
Вы можете добавлять несколько параметров тела для вашей *функции операции пути*, даже если запрос может содержать только одно тело.
Вы можете добавлять несколько body-параметров вашей *функции операции пути*, несмотря на то, что запрос может содержать только одно тело.
**FastAPI** справится с этим, предоставит вам правильные данные в вашей функции, проведет валидацию и документирование правильной схемы в*операции пути*.
Но **FastAPI** справится с этим, предоставит вам правильные данные в вашей функции, а также выполнит валидацию и документирование правильной схемы *операции пути*.
Вы также можете объявлять отдельные значения для получения в рамках тела запроса.
И вы можете указать **FastAPI** встраивать тело запроса в ключ, даже если объявлен только один параметр.
И вы можете настроить **FastAPI** так, чтобы включить тело запроса в ключ, даже если объявлен только один параметр.
Можно также задать список как `"*"` (подстановочный символ), чтобы разрешить все источники.
Можно указать список, используя подстановочный символ `"*"`, чтобы разрешить любые источники.
Но тогда разрешатся только определённые виды взаимодействия, исключая всё, что связано с учётными данными: куки, заголовки Authorization такие, как те, что используются с токенами Bearer и т.д.
Но тогда не будут разрешены некоторые виды взаимодействия, включая всё, что связано с учётными данными: куки, заголовки Authorization с токенами Bearer и т.п.
Поэтому, чтобы всё функционировало правильно, лучше явно указывать разрешённые источники.
@ -46,20 +46,17 @@
* Специфические HTTP-методы (`POST`, `PUT`) или все из них с помощью подстановочного символа `"*"`.
* Специфические HTTP-заголовки или все из них с помощью подстановочного символа `"*"`.
{* ../../docs_src/cors/tutorial001.py hl[2,6:12,14:20] *} <!-- Updated highlighting lines as '13:19' changed to '14:20' in the new code block -->
По умолчанию параметры, используемые реализацией `CORSMiddleware`, являются ограничительными, поэтому вам потребуется явно разрешить конкретные источники, методы или заголовки, чтобы браузеры могли использовать их в междоменном контексте.
По умолчанию параметры, используемые в реализации `CORSMiddleware`, имеют ограничительные значения, поэтому вам необходимо явно разрешить использование отдельных источников, методов или заголовков, чтобы браузеры могли использовать их в кросс-доменном контексте.
Поддерживаются следующие аргументы:
* `allow_origins` - Список источников, которым разрешено выполнять междоменные запросы. Например, `['https://example.org', 'https://www.example.org']`. Вы можете использовать `['*']`, чтобы разрешить любой источник.
* `allow_origin_regex` - Строка регулярного выражения для соответствия источникам, которым разрешено выполнять междоменные запросы, например, `'https://.*\.example\.org'`.
* `allow_methods` - Список HTTP-методов, которые должны быть разрешены для междоменных запросов. По умолчанию равно `['GET']`. Вы можете использовать `['*']`, чтобы разрешить все стандартные методы.
* `allow_headers` - Список HTTP-заголовков запросов, которые должны поддерживаться при междоменных запросах. По умолчанию равно `[]`. Вы можете использовать `['*']`, чтобы разрешить все заголовки. Заголовки `Accept`, `Accept-Language`, `Content-Language` и `Content-Type` всегда разрешены для <ahref="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests"class="external-link"rel="noopener"target="_blank">простых CORS-запросов</a>.
* `allow_credentials` - Указывает, что куки должны поддерживаться для междоменных запросов. По умолчанию равно `False`.
Нельзя установить значения `allow_origins`, `allow_methods` и `allow_headers` в `['*']`, если `allow_credentials` установлено в `True`. Все они должны быть <ahref="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards"class="external-link"rel="noopener"target="_blank">явно определены</a>.
* `allow_origins` - Список источников, на которые разрешено выполнять кросс-доменные запросы. Например, `['https://example.org', 'https://www.example.org']`. Можно использовать `['*']`, чтобы разрешить любые источники.
* `allow_origin_regex` - Регулярное выражение для определения источников, на которые разрешено выполнять кросс-доменные запросы. Например, `'https://.*\.example\.org'`.
* `allow_methods` - Список HTTP-методов, которые разрешены для кросс-доменных запросов. По умолчанию равно `['GET']`. Можно использовать `['*']`, чтобы разрешить все стандартные методы.
* `allow_headers` - Список HTTP-заголовков, которые должны поддерживаться при кросс-доменных запросах. По умолчанию равно `[]`. Можно использовать `['*']`, чтобы разрешить все заголовки. Заголовки `Accept`, `Accept-Language`, `Content-Language` и `Content-Type` всегда разрешены для <ahref="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests"class="external-link"rel="noopener"target="_blank">простых CORS-запросов</a>.
* `allow_credentials` - Указывает, что куки разрешены в кросс-доменных запросах. По умолчанию равно `False`. Ни один из параметров `allow_origins`, `allow_methods` и `allow_headers` не может быть установлен в `['*']`, если `allow_credentials` установлен в `True`. Все они должны быть <ahref="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards"class="external-link"rel="noopener"target="_blank">явно указаны</a>.
* `expose_headers` - Указывает любые заголовки ответа, которые должны быть доступны браузеру. По умолчанию равно `[]`.
* `max_age` - Устанавливает максимальное время в секундах, в течение которого браузеры могут кэшировать CORS-ответы. По умолчанию равно `600`.
В Pydantic версии 1 метод назывался `.dict()`, он был устаревшим (но все еще поддерживается) в Pydantic версии 2 и переименован в `.model_dump()`.
В Pydantic v1 метод назывался `.dict()`, в Pydantic v2 он был устаревшим (но всё ещё поддерживается) и переименован в `.model_dump()`.
Примеры здесь используют `.dict()` для совместимости с Pydantic версии 1, но вы должны использовать `.model_dump()`, если можете применять Pydantic версии 2.
В примерах здесь используется `.dict()` для совместимости с Pydantic v1, но если вы можете использовать Pydantic v2, следует использовать `.model_dump()`.
///
@ -90,7 +91,7 @@ UserInDB(
)
```
Или, более точно, используя напрямую `user_dict`, с любым будущим содержимым:
Или более точно, используя `user_dict` напрямую, с любым потенциальным содержимым:
```Python
UserInDB(
@ -122,7 +123,7 @@ UserInDB(**user_in.dict())
#### Распаковка `dict` и дополнительные именованные аргументы
И затем, добавляя дополнительный именованный аргумент `hashed_password=hashed_password`, как в:
И затем, если мы добавим дополнительный именованный аргумент `hashed_password=hashed_password`, как здесь:
Поддерживающие дополнительные функции `fake_password_hasher` и `fake_save_user` существуют исключительно для демонстрации возможного потока данных, но, конечно, не обеспечивают реальной безопасности.
Используемые в примере вспомогательные функции `fake_password_hasher` и `fake_save_user` служат лишь для демонстрации возможного потока данных, но, конечно, они не обеспечивают настоящую безопасность.
///
@ -150,15 +151,15 @@ UserInDB(
Сокращение дублирования кода — одна из основных идей **FastAPI**.
Поскольку дублирование кода увеличивает вероятность появления ошибок, проблем с безопасностью, проблем с десинхронизацией кода (когда вы обновляете что-то в одном месте, но не в других), и т.д.
Поскольку дублирование кода увеличивает риск появления багов, проблем с безопасностью, проблем десинхронизации кода (когда вы обновляете код в одном месте, но не обновляете в другом), и т.д.
И все эти модели разделяют много данных и дублируют названия и типы атрибутов.
Мы могли бы сделать лучше.
Мы можем определить модель `UserBase`, которая будет базовой для наших других моделей. А затем мы можем создать подклассы этой модели, которые будут наследовать её атрибуты (объявления типов, валидацию и т.д.).
Мы можем объявить модель `UserBase`, которая будет базовой для остальных моделей. И затем мы можем создать подклассы этой модели, которые будут наследовать её атрибуты (объявления типов, валидацию и т.п.).
Все преобразования данных, валидация, документация и т.д. будут работать как обычно.
Все операции конвертации, валидации, документации и т.п. будут по-прежнему работать нормально.
Таким образом, мы можем определить только отличия между моделями (с открытым `password`, с `hashed_password` и без пароля):
@ -166,7 +167,7 @@ UserInDB(
## `Union` или `anyOf`
Вы можете объявить ответ как `Union` из двух или более типов, что значит, что ответ может быть любым из них.
Вы можете объявить ответ как `Union` из двух или более типов, это значит, что ответ должен соответствовать одному из них.
Он будет определён в OpenAPI как `anyOf`.
@ -192,7 +193,7 @@ UserInDB(
some_variable: PlaneItem | CarItem
```
Но если мы поместим это в присвоение `response_model=PlaneItem | CarItem`, мы получим ошибку, потому что Python попытается выполнить **недопустимую операцию** между `PlaneItem` и `CarItem` вместо интерпретации это как аннотацию типа.
Но если мы поместим это в `response_model=PlaneItem | CarItem`, мы получим ошибку, потому что Python попытается произвести **некорректную операцию** между `PlaneItem` и `CarItem` вместо того, чтобы интерпретировать это как аннотацию типа.
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> server </font></span>Сервер запущен по адресу<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://127.0.0.1:8000</u></font>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> server </font></span>Документация по адресу<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> server </font></span>Server started at<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://127.0.0.1:8000</u></font>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> server </font></span>Documentation at<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> tip </font></span>Запуск в режиме разработки, для использования в продакшене:
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> tip </font></span>Running in development mode, for production use:
<b>fastapi run</b>
Логи:
Logs:
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Будем отслеживать изменения в этих директориях:
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Will watch for changes in these directories:
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span> Uvicorn запущен по адресу<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://127.0.0.1:8000</u></font><b>(</b>нажмите CTRL+C
для завершения<b>)</b>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Процесс перезагрузки запущен<b>[</b><fontcolor="#34E2E2"><b>383138</b></font><b>]</b>с использованием WatchFiles
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Серверный процесс запущен<b>[</b><fontcolor="#34E2E2"><b>383153</b></font><b>]</b>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Ожидание запуска приложения.
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Запуск приложения завершен.
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span> Uvicorn running on<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://127.0.0.1:8000</u></font><b>(</b>Press CTRL+C
to quit<b>)</b>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Started reloader process<b>[</b><fontcolor="#34E2E2"><b>383138</b></font><b>]</b>using WatchFiles
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Started server process<b>[</b><fontcolor="#34E2E2"><b>383153</b></font><b>]</b>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Waiting for application startup.
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Application startup complete.
```
</div>
В выводе будет строка, похожая на:
В окне вывода появится следующая строка:
```hl_lines="4"
INFO: Uvicorn запущен по адресу http://127.0.0.1:8000 (нажмите CTRL+C для завершения)
@ -165,7 +165,7 @@ OpenAPI определяет схему API для вашего API. А эта
Здесь переменная `app` будет "экземпляром" класса `FastAPI`.
Это будет главная точка взаимодействия для создания и работы с вашим API.
Это единая точка входа для создания и взаимодействия с API.
### Шаг 3: создайте *операцию пути*
@ -316,8 +316,8 @@ https://example.com/items/foo
## Резюме
* Импортируйте `FastAPI`.
* Создайте экземпляр `app`.
* Напишите **декоратор операции пути** с помощью декораторов вроде `@app.get("/")`.
* Определите **функцию операции пути**; например, `def root(): ...`.
* Запустите сервер разработки с помощью команды `fastapi dev`.
* Импортируем`FastAPI`.
* Создаём экземпляр `app`.
* Пишем **декоратор операции пути** (такой как `@app.get("/")`).
* Пишем **функцию операции пути** (`def root(): ...`).
* Запускаем сервер в режиме разработки (`fastapi dev`).
`HTTPException` - это обычное исключение Python с дополнительными данными, актуальными для API.
@ -35,9 +35,9 @@
Это также означает, что если вы находитесь внутри функции, которая вызывается внутри вашей *функции операции пути*, и вы вызываете `HTTPException` внутри этой функции, то она не будет выполнять остальной код в *функции операции пути*, а сразу завершит запрос и отправит HTTP-ошибку из `HTTPException` клиенту.
О том, насколько выгоднее `вызывать` исключение, чем `возвращать` значение, будет рассказано в разделе, посвященном зависимости и безопасности.
Польза от `вызова` исключения, а не `возврата` значения, станет очевидной в разделе, посвящённом зависимостям и безопасности.
В данном примере, когда клиент запрашивает элемент по несуществующему ID, возникает исключение со статус-кодом `404`:
В этом примере, когда клиент запрашивает элемент по несуществующему ID, возникает исключение со статус-кодом `404`:
Здесь, если запросить`/unicorns/yolo`, то *операция пути* вызовет `UnicornException`.
Здесь, если вы запросите`/unicorns/yolo`, то *операция пути* вызовет `UnicornException`.
Но оно будет обработано `unicorn_exception_handler`.
@ -232,9 +232,11 @@ path -> item_id
И класс ошибок **FastAPI**`HTTPException` наследует от класса ошибок Starlette `HTTPException`.
Единственное отличие состоит в том, что `HTTPException` от **FastAPI** позволяет добавлять заголовки, которые будут включены в ответ, в то время как `HTTPException` от Starlette принимает только строки.
Единственное отличие заключается в том, что `HTTPException` от **FastAPI** позволяет добавлять заголовки, которые будут включены в ответ.
Таким образом, вы можете продолжать вызывать `HTTPException` от **FastAPI** как обычно в своем коде.
Он необходим/используется внутри системы для OAuth 2.0 и некоторых утилит безопасности.
Таким образом, вы можете продолжать вызывать `HTTPException` от **FastAPI** как обычно в своём коде.
Но когда вы регистрируете обработчик исключений, вы должны зарегистрировать его для `HTTPException` от Starlette.
Этот учебник показывает, как использовать **FastAPI** со многими его функциями, шаг за шагом.
Этот учебник показывает вам, как использовать **FastAPI** с большинством его функций, шаг за шагом.
Каждый раздел постепенно опирается на предыдущие, но структура разбита на отдельные темы, чтобы вы могли перейти прямо к любой конкретной теме для решения своих специфичных нужд API.
Каждый раздел постепенно основан на предыдущих, но он структурирован по отдельным темам, чтобы вы могли перейти непосредственно к конкретной теме для решения ваших конкретных потребностей в API.
Он также создан для использования в качестве справочной информации в будущем, чтобы вы могли возвращаться и находить именно то, что вам нужно.
## Запуск кода
Этот учебник также предназначен для использования в качестве справочника в будущем, так что вы можете вернуться и посмотреть именно то, что вам нужно.
Все блоки кода можно копировать и использовать напрямую (это действительно проверенные файлы Python).
Чтобы запустить любой из примеров, скопируйте код в файл `main.py` и запустите `fastapi dev` с помощью:
Все блоки кода можно копировать и использовать напрямую (на самом деле это проверенные файлы Python).
Чтобы запустить любой из примеров, скопируйте код в файл `main.py` и запустите `fastapi dev` с:
<divclass="termy">
```console
$ <fontcolor="#4E9A06">fastapi</font> dev <ustyle="text-decoration-style:solid">main.py</u>
<spanstyle="background-color:#009485"><fontcolor="#D3D7CF"> FastAPI </font></span>Начало работы сервера разработки 🚀
<spanstyle="background-color:#009485"><fontcolor="#D3D7CF"> FastAPI </font></span>Starting development server 🚀
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> server </font></span>Сервер запущен на<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://127.0.0.1:8000</u></font>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> server </font></span>Документация доступна по адресу<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> server </font></span>Server started at<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://127.0.0.1:8000</u></font>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> server </font></span>Documentation at<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> tip </font></span>Запущен в режиме разработки, для продакшена используйте:
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> tip </font></span>Running in development mode, for production use:
<b>fastapi run</b>
Логи:
Logs:
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Будет отслеживать изменения в этих каталогах:
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Will watch for changes in these directories:
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span> Uvicorn запущен на<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://127.0.0.1:8000</u></font><b>(</b>нажмите CTRL+C
для выхода<b>)</b>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Запущен процесс автоперезагрузки<b>[</b><fontcolor="#34E2E2"><b>383138</b></font><b>]</b>с использованием WatchFiles
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Запущен процесс сервера<b>[</b><fontcolor="#34E2E2"><b>383153</b></font><b>]</b>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Ожидание запуска приложения.
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Запуск приложения завершен.
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span> Uvicorn running on<fontcolor="#729FCF"><ustyle="text-decoration-style:solid">http://127.0.0.1:8000</u></font><b>(</b>Press CTRL+C
to quit<b>)</b>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Started reloader process<b>[</b><fontcolor="#34E2E2"><b>383138</b></font><b>]</b>using WatchFiles
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Started server process<b>[</b><fontcolor="#34E2E2"><b>383153</b></font><b>]</b>
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Waiting for application startup.
<spanstyle="background-color:#007166"><fontcolor="#D3D7CF"> INFO </font></span>Application startup complete.
```
</div>
**Настоятельно рекомендуется** написать или скопировать код, отредактировать его и запустить локально.
Использование кода в вашем редакторе действительно показывает вам преимущества FastAPI, видя, как мало кода вам нужно написать, все проверки типов, автодополнение и т.д.
Использование его в вашем редакторе показывает вам преимущества FastAPI, видя, как мало кода вам нужно написать, все проверки типов, автозавершение и т.д.
---
@ -62,7 +62,7 @@ $ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid
Первый шаг — установить FastAPI.
Убедитесь, что вы создали [виртуальную среду](../virtual-environments.md){.internal-link target=_blank}, активировали ее, а затем **установили FastAPI**:
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его, а затем **установили FastAPI**:
При установке с помощью `pip install "fastapi[standard]"` поставляются некоторые стандартные дополнительные зависимости, включая `fastapi-cloud-cli`, который позволяет развертывать приложения на <ahref="https://fastapicloud.com"class="external-link"target="_blank">FastAPI Cloud</a>.
Если вы устанавливаете с помощью `pip install "fastapi[standard]"`, это включает в себя некоторые стандартные необязательные зависимости, включая `fastapi-cloud-cli`, который позволяет развернуть на <ahref="https://fastapicloud.com"class="external-link"target="_blank">FastAPI Cloud</a>.
Если вы не хотите иметь эти необязательные зависимости, вы можете установить `pip install fastapi`.
Если вы не хотите иметь эти необязательные зависимости, можно установить `pip install fastapi`.
Если вы хотите установить стандартные зависимости, но без `fastapi-cloud-cli`, вы можете установить с помощью `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
Если вы хотите установить стандартные зависимости, но без `fastapi-cloud-cli`, установите с помощью `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
///
## Продвинутое руководство пользователя
Существует также **Продвинутое руководство пользователя**, которое вы можете прочитать после **Учебника - Руководства пользователя**.
Существует также **Продвинутое руководство пользователя**, которое вы сможете прочитать после **Учебника - Руководства пользователя**.
**Продвинутое руководство пользователя** основывается на этом, использует те же концепции и обучает вас некоторым дополнительным функциям.
**Продвинутое руководство пользователя** основывается на этом, использует те же концепции и учит вас некоторым дополнительным функциям.
Но сначала вам следует прочитать **Учебник - Руководство пользователя** (то, что вы читаете прямо сейчас).
Но сначала вы должны прочитать **Учебник - Руководство пользователя** (то, что вы читаете прямо сейчас).
Он разработан так, чтобы вы могли создать полноценное приложение, используя только **Учебник - Руководство пользователя**, а затем расширить его различными способами, в зависимости от ваших нужд, с использованием некоторых дополнительных идей из **Продвинутого руководства пользователя**.
Он разработан таким образом, чтобы вы могли создать полноценное приложение, используя только **Учебник - Руководство пользователя**, а затем расширить его различными способами, в зависимости от ваших потребностей, используя некоторые дополнительные идеи из **Продвинутого руководства пользователя**.
Вы можете добавить промежуточное ПО (middleware) в приложения на **FastAPI**.
Вы можете добавить промежуточный слой (middleware) в **FastAPI** приложение.
"Промежуточное ПО" - это функция, которая работает с каждым **запросом** до того, как он обработается какой-либо конкретной *операцией пути*. А также с каждым **ответом** перед его возвратом.
"Middleware" это функция, которая выполняется с каждым **запросом** до его обработки какой-либо конкретной *операцией пути*. А также с каждым **ответом** перед его возвращением.
* Оно обрабатывает каждый **запрос**, поступающий в ваше приложение.
* Может что-то сделать с этим **запросом** или выполнить любой необходимый код.
@ -19,9 +19,9 @@
///
## Создание промежуточного ПО
## Создание middleware
Чтобы создать промежуточное ПО, используйте декоратор `@app.middleware("http")` над функцией.
Для создания middleware используйте декоратор `@app.middleware("http")` над функцией.
Здесь мы используем <ahref="https://docs.python.org/3/library/time.html#time.perf_counter"class="external-link"target="_blank">`time.perf_counter()`</a> вместо `time.time()`, так как он может быть более точным для этих случаев использования. 🤓
Мы используем <ahref="https://docs.python.org/3/library/time.html#time.perf_counter"class="external-link"target="_blank">`time.perf_counter()`</a> вместо `time.time()` для обеспечения большей точности наших примеров. 🤓
///
## Порядок выполнения нескольких промежуточных ПО
## Порядок выполнения нескольких middleware
Когда вы добавляете несколько промежуточных ПО с использованием либо декоратора `@app.middleware()`, либо метода `app.add_middleware()`, каждое новое промежуточное ПО оборачивает приложение, формируя стек. Последнее добавленное промежуточное ПО является *самым внешним*, а первое - *самым внутренним*.
Когда вы добавляете несколько middleware с помощью декоратора `@app.middleware()`или метода `app.add_middleware()`, каждое новое middleware оборачивает приложение, образуя стек. Последнее добавленное middleware будет *внешним*, а первое — *внутренним*.
На пути запроса сначала выполняется *самое внешнее* промежуточное ПО.
На этапе запроса сначала выполняется *внешнее* middleware.
Это поведение стека гарантирует, что промежуточные ПО выполняются в предсказуемом и контролируемом порядке.
Такое поведение стека обеспечивает выполнение middleware в предсказуемом и контролируемом порядке.
## Другие промежуточные ПО
## Другие middleware
Вы можете позже прочитать больше о других промежуточных ПО в разделе [Advanced User Guide: Advanced Middleware](../advanced/middleware.md){.internal-link target=_blank}.
О других middleware вы можете узнать больше в разделе [Advanced User Guide: Advanced Middleware](../advanced/middleware.md){.internal-link target=_blank}.
В следующем разделе вы узнаете, как настроить <abbrtitle="Cross-Origin Resource Sharing">CORS</abbr> с помощью промежуточного ПО.
Query-параметр `q` имеет тип `str | None`, это значит, что он имеет тип `str`, но также может быть `None`. Более того, значение по умолчанию равно `None`, поэтому FastAPI определит, что он не является обязательным.
Query-параметр `q` имеет тип `str | None`. Это означает, что входной параметр будет типа `str`, но может быть и `None`. Ещё параметр имеет значение по умолчанию `None`, из-за чего FastAPI определит параметр как необязательный.
/// note | Технические детали
FastAPI определит, что значение `q` не является обязательным благодаря значению по умолчанию `= None`.
Использование `str | None` позволит вашему редактору кода обеспечить лучшую поддержку и обнаружение ошибок.
`str | None` позволит редактору кода оказать вам лучшую поддержку и найти ошибки.
///
## Дополнительная валидация
Мы хотим убедиться, что даже если `q` является опциональным, когда он предоставлен, **его длина не превышает 50 символов**.
Добавим дополнительное условие валидации параметра `q` — **длина строки не более 50 символов**.
FastAPI добавил поддержку `Annotated` (и стал рекомендовать его) в версии 0.95.0.
FastAPI добавил поддержку `Annotated` (и начал рекомендовать его) в версии 0.95.0.
Если у вас более старая версия, вы получите ошибки при попытке использовать `Annotated`.
Убедитесь, что вы [обновите версию FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} до как минимум 0.95.1 перед использованием `Annotated`.
Убедитесь, что вы [обновили версию FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} до как минимум 0.95.1 перед использованием `Annotated`.
Обратите внимание, что значение по умолчанию все еще `None`, так что параметр остается необязательным.
Но теперь, имея `Query(max_length=50)` внутри `Annotated`, мы говорим FastAPI, что хотим иметь **дополнительную валидацию** для этого значения, мы хотим, чтобы оно содержало максимум 50 символов. 😎
Однако теперь, имея `Query(max_length=50)` внутри `Annotated`, мы говорим FastAPI, что мы хотим иметь **дополнительные условия валидации** для этого значения, чтобы его длина не превышала 50 символов. 😎
/// tip | Подсказка
Здесь мы используем `Query()`, потому что это **query-параметр**. Позже мы увидим другие, такие как `Path()`, `Body()`, `Header()`, и `Cookie()`, которые также принимают те же аргументы, что и `Query()`.
Здесь мы используем `Query()`, так как это **query-параметр**. Позже мы увидим другие параметры, такие как `Path()`, `Body()`, `Header()`, и `Cookie()`, которые также принимают те же аргументы, что и `Query()`.
///
Теперь FastAPI:
* **Проверит** данные, убедившись, что максимальная длина составляет 50 символов
* Покажет **четкую ошибку** клиента, когда данные невалидны
* **Документирует** параметр в схеме OpenAPI *операции пути* (что будет отображаться в **автоматической документации**)
* **Валидирует** (проверяет), что полученные данные состоят максимум из 50 символов
* Показывает **понятную ошибку** для клиента в случаях, когда данные не валидны
* **Документирует** параметр в схему OpenAPI *операции пути* (что отображается в **UI автоматической документации**)
## Альтернативный (старый): `Query` как значение по умолчанию
## Альтернативный (устаревший) способ: `Query` как значение по умолчанию
В предыдущих версиях FastAPI (до <abbrtitle="раньше 2023-03">0.95.0</abbr>) требовалось использовать `Query` как значение по умолчанию для вашего параметра, вместо размещения его в `Annotated`. Есть высокая вероятность, что вы увидите код, использующий его, поэтому я объясню его вам.
В предыдущих версиях FastAPI (до <abbrtitle="ранее 2023-03">0.95.0</abbr>) необходимо было использовать `Query` как значение по умолчанию для query-параметра. Так было вместо размещения его в `Annotated`, так что велика вероятность, что вам встретится такой код. Сейчас объясню.
В этом случае (без использования `Annotated`) мы заменяем значение по умолчанию `None` функции на `Query()`, теперь нам необходимо установить значение по умолчанию с параметром `Query(default=None)`, это служит той же цели определения значения по умолчанию (по крайней мере для FastAPI).
Это проверит данные, покажет четкую ошибку, когда данные невалидны, и задокументирует параметр в OpenAPI схеме*операции пути*.
Входные данные будут проверены. Если данные недействительны, тогда будет указано на ошибку в запросе. Параметр также будет задокументирован в схеме OpenAPI данной*операции пути*.
### `Query` как значение по умолчанию или в `Annotated`
Имейте в виду, что при использовании `Query` внутри `Annotated` вы не можете использовать параметр `default` у `Query`.
Вместо этого используйте фактическое значение по умолчанию параметра функции. В противном случае это будет не совместимо.
Вместо этого, используйте обычное указание значения по умолчанию для параметра функции. Иначе это будет несовместимо.
**Использование `Annotated` рекомендуется** вместо значения по умолчанию в параметрах функции, это **лучше** по нескольким причинам. 🤓
**Рекомендуется использовать `Annotated`** вместо значения по умолчанию в параметрах функции. Это **лучше** по нескольким причинам. 🤓
**Значение по умолчанию** **параметра функции** - это **фактически значение по умолчанию**, что более интуитивно для общего использования Python. 😌
Значение **по умолчанию** у **параметров функции** — это **действительно значение по умолчанию**, что более интуитивно понятно для пользователей Python. 😌
Вы можете **вызвать**эту же функцию в **других местах** без FastAPI, и она **будет работать как ожидается**. Если есть**обязательный** параметр (без значения по умолчанию), ваш **редактор** сообщит вам об этом ошибкой, **Python** также укажет на нее, если вы запустите его без передачи обязательного параметра.
Вы можете **вызвать** ту же функцию в **иных местах** без FastAPI, и она **сработает как ожидалось**. Если это**обязательный** параметр (без значения по умолчанию), ваш **редактор кода** сообщит об ошибке. **Python** также укажет на ошибку, если вы вызовете функцию без передачи ей обязательного параметра.
Когда вы не используете `Annotated` и вместо этого пользуетесь **(старым) стилем значений по умолчанию**, если вы вызовете эту функцию без FastAPI в **других местах**, вам нужно **помнить** передавать аргументы функции, чтобы она работала правильно, в противном случае значения будут отличаться от тех, которые вы ожидаете (например, `QueryInfo` или что-то подобное вместо `str`). И ваш редактор не будет жаловаться, и Python не будет жаловаться, выполняя эту функцию, только операции внутри этой функции будут давать сбой.
Если вы вместо `Annotated` используете **(устаревший) стиль значений по умолчанию**, тогда при вызове этой функции без FastAPI в **других местах** вам необходимо **помнить** о передаче аргументов функции, чтобы она работала корректно. В противном случае, значения будут отличаться от тех, что вы ожидаете (например, `QueryInfo` или что-то подобное вместо `str`). И ни ваш редактор кода, ни Python не будут жаловаться на работу этой функции, только когда вычисления внутри дадут сбой.
Так как `Annotated` может иметь более одной аннотации метаданных, вы теперь даже можете использовать одну и ту же функцию с другими инструментами, такими как <ahref="https://typer.tiangolo.com/"class="external-link"target="_blank">Typer</a>. 🚀
Это регулярное выражение проверяет, что полученное значение параметра:
* `^`: начинается с следующих символов, перед которыми ничего нет.
* `fixedquery`: имеет точное значение`fixedquery`.
* `$`: заканчивается здесь, после `fixedquery` нет символов.
* `^`: начинается с следующих символов, не имеет символов перед.
* `fixedquery`: в точности содержит строку`fixedquery`.
* `$`: заканчивается там, где `fixedquery`, не имеет символов после.
Если вам сложно дается концепция **"регулярные выражения"**, не беспокойтесь. Для многих людей это трудная тема. Вы можете делать множество вещей, не прибегая к регулярным выражениям.
Теперь вы знаете, что когда они вам понадобятся, вы сможете использовать их в **FastAPI**.
Теперь вы знаете, что, когда вам понадобятся регулярные выражения, вы сможете использовать их в **FastAPI**.
### Pydantic v1 `regex` вместо `pattern`
### `regex` в Pydantic v1 вместо `pattern`
До версии Pydantic 2 и до FastAPI 0.100.0 параметр назывался `regex` вместо `pattern`, но теперь он устарел.
До версии Pydantic 2 и FastAPI 0.100.0, параметр назывался `regex` вместо `pattern`, но сейчас он считается устаревшим.
Вы все еще можете увидеть код, использующий его:
Вы всё ещё можете встретить код, использующий его:
Но знайте, что это устаревшая версия и ее следует обновить для использования нового параметра `pattern`. 🤓
Однако знайте, что это устарело и стоит обновить используемый параметр на `pattern`. 🤓
## Значения по умолчанию
Разумеется, вы можете использовать значения по умолчанию, отличные от `None`.
Вы можете также указать любое значение `по умолчанию`, отличное от `None`.
Предположим, вы хотите объявить query-параметр `q` с `min_length` равным `3` и со значением по умолчанию `"fixedquery"`:
Например, если вы хотите для параметра запроса `q` указать, что он должен состоять минимум из 3 символов (`min_length=3`) и иметь значение по умолчанию `"fixedquery"`:
Когда нам не нужно объявлять больше валидации или метаданных, мы можем сделать query-параметр `q` обязательным, просто не объявляя значение по умолчанию, например:
Когда вам не требуется дополнительная валидация или другие метаданные для параметра запроса, вы можете сделать параметр `q` обязательным просто не указывая значения по умолчанию. Например:
```Python
q: str
@ -248,7 +248,7 @@ q: str
q: str | None = None
```
Но мы теперь определяем его с `Query`, например так:
Но в настоящее время мы определяем его через `Query`. Например:
Вы можете объявить, что параметр может принимать значение `None`, но при этом является обязательным. Это заставит клиентов отправлять значение, даже если оно равно `None`.
Для этого вы можете объявить, что `None` допустимый тип, но просто не указывать значение по умолчанию:
Чтобы этого добиться, вам нужно определить `None` как валидный тип для параметра запроса, но не указывать значение по умолчанию:
## Список значений для query-параметра / несколько значений
## Множество значений для query-параметра
Когда вы явно определяете query-параметр с помощью `Query`, вы также можете указать его как принимающий список значений или, иначе говоря, принимающий несколько значений.
Для query-параметра `Query` можно указать, что он принимает список значений (множество значений).
Например, чтобы объявить query-параметр `q`, который может появляться несколько раз в URL, вы можете написать:
@ -340,7 +340,7 @@ http://localhost:8000/items/
Имейте в виду, что в этом случае FastAPI не будет проверять содержимое списка.
Например, `list[int]` бы проверяла (и документировала), что содержимое списка является целыми числами. Но простой `list` не будет этого делать.
Например, для List[int] список будет валидирован (и задокументирован) на содержание только целочисленных элементов. Но для простого `list` такой проверки не будет.
///
@ -354,11 +354,11 @@ http://localhost:8000/items/
Имейте в виду, что разные инструменты могут иметь разный уровень поддержки OpenAPI.
Некоторые из них могут не показывать всю дополнительную информацию, хотя в большинстве случаев отсутствующая функция уже запланирована к разработке.
Некоторые из них могут не отображать всю заявленную дополнительную информацию, хотя в большинстве случаев отсутствующая функция уже запланирована к разработке.
///
Вы можете добавить `title`:
Вы можете добавить название query-параметра, используя параметр `title`:
Могут быть случаи, когда вам потребуется выполнить **кастомную валидацию**, которая не может быть выполнена с показанными выше параметрами.
Могут быть случаи, когда вам нужно исполнить **кастомную валидацию**, которую нельзя выполнить с использованием только параметров, описанных выше.
В таких случаях вы можете использовать **кастомную функцию валидации**, которая применяется после обычной валидации (например, после проверки что значение является`str`).
В таких сценариях, вы можете использовать **кастомную функцию-валидатор**, которая применяется после обычной валидации (например, после проверки значения на совпадение с`str`).
Вы можете это сделать, используя <ahref="https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator"class="external-link"target="_blank">Pydantic's `AfterValidator`</a> внутри `Annotated`.
Это можно сделать, используя <ahref="https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator"class="external-link"target="_blank">`AfterValidator` из Pydantic</a> внутри `Annotated`.
/// tip | Подсказка
@ -420,57 +420,57 @@ Pydantic также имеет <a href="https://docs.pydantic.dev/latest/concept
///
Например, этот кастомный валидатор проверяет, что идентификатор предмета начинается с `isbn-` для номера книги ISBN или с `imdb-` для идентификатора URL-адреса фильма из IMDB:
Например, следующий кастомный валидатор проверяет, начинается ли ID статьи с `isbn-` (для международного стандартного номера книги ISBN) или с `imdb-` (для URL-идентификатора фильма на IMDb):
Это доступно начиная с версии Pydantic 2 или выше. 😎
///
/// tip | Подсказка
Если вам необходимо выполнить любую проверку, требующую связи с **внешним компонентом**, как например база данных или другой API, вам следует вместо этого использовать **FastAPI Dependencies**, вы узнаете о них позже.
Если вам нужно выполнить валидацию, которая требует связи с **внешним компонентом**, например с базой данных или другим API, используйте **FastAPI Dependencies**. Вы узнаете о них позже.
Эти кастомные валидаторы предназначены для вещей, которые могут быть проверены с использованием **только****тех же данных**, которые переданы в запросе.
Эти кастомные валидаторы предназначены для выполнения проверок, которые можно осуществить, используя **исключительно****те же данные**, переданные в запросе.
///
### Понять этот код
### Понимание кода
Главная идея использовать**`AfterValidator` с функцией внутри `Annotated`**. Не стесняйтесь пропустить эту часть. 🤸
Основное — это использование**`AfterValidator` с функцией внутри `Annotated`**. Если вы хотите, вы можете пропустить эту часть. 🤸
---
Но если вас интересует этот конкретный пример кода и вы все еще заинтересованы, вот некоторые дополнительные детали.
Но если вам интересно или вы всё ещё намерены продолжить, вот несколько дополнительных подробностей.
#### Строка с `value.startswith()`
Вы заметили? строка, использующая `value.startswith()`, может принимать кортеж, и она проверит каждое значение в кортеже:
Обратили внимание? строка, использующая `value.startswith()`, может принять кортеж, и она проверит каждое значение в кортеже:
С помощью `data.items()` мы получаем <abbrtitle="Что-то, на чем мы можем выполнять цикл for, как список, множество и т. д.">итерируемый объект</abbr> с кортежами, содержащими ключ и значение для каждого элемента словаря.
С `data.items()` мы получаем <abbrtitle="Что-то, что можно итерировать в цикле for, например, список, множества и т.д.">итерируемый объект</abbr> с кортежами, содержащими ключ и значение для каждого элемента словаря.
Мы преобразуем этот итерируемый объект в полноценный `list` с`list(data.items())`.
Мы преобразуем этот итерируемый объект в обычный `list` с помощью`list(data.items())`.
Затем с помощью`random.choice()` мы можем получить **случайное значение** из списка, так что мы получаем кортеж с `(id, name)`. Это будет что-то вроде `("imdb-tt0371724", "Автостопом по галактике")`.
Затем через`random.choice()` мы можем получить **случайное значение** из списка, это будет кортеж `(id, name)`. Пример: `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`.
Затем мы **присваиваем эти два значения** кортежа переменным `id` и `name`.
Потом мы **присваиваем эти два значения** из кортежа переменным `id` и `name`.
Таким образом, если пользователь не предоставил ID предмета, он все равно получит случайное предложение.
Так, если пользователь не передал ID элемента, они всё равно получат случайное предложение.
...все это мы делаем в **одной простой строке**. 🤯 Разве вы не любите Python? 🐍
...всё это выполняется в одной **простой строке**. 🤯 Разве вы не любите Python? 🐍
Так же, как вы можете задать модель ответа, вы можете указать HTTP код состояния, используемый для ответа, с помощью параметра `status_code` в любой из *операций пути*:
Так же, как вы можете указать модель ответа, вы также можете задать HTTP статус-код, используемый для ответа, с помощью параметра `status_code` в любой из *операций пути*:
* `@app.get()`
* `@app.post()`
@ -12,61 +12,61 @@
/// note | Примечание
Обратите внимание, что `status_code` является параметром метода "декоратора" (`get`, `post` и т.д.), а не вашей *функции обработки пути*, как остальные параметры и тело.
Обратите внимание, что `status_code` является параметром метода-декоратора (`get`, `post` и т.д.), а не вашей *функции-обработчика пути*, как все остальные параметры и тело.
///
Параметр `status_code` принимает число, обозначающее HTTP код состояния.
Параметр `status_code` принимает число, обозначающее HTTP статус-код.
/// info | Информация
`status_code` может также принимать `IntEnum`, такой как<ahref="https://docs.python.org/3/library/http.html#http.HTTPStatus"class="external-link"target="_blank">`http.HTTPStatus`</a> в Python.
В качестве значения параметра `status_code` может также использоваться `IntEnum`, например, из библиотеки Python<ahref="https://docs.python.org/3/library/http.html#http.HTTPStatus"class="external-link"target="_blank">`http.HTTPStatus`</a>.
///
Это позволит:
* Возвращать указанный код состояния в ответе.
* Документировать его в OpenAPI схеме (и, соответственно, в пользовательских интерфейсах):
* Возвращать указанный код статуса в ответе.
* Документировать его как таковой в OpenAPI схеме (а значит, и в пользовательском интерфейсе):
Некоторые коды состояния ответа (см. следующий раздел) указывают на то, что тело ответа отсутствует.
Некоторые коды ответа (см. следующий раздел) указывают, что ответа с телом не будет.
FastAPI об этом знает и создаст документацию OpenAPI, в которой будет указано, что тела ответа нет.
FastAPI знает об этом и создаст документацию OpenAPI, которая укажет, что тело ответа отсутствует.
///
## О HTTP кодах состояния
## Об HTTP статус-кодах
/// note | Примечание
Если вы уже знаете, что такое HTTP коды состояния, пропустите этот раздел.
Если вы уже знаете, что такое HTTP статус-коды, можете пропустить этот раздел.
///
В HTTP вы отправляете числовой код состояния из 3 цифр как часть ответа.
У этих кодов состояния есть название для их распознавания, но важной частью является число.
У этих кодов статуса есть связанные с ними названия для их распознавания, но важна именно числовая часть.
Вкратце:
Кратко о значениях:
* `100 - 199`предназначены для "Информации". Вы редко используете их напрямую. Ответы с этими кодами состояния не могут иметь тело.
* **`200 - 299`** предназначены для "Успешных" ответов. Это те, которые вы будете использовать чаще всего.
* `200` — код состояния по умолчанию, означающий, что все прошло "ОК".
* Другой пример — `201`, "Создано". Он обычно используется после создания новой записи в базе данных.
* Особый случай — `204`, "Нет содержимого". Этот ответ используется, когда нет содержимого для возврата клиенту, и поэтому ответ не должен иметь тела.
* **`300 - 399`** предназначены для "Перенаправления". Ответы с этими кодами состояния могут иметь или не иметь тело, за исключением `304`, "Не изменено", который не должен иметь тела.
* **`400 - 499`** предназначены для "Ошибки клиента". Это второй по распространенности тип использования.
* Пример — `404`, для ответа "Не найдено".
* Для общих ошибок клиента можно использовать `400`.
* `500 - 599`предназначены для ошибок сервера. Вы почти никогда не используете их напрямую. Когда что-то идет не так в вашем приложении или на сервере, автоматически возвращается один из этих кодов состояния.
* `100 - 199`— это информационные коды. Вы редко используете их напрямую. Ответы с этими кодами не должны содержать тело.
* **`200 - 299`** — это успешные ответы. Эти вы будете использовать чаще всего.
* `200` — это код по умолчанию, который означает, что все прошло "OK".
* Другой пример — `201`, "Created". Он часто используется после создания новой записи в базе данных.
* Особый случай — `204`, "No Content". Этот ответ используется, когда нет содержимого для возврата клиенту, и поэтому тело отсутствует.
* **`300 - 399`** — это коды перенаправлений. Ответы с этими кодами статуса могут, а могут и не иметь тело, за исключением `304`, "Not Modified", у которого его не должно быть.
* **`400 - 499`** — это ошибки клиента. Это вторая группа, которую вы, вероятно, будете использовать чаще всего.
* Пример — `404`, для ответа "Not Found".
* Для общих ошибок клиента можно использовать просто `400`.
* `500 - 599`— это ошибки сервера. Вы почти никогда не используете их напрямую. Когда что-то идет не так в коде вашего приложения или на сервере, он автоматически возвращает один из этих кодов.
/// tip | Подсказка
/// tip | Совет
Чтобы узнать больше о каждом коде состояния и их значении, ознакомьтесь с <ahref="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status"class="external-link"target="_blank"><abbrtitle="Mozilla Developer Network">документацией MDN о HTTP кодах состояния</a>.
Чтобы узнать больше о каждом статус-коде и для чего он предназначен, ознакомьтесь с <ahref="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status"class="external-link"target="_blank">документацией <abbrtitle="Mozilla Developer Network">MDN</abbr> об HTTP кодах статуса</a>.
///
@ -76,7 +76,7 @@ FastAPI об этом знает и создаст документацию Open
@ -92,10 +92,10 @@ FastAPI об этом знает и создаст документацию Open
Вы также можете использовать `from starlette import status`.
**FastAPI** предоставляет `starlette.status`так же, как `fastapi.status` исключительно для удобства разработчиков. Но `fastapi.status` поставляется непосредственно из Starlette.
**FastAPI** предоставляет `starlette.status` как `fastapi.status` исключительно для удобства разработчиков. Однако он поставляется непосредственно из Starlette.
///
## Изменение значения по умолчанию
## Изменение кода по умолчанию
Позже, в [Руководстве для продвинутых пользователей](../advanced/response-change-status-code.md){.internal-link target=_blank}, вы узнаете, как возвращать другой код состояния, отличный от объявленного здесь по умолчанию.
Позже, в [Руководстве для продвинутых пользователей](../advanced/response-change-status-code.md){.internal-link target=_blank}, вы узнаете, как возвращать HTTP код статуса, отличный от используемого здесь кода по умолчанию.
**FastAPI** не требует использования реляционной базы данных. Вы можете воспользоваться любой базой данных, которую хотите.
**FastAPI** не требует от вас использования реляционной базы данных. Но вы можете использовать **любую базу данных**, которую захотите.
В этом разделе мы рассмотрим пример использования<ahref="https://sqlmodel.tiangolo.com/"class="external-link"target="_blank">SQLModel</a>.
Здесь мы увидим пример, использующий<ahref="https://sqlmodel.tiangolo.com/"class="external-link"target="_blank">SQLModel</a>.
**SQLModel** построен на основе <ahref="https://www.sqlalchemy.org/"class="external-link"target="_blank">SQLAlchemy</a> и Pydantic. Он был создан тем же автором, что и **FastAPI**, для обеспечения идеальной совместимости приложений на FastAPI, использующих **реляционные базы данных**.
**SQLModel** построен на основе <ahref="https://www.sqlalchemy.org/"class="external-link"target="_blank">SQLAlchemy</a> и Pydantic. Он был создан автором **FastAPI** и является идеальным выбором для приложений FastAPI, которые нуждаются в реляционных базах данных.
/// tip | Совет
Вы можете воспользоваться любой другой библиотекой для работы с реляционными (SQL) или нереляционными (NoSQL) базами данных, нельзя сказать, что FastAPI вас вынуждает использовать что-то конкретное. 😎
Вы можете использовать любую другую библиотеку для работы с реляционными (SQL) или нереляционными (NoSQL) базами данных, в некоторых случаях называемую <abbrtitle="ORM = Object Relational Mapper, это библиотека, где классы представляют SQL-таблицы, а экземпляры классов представляют строки в этих таблицах.">"ORMs"</abbr>. FastAPI не заставляет вас использовать что-то конкретное. 😎
///
Поскольку SQLModel основан на SQLAlchemy, можно легко использовать **любую базу данных**, поддерживаемую SQLAlchemy (что делает их также поддерживаемыми SQLModel), такие как:
Поскольку SQLModel основан на SQLAlchemy, вы можете легко использовать **любую базу данных, поддерживаемую** SQLAlchemy (соответственно поддерживаемую SQLModel), например:
* PostgreSQL
* MySQL
* SQLite
* Oracle
* Microsoft SQL Server, и т.д.
* Microsoft SQL Server и т.д.
В этом примере мы будем использовать **SQLite**, так как она состоит из единственного файла, и Python имеет интегрированную поддержку. Таким образом, вы сможете скопировать этот пример и запустить его как есть.
В этом примере мы будем использовать базу данных **SQLite**, потому что она использует один файл, и поддержка Python встроена. Таким образом, вы можете скопировать этот пример и запустить его как есть.
Позже, для вашей продукции, вы, возможно, захотите использовать сервер базы данных, например**PostgreSQL**.
В дальнейшем, для продакшн-версии вашего приложения, возможно, вам стоит использовать серверную базу данных, такую как**PostgreSQL**.
/// tip | Совет
Существует официальный генератор проектов с **FastAPI** и **PostgreSQL**, включающий frontend и другие инструменты: <ahref="https://github.com/fastapi/full-stack-fastapi-template"class="external-link"target="_blank">https://github.com/fastapi/full-stack-fastapi-template</a>
Существует официальный генератор проектов с **FastAPI** и **PostgreSQL**, включая frontend и другие инструменты: <ahref="https://github.com/fastapi/full-stack-fastapi-template"class="external-link"target="_blank">https://github.com/fastapi/full-stack-fastapi-template</a>
///
Это очень простое и короткое руководство. Если вы хотите узнать больше о базах данных в целом, о SQL или более продвинутых функциях, обратитесь к <ahref="https://sqlmodel.tiangolo.com/"class="external-link"target="_blank">документации SQLModel</a>.
Это очень простое и короткое руководство. Если вы хотите узнать больше о базах данных в целом, об SQL или более продвинутых возможностях, обратитесь к <ahref="https://sqlmodel.tiangolo.com/"class="external-link"target="_blank">документации SQLModel</a>.
## Установка `SQLModel`
Сначала убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его и затем установили `sqlmodel`:
Сначала убедитесь, что вы создали свое [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его, а затем установили `sqlmodel`:
<divclass="termy">
@ -45,11 +45,11 @@ $ pip install sqlmodel
</div>
## Создание приложения с единственной моделью
## Создание приложения с одной моделью
Мы начнем с создания самой простой, первой версии приложения с одной моделью **SQLModel**.
Мы сначала создадим самую простую версию приложения с одной моделью **SQLModel**.
Позднее мы улучшим его, повышая безопасность и универсальность, добавив **несколько моделей** ниже. 🤓
Позже мы улучшим его, повысив безопасность и универсальность за счет **нескольких моделей** ниже. 🤓
Класс `Hero` очень похож на модель Pydantic (фактически, под капотом, он на самом деле *является моделью Pydantic*).
Класс `Hero` очень похож на модель Pydantic (по сути, под капотом, это действительно *модель Pydantic*).
Есть несколько отличий:
Но есть несколько различий:
* `table=True`указывает SQLModel, что это модель-таблица, она должна представлять **таблицу** в реляционной базе данных, а не просто модель данных (как любой другой обычный класс Pydantic).
* `table=True`говорит SQLModel, что это *модель таблицы*, она должна представлять **таблицу** в реляционной базе данных, это не просто *модель данных* (в отличие от обычного класса Pydantic).
* `Field(primary_key=True)`указывает SQLModel, что `id` является **первичным ключом** в базе данных SQL (вы можете узнать больше о первичных ключах баз данных в документации SQLModel).
* `Field(primary_key=True)`говорит SQLModel, что `id` является **первичным ключом** в базе данных SQL (более подробно о первичных ключах SQL можно узнать в документации SQLModel).
Тип `int | None`говорит SQLModel, что этот столбец должен иметь тип`INTEGER` в базе данных SQL и быть `NULLABLE`.
Тип `int | None`сообщает SQLModel, что этот столбец должен быть`INTEGER` в базе данных SQL и что он должен быть `NULLABLE`.
* `Field(index=True)`указывает SQLModel, что для этого столбца должен быть создан SQL-индекс, который позволит ускорить поиск в базе данных при чтении данных, отфильтрованных по этому столбцу.
* `Field(index=True)`говорит SQLModel, что он должен создать **SQL индекс** для этого столбца, что позволит ускорить поиск в базе данных при чтении данных, отфильтрованных по этому столбцу.
SQLModel будет считать, что данные, объявленные как `str`, будут храниться в базе данных как столбец типа `TEXT` (или `VARCHAR`, в зависимости от базы данных).
SQLModel будет знать, что нечто объявленное как `str` будет SQL-столбцом типа `TEXT` (или `VARCHAR`, в зависимости от базы данных).
### Создание `Engine`
### Создание подключения
Объект `engine` в SQLModel (под капотом это на самом деле `engine` из SQLAlchemy) **удерживает соединения** с базой данных.
Объект `engine` в SQLModel (по сути это `Engine` из SQLAlchemy) **удерживает соединения** с базой данных.
У вас будет **один объект `engine`** для всех ваших кодов, чтобы подключиться к одной и той же базе данных.
Для обеспечения всех подключений приложения к одной базе данных существует **единственный объект `engine`**.
Использование `check_same_thread=False` позволяет FastAPI использовать одну и ту же базу SQLite в разных потоках. Это необходимо, так как один единственный запрос может использовать более одного потока (например, в зависимостях).
Использование `check_same_thread=False` позволяет FastAPI использовать одну и ту же SQLite базу данных в различных потоках. Это необходимо, так как **один запрос** может использовать **более одного потока** (например, в зависимостях).
Не волнуйтесь, со структурой кода мы позаботимся о том, чтобы использовать **отдельную сессию SQLModel для каждого запроса** позже, это на самом деле то, что `check_same_thread` пытается реализовать.
Не беспокойтесь, с такой структурой кода, мы позаботимся о том, чтобы использовать **отдельную *сессию* SQLModel для каждого запроса** позже, именно это и пытается достичь `check_same_thread`.
### Создание таблиц
Затем мы добавляем функцию, использующую `SQLModel.metadata.create_all(engine)`, чтобы **создать таблицы** для всех моделей таблиц.
Далее мы добавляем функцию, использующую `SQLModel.metadata.create_all(engine)`, для того чтобы создать **таблицы** для всех **моделей таблиц**.
**`Session`** хранит **объекты в памяти** и отслеживает любые необходимые изменения данных, затем **использует `engine`** для связи с базой данных.
Сессия базы данных (**`Session`**) хранит **объекты в памяти** и отслеживает любые изменения в данных, затем она**использует `engine`** для связи с базой данных.
Мы создадим FastAPI-**зависимость** с помощью`yield`, которая будет предоставлять новую сессию `Session` для каждого запроса. Это как раз и обеспечит использование одной сессии для каждого запроса. 🤓
Мы создадим зависимость FastAPI с использованием`yield`, которая будет предоставлять новую `Session` для каждого запроса. Это именно то, что обеспечит использование отдельной сессии для каждого запроса. 🤓
Затем создадим `Annotated` зависимость `SessionDep`, чтобы упростить остальной код, который будет использовать эту зависимость.
Затем мы создадим объявленную зависимость `SessionDep`, чтобы упростить остальной код, который будет использовать эту зависимость.
Здесь мы создаем таблицы на событии запуска приложения.
Здесь мы создаём таблицы на событии начала работы приложения.
Для продукта вы, вероятно, будете использовать скрипт миграции, который запускается до запуска вашего приложения. 🤓
Для продакшн-версии вы, вероятно, будете использовать скрипт миграции базы данных, который выполняется перед запуском вашего приложения. 🤓
/// tip | Совет
SQLModel будет иметь утилиты миграции, оборачивающие Alembic, но сейчас вы можете использовать <ahref="https://alembic.sqlalchemy.org/en/latest/"class="external-link"target="_blank">Alembic</a> напрямую.
В SQLModel будут включены утилиты для миграции на основе Alembic, но пока вы можете использовать <ahref="https://alembic.sqlalchemy.org/en/latest/"class="external-link"target="_blank">Alembic</a> напрямую.
///
### Создание героя
Так как каждая модель SQLModel также является моделью Pydantic, вы можете использовать ее в тех же **аннотациях типов**, что и модели Pydantic.
Поскольку каждая модель в SQLModel также является моделью Pydantic, вы можете использовать её в тех же **аннотациях типов**, что и модели Pydantic.
Например, если вы объявляете параметр типа `Hero`, он будет считан из **тела JSON**.
Например, если вы объявите параметр типа `Hero`, он будет прочитан из **тела JSON**.
Таким же образом вы можете объявить его как **возвращаемый тип** функции, и тогда структура данных будет отображаться в автоматически созданной документации API.
Точно так же, вы можете объявить его как **возвращаемый тип функции**, и тогда форма данных будет отображаться в интерфейсе автоматической документации API.
Здесь мы используем зависимость `SessionDep` (объект `Session`), чтобы добавить нового `Hero` в экземпляр `Session`, зафиксировать изменения в базе данных, обновить данные в `hero`, а затем вернуть их.
Здесь мы используем зависимость `SessionDep` (сессию базы данных) для того, чтобы добавить нового героя `Hero` в объект сессии (`Session`), сохранить изменения в базе данных, обновить данные героя и затем вернуть их.
### Чтение данных героев
Мы можем **читать** героев`Hero` из базы данных, используя `select()`. Мы можем включить `limit` и `offset` для постраничного доступа к результатам.
Мы можем **читать**данных героев из базы данных, используя `select()`. Мы можем включить `limit` и `offset` для постраничного считывания результатов.
Затем перейдите в пользовательский интерфейс `/docs`, вы увидите, что **FastAPI** использует эти **модели** для **документирования** API, а также использует их для **сериализации** и **проверки** данных.
Затем перейдите в пользовательский интерфейс API `/docs`. Вы увидите, что **FastAPI** использует эти **модели** для **документирования** API, а также для **сериализации** и **проверки** данных.
Теперь давайте немного **отрефакторим**приложение для повышения **безопасности** и **универсальности**.
Теперь давайте немного **отрефакторим**это приложение, чтобы повысить его **безопасность** и **универсальность**.
Если вы посмотрите на предыдущее приложение, в пользовательском интерфейсе вы можете увидеть, что до сих пор оно позволяет клиенту определять `id` для создаваемого `Hero`. 😱
Если вы посмотрите на предыдущее приложение, в UI вы увидите, что до сих пор оно позволяет клиенту задавать `id` создаваемого героя (`Hero`). 😱
Мы не должны этого допускать, это может привести к перезаписи `id`, который был уже присвоен в базе данных. Определение `id` должно выполняться на уровне **backend** или **базы данных**, а не **клиентом**.
Нам нельзя допускать этого, так как они могут перезаписать `id`, который у нас уже есть в базе данных. Присвоение `id` должно происходить **на уровне бэкэнда** или **на уровне базы данных**, **не на уровне клиента**.
Кроме того, мы создаем `secret_name` для героя, но везде его возвращаем, это совсем не**секретно**... 😅
Кроме того, мы создаём `secret_name` для героя, но на данный момент мы возвращаем его везде, и это не очень**секретно**... 😅
Мы исправим эти вещи, добавив несколько **дополнительных моделей**. Здесь SQLModel будет по-настоящему полезен. ✨
Мы исправим это, добавив несколько **дополнительных моделей**. Вот где SQLModel действительно засияет. ✨
### Создание нескольких моделей
В **SQLModel** любая модель с `table=True` является **моделью таблицы**.
В **SQLModel** любая модель класса, содержащая`table=True`, является **моделью таблицы**.
А любые моделия без `table=True` являются **моделями данных**, которые на самом деле просто модели Pydantic (с парой небольших расширений). 🤓
А любая модель класса без `table=True` является **моделью данных**, они на самом деле представляют собой модели Pydantic (с несколькими небольшими дополнительными возможностями). 🤓
С помощью SQLModel мы можем использовать **наследование**, чтобы **избежать дублирования** всех полей во всех случаях.
#### `HeroBase` - базовый класс
Начнем с модели `HeroBase`, содержащей все **общие поля** для всех моделей:
Начнем с модели `HeroBase`, которая имеет все **общие поля** для всех моделей:
* `name`
* `age`
@ -196,12 +196,12 @@ $ fastapi dev main.py
#### `Hero` - *модель таблицы*
Затем создадим `Hero`, текущую модель таблицы с **дополнительными полями**, которые не всегда будут в других моделях:
Затем создадим `Hero`, это фактическая *модель таблицы*, с **дополнительными полями**, которых может не быть в других моделях:
* `id`
* `secret_name`
Поскольку `Hero` наследуется от `HeroBase`, он также имеет **поля**, объявленные в `HeroBase`, так что все поля для `Hero`:
Так как `Hero` наследует от `HeroBase`, он **также** содержит **поля**, объявленные в `HeroBase`, так что все поля для `Hero` следующие:
* `id`
* `name`
@ -212,23 +212,23 @@ $ fastapi dev main.py
#### `HeroPublic` - публичная *модель данных*
Далее создадим модель `HeroPublic`, которая будет **возвращаться** клиенту API.
Далее создаём модель `HeroPublic`, это модель, которая будет **возвращаться** клиентам API.
Она имеет те же поля, что и `HeroBase`, поэтому не будет содержать `secret_name`.
Она имеет те же поля, что и `HeroBase`, поэтому не будет включать `secret_name`.
Теперь личность наших героев защищена! 🥷
Наконец-то личности наших героев защищены! 🥷
Кроме того, она снова объявляет `id: int`. Делая это, мы заключаем **контракт** с клиентами API, чтобы они всегда могли ожидать, что `id` будет там и будет `int` (никогда не будет `None`).
Также она повторном объявляет `id: int`. Таким образом мы устанавливаем **договоренность** с клиентами API, что они всегда могут ожидать наличие `id` и то, что это будет `int` (оно никогда не будет `None`).
/// tip | Совет
Имейте модель ответа, обеспечивающую доступность значения и всегда `int` (не `None`), это очень полезно для клиентов API. Они могут писать намного проще код, имея эту уверенность.
Наличие в модели возврата гарантии того, что значение всегда будет доступно и будет `int` (не `None`), очень полезно для API-клиентов, они могут писать более простой код с этой уверенностью.
Кроме того, **автоматически сгенерированные клиенты** будут иметь более простые интерфейсы, чтобы разработчикам, работающим с вашим API, было значительно легче. 😎
Кроме того, автоматически сгенерированные клиенты будут иметь более простые интерфейсы, так что разработчикам, работающим с вашим API, будет значительно проще. 😎
///
Все поля в `HeroPublic` такие же, как и в `HeroBase`, с `id`, объявленным как `int` (не `None`):
Все поля в `HeroPublic` такие же, как в `HeroBase`, с `id`, объявленным как `int` (не `None`):
* `id`
* `name`
@ -238,17 +238,17 @@ $ fastapi dev main.py
#### `HeroCreate` - *модель данных* для создания героя
Теперь создадим модель `HeroCreate`, которая будет **проверять** данные клиентов.
Сейчас мы создаём модель `HeroCreate`. Это модель, которая будет **проверять** данные от клиентов.
Она имеет те же поля, что и `HeroBase`, а также включает`secret_name`.
Она содержит те же поля, что и `HeroBase`, а также `secret_name`.
Теперь, когда клиенты **создают нового героя**, они отправляют `secret_name`, который будет сохранен в базе данных, но эти секретные имена не будут возвращены в API клиентам.
Теперь, когда клиенты **создают нового героя**, они отправят `secret_name`, он будет сохранён в базе данных, но эти секретные имена не будут возвращаться в API клиентам.
/// tip | Совет
Вот как вы должны работать с **паролями**: получайте их, но не возвращайте через API.
Это то, как вы должны обрабатывать **пароли**: получайте их, но не возвращайте их в API.
Вы также должны **хэшировать** значения паролей перед их сохранением, **никогда не храните их в открытом виде**.
Также **хешируйте** значения паролей перед их сохранением, **никогда не храните их в открытом виде**.
#### `HeroUpdate` - *модель данных* для обновления героя
#### Модель для обновления героя `HeroUpdate`
В предыдущей версии приложения у нас не было способа **обновить героя**, но теперь с помощью **несколько моделей** мы сможем это сделать. 🎉
В предыдущих версиях нашего приложения у нас не было способа **обновить героя**, но теперь с **несколькими моделями** мы можем это сделать. 🎉
*Модель данных* `HeroUpdate` несколько особенная. Она имеет **все те же поля**, которые потребовались бы для создания нового героя, но все поля **опциональны** (у всех есть значение по умолчанию). Таким образом, при обновлении героя вы можете отправить только те поля, которые хотите обновить.
*Данные модели* `HeroUpdate` несколько особенные, у них есть **все те же поля**, которые потребуются для создания нового героя, но все поля **необязательны** (они все имеют значение по умолчанию). Таким образом, при обновлении героя вы можете отправить только те поля, которые хотите обновить.
Все **поля на самом деле меняются** (тип теперь включает `None`, и у них теперь есть значение по умолчанию `None`), поэтому мы должны **перечислить** их заново.
Поскольку **все поля фактически изменяются** (тип теперь включает `None` и теперь у них значение по умолчанию `None`), мы должны их **объявить заново**.
Мы не должны наследоваться от `HeroBase`, потому что мы заново объявляем все поля. Я оставлю наследование только для поддержания общего стиля, но это необязательно. 🤷
Нам не нужно наследовать от `HeroBase` потому что мы все равно повторном объявляем все поля. Я оставлю наследование просто для поддержания общего стиля, но это необязательно. Это скорее дело вкуса. 🤷
Поля `HeroUpdate`:
@ -280,29 +280,29 @@ $ fastapi dev main.py
### Создание с помощью `HeroCreate` и возврат `HeroPublic`
Теперь, когда у нас есть **несколько моделей**, мы можем обновить те части приложения, которые их используют.
Теперь, когда у нас есть **несколько моделей**, мы можем обновить части приложения, которые их используют.
В запросе мы получаем *модель данных*`HeroCreate`, и из нее создаем `Hero`*модель таблицы*.
Мы получаем в запросе модель данных `HeroCreate`, и из нее создаём модель таблицы `Hero`.
Эта новая *модель таблицы*`Hero` будет иметь поля, отправленные клиентом, и также `id`, сгенерированный базой данных.
Эта новая модель таблицы `Hero` будет иметь поля, отправленные клиентом, а также `id`, сгенерированное базой данных.
Затем мы возвращаем ту же *модель таблицы*`Hero` из функции. Но так как мы объявили `response_model` с *моделью данных*`HeroPublic`, **FastAPI** будет использовать `HeroPublic` для проверки и сериализации данных.
Затем возвращаем ту же модель таблицы `Hero`как есть из функции. Но так как мы объявляем `response_model` с моделью данных`HeroPublic`, **FastAPI** будет использовать `HeroPublic` для проверки и сериализации данных.
Теперь мы используем `response_model=HeroPublic` вместо **аннотации возвращаемого типа**`-> HeroPublic`, потому что значение, которое мы возвращаем, на самом деле *не является*`HeroPublic`.
Теперь мы используем `response_model=HeroPublic` вместо аннотации **возвращаемого типа**`-> HeroPublic`, потому что значение, которое мы возвращаем, **на самом деле не**`HeroPublic`.
Если бы мы объявили `-> HeroPublic`, ваш редактор и linter начали бы жаловаться (и вполне справедливо), что вы возвращаете `Hero`, а не `HeroPublic`.
Если бы мы объявили `-> HeroPublic`, ваш редактор и линтер бы пожаловались (и справедливо), что вы возвращаете `Hero`, а не `HeroPublic`.
Объявляя это в `response_model`, мы говорим **FastAPI** делать свое дело, не мешая аннотациям типов и не полагаясь на помощь от вашего редактора и других инструментов.
Объявляя это в `response_model`, мы говорим **FastAPI** делать свое дело, не вмешиваясь в аннотации типов и не полагаясь на помощь редактора и других инструментов.
///
### Чтение героев с `HeroPublic`
Мы можем сделать то же самое при **чтении** героев, опять же используем `response_model=list[HeroPublic]`, чтобы убедиться, что данные правильно проверены и сериализованы.
Мы можем сделать то же самое, как и раньше для **чтения** данных героев, используя `response_model=list[HeroPublic]`, чтобы обеспечить правильную валидацию и сериализацию данных.
Мы можем **обновить героя**. Для этого используем HTTP-метод`PATCH`.
Мы можем **обновить героя**. Для этого мы используем HTTP-операцию`PATCH`.
В коде мы получаем словарь `dict` с данными, отправленными клиентом, **только данными, отправленными клиентом**, исключаем любые значения, которые были бы там только из-за значений по умолчанию. Чтобы сделать это, используем `exclude_unset=True`. Это главная хитрость. 🪄
И в коде мы получаем объект `dict` со всеми данными, отправленными клиентом, содержанием **только данных, отправленных клиентом**, исключая любые значения, которые могут быть там только как значения по умолчанию. Для этого мы используем `exclude_unset=True`. Это главный трюк. 🪄
Затем используем `hero_db.sqlmodel_update(hero_data)`, чтобы обновить `hero_db` данными из `hero_data`.
Если вы зайдете в пользовательский интерфейс `/docs`, вы увидите, что он теперь обновлен, и не будет ожидать получить`id` от клиента при создании героя и т.д.
Если вы пойдете в пользовательский интерфейс API`/docs`, вы увидите, что он обновлен и больше не ожидает получение параметра`id` от клиента при создании нового героя и т.д.
Вы можете использовать <ahref="https://sqlmodel.tiangolo.com/"class="external-link"target="_blank">**SQLModel**</a> для взаимодействия с реляционными базами данных и упрощения работы с *моделями данных* и *моделями таблиц*.
Вы можете использовать <ahref="https://sqlmodel.tiangolo.com/"class="external-link"target="_blank">**SQLModel**</a> для взаимодействия с реляционной базой данных и упрощения кода с *моделями данных* и *моделями таблиц*.
Вы можете узнать намного больше в документации **SQLModel**, там есть более длительное <ahref="https://sqlmodel.tiangolo.com/tutorial/fastapi/"class="external-link"target="_blank">мини-руководство по использованию SQLModel с **FastAPI**</a>. 🚀
Вы можете узнать гораздо больше в документации **SQLModel**, там есть более длительное мини<ahref="https://sqlmodel.tiangolo.com/tutorial/fastapi/"class="external-link"target="_blank">руководство по использованию SQLModel с **FastAPI**</a>. 🚀
Когда вы работаете над проектами на Python, рекомендуется использовать **виртуальную среду** (или аналогичный механизм) для изоляции пакетов, которые вы устанавливаете для каждого проекта.
Когда вы работаете с проектами на Python, вероятно, вам стоит использовать **виртуальное окружение** (или аналогичный механизм) для изоляции пакетов, которые вы устанавливаете для каждого проекта.
/// info | Информация
Если вы уже знаете о виртуальных средах, как их создавать и использовать, вы можете пропустить этот раздел. 🤓
Если вы уже знакомы с виртуальными окружениями, знаете, как их создавать и использовать, вы можете пропустить этот раздел. 🤓
///
/// tip | Совет
**Виртуальная среда** отличается от **переменной окружения**.
**Виртуальное окружение** отличается от **переменной окружения**.
**Переменная окружения** — это системная переменная, которую могут использовать программы.
**Виртуальная среда** — это директория, содержащая некоторые файлы.
**Виртуальное окружение** — это директория с файлами.
///
/// info | Информация
На этой странице вы узнаете, как использовать **виртуальные среды** и как они работают.
Этот раздел научит вас использовать **виртуальные окружения** и как они работают.
Если вы готовы использовать **инструмент, который управляет всем**для вас (включая установку Python), попробуйте <ahref="https://github.com/astral-sh/uv"class="external-link"target="_blank">uv</a>.
Если вы готовы начать использовать **инструмент, который управляет всем**за вас (включая установку Python), попробуйте <ahref="https://github.com/astral-sh/uv"class="external-link"target="_blank">uv</a>.
///
@ -30,40 +30,40 @@
Сначала создайте директорию для вашего проекта.
Обычно я создаю директорию с названием `code` внутри своего домашнего каталога.
Обычно я создаю директорию с именем `code` внутри моего домашнего каталога.
И внутри этой директории я создаю по одной директории на каждый проект.
И внутри этой директории создаю одну папку для каждого проекта.
<divclass="termy">
```console
// Перейти в домашний каталог
$ cd
// Создать директорию для всех проектов
// Создайте директорию для всех ваших проектов с кодом
$ mkdir code
// Войти в эту директорию
// Войдите в указанную директорию
$ cd code
// Создать директорию для этого проекта
// Создайте директорию для этого проекта
$ mkdir awesome-project
// Войти в директорию проекта
// Перейдите в эту директорию проекта
$ cd awesome-project
```
</div>
## Создание виртуальной среды
## Создание виртуального окружения
Когда вы впервые начинаете работу над проектом на Python, создайте виртуальную среду**<abbrtitle="есть другие варианты, это простая рекомендация">внутри вашего проекта</abbr>**.
Когда вы начинаете работать над Python-проектом **в первый раз**, создайте виртуальное окружение**<abbrtitle="существуют и другие варианты, это простое руководство">внутри вашего проекта</abbr>**.
/// tip | Совет
Это нужно сделать только **один раз для проекта**, а не каждый раз, когда вы работаете.
Вы должны сделать это **один раз на проект**, а не каждый раз, когда работаете.
///
//// tab | `venv`
Для создания виртуальной среды вы можете использовать модуль `venv`, который поставляется с Python.
Чтобы создать виртуальное окружение, вы можете использовать модуль `venv`, который входит в комплект поставки Python.
<divclass="termy">
@ -73,12 +73,12 @@ $ python -m venv .venv
</div>
/// details | Что делает эта команда?
/// details | Что делает эта команда
* `python`: используется программа под названием`python`
* `-m`: вызывает модуль как скрипт, далее укажем, какой модуль
* `venv`: используется модуль под названием `venv`, который обычно устанавливается вместе с Python
* `.venv`: создаёт виртуальную среду в новой директории `.venv`
* `python`: использовать программу`python`
* `-m`: вызвать модуль как сценарий, далее укажем, какой модуль использовать
* `venv`: использовать модуль под названием `venv`, который обычно устанавливается вместе с Python
* `.venv`: создать виртуальное окружение в новой директории `.venv`
///
@ -86,7 +86,7 @@ $ python -m venv .venv
//// tab | `uv`
Если у вас установлен <ahref="https://github.com/astral-sh/uv"class="external-link"target="_blank">`uv`</a>, вы можете использовать его для создания виртуальной среды.
Если у вас установлен <ahref="https://github.com/astral-sh/uv"class="external-link"target="_blank">`uv`</a>, вы можете использовать его для создания виртуального окружения.
<divclass="termy">
@ -98,29 +98,29 @@ $ uv venv
/// tip | Совет
По умолчанию `uv` создаёт виртуальную среду в директории, называемой`.venv`.
По умолчанию `uv` создаст виртуальное окружение в директории под названием`.venv`.
Но вы можете изменить это, добавив дополнительный аргумент с именем директории.
Но вы можете изменить это, передав дополнительный аргумент с именем директории.
///
////
Эта команда создаёт новую виртуальную среду в директории, называемой`.venv`.
Эта команда создаст новое виртуальное окружение в директории`.venv`.
/// details | `.venv` или другое имя?
/// details | `.venv` или другое имя
Вы можете создать виртуальную среду в другой директории, но существует традиция называть её`.venv`.
Вы можете создать виртуальное окружение в другой директории, но принято называть его`.venv`.
///
## Активация виртуальной среды
## Активация виртуального окружения
Активируйте созданную виртуальную среду, чтобы любая выполняемая команда Python или устанавливаемый пакет использовали её.
Активируйте новое виртуальное окружение, чтобы любая команда Python, которую вы запускаете, или пакет, который вы устанавливаете, использовала его.
/// tip | Совет
Делайте это **каждый раз**, когда начинаете**новую сессию терминала** для работы над проектом.
Делайте это **каждый раз**, начиная**новую сессию терминала** для работы над проектом.
///
@ -150,7 +150,7 @@ $ .venv\Scripts\Activate.ps1
//// tab | Windows Bash
Или, если вы используете Bash для Windows (например, <ahref="https://gitforwindows.org/"class="external-link"target="_blank">Git Bash</a>):
Или если вы используете Bash для Windows (например, <ahref="https://gitforwindows.org/"class="external-link"target="_blank">Git Bash</a>):
Проверьте, активна ли виртуальная среда (предыдущая команда сработала).
Каждый раз, когда вы устанавливаете **новый пакет** в этой среде, **активируйте** ее снова.
Это гарантирует, что если вы используете программу из **терминала (<abbr title="интерфейс командной строки">CLI</abbr>)**, установленную этим пакетом, вы используете ту, что в вашем виртуальном окружении, а не ту, что может быть установлена глобально, с другой версией, чем вам нужно.
///
## Проверка активации виртуального окружения
Проверьте, что виртуальное окружение активно (предыдущая команда сработала).
/// tip | Совет
Это **не обязательно**, но это хороший способ **убедиться**, что всё работает как ожидалось, и вы используете именно ту виртуальную среду, которую планировали.
Это **необязательно**, но хороший способ **убедиться**, что все работает как надо и вы используете то виртуальное окружение, которое хотели.
///
@ -192,7 +200,7 @@ $ which python
</div>
Если показывает, что `python` находится в `.venv/bin/python` внутри вашего проекта (в данном случае `awesome-project`), значит, всё сработало. 🎉
Если путь указывает на бинарный файл `python` в `.venv/bin/python`, внутри вашего проекта (в нашем случае это `awesome-project`), значит все сработало. 🎉
Если показывает, что `python` находится в `.venv\Scripts\python` внутри вашего проекта (в данном случае `awesome-project`), значит, всё сработало. 🎉
Если путь указывает на бинарный файл `python` в `.venv\Scripts\python`, внутри вашего проекта (в нашем случае это `awesome-project`), значит все сработало. 🎉
Если вы используете <ahref="https://github.com/astral-sh/uv"class="external-link"target="_blank">`uv`</a>, то будете использовать его для установки пакетов вместо `pip`, поэтому обновлять `pip` не нужно. 😎
Если вы используете <ahref="https://github.com/astral-sh/uv"class="external-link"target="_blank">`uv`</a> для установки пакетов, вам не нужно обновлять `pip`. 😎
///
Если вы используете `pip` для установки пакетов (он по умолчанию поставляется с Python), стоит **обновить** его до последней версии.
Если вы используете `pip` для установки пакетов (он идет по умолчанию вместе с Python), для начала обновите его до последней версии.
Многие экзотические ошибки при установке пакетов решаются просто обновлением `pip`.
Многие экзотические ошибки при установке пакетов решаются простым обновлением `pip`.
/// tip | Совет
Обычно это делается **один раз**, сразу после создания виртуальной среды.
Обычно это делается **один раз**, сразу после создания виртуального окружения.
///
Убедитесь, что виртуальная среда активирована (используя команду выше), и затем выполните:
Убедитесь, что виртуальное окружение активно (с помощью вышеуказанной команды), и затем запустите:
Если вы используете **Git** (а вы должны), добавьте файл `.gitignore`, чтобы исключить всё содержимое вашей папки `.venv` из Git.
Если вы используете **Git** (и вам стоит его использовать), добавьте файл `.gitignore`, чтобы исключить всё из директории `.venv` из Git.
/// tip | Совет
Если вы использовали <ahref="https://github.com/astral-sh/uv"class="external-link"target="_blank">`uv`</a> для создания виртуальной среды, это уже сделано за вас, можете пропустить этот шаг. 😎
Если вы использовали <ahref="https://github.com/astral-sh/uv"class="external-link"target="_blank">`uv`</a> для создания виртуального окружения, оно уже это сделало за вас, вы можете пропустить этот шаг. 😎
///
/// tip | Совет
Это делается**один раз**, сразу после создания виртуальной среды.
Сделайте это**один раз**, сразу после создания виртуального окружения.
Файл `requirements.txt` с несколькими пакетами может выглядеть так:
`requirements.txt` со списком некоторых пакетов может выглядеть так:
```requirements.txt
fastapi[standard]==0.113.0
@ -378,7 +386,7 @@ pydantic==2.8.0
## Запуск программы
После активации виртуальной среды вы можете запустить свою программу, и она будет использовать Python внутри вашей виртуальной среды с установленными там пакетами.
После активации виртуального окружения вы можете запустить свою программу, и она будет использовать Python из вашего виртуального окружения с установленными там пакетами.
<divclass="termy">
@ -392,7 +400,7 @@ Hello World
## Настройка редактора
Вы, вероятно, будете использовать редактор, убедитесь, что настроили его на использование той же виртуальной среды, которую вы создали (скорее всего, он её автоматически распознает), чтобы вы могли использовать автозавершение и видеть ошибки в коде.
Вероятно, вы будете использовать редактор кода; убедитесь, что вы настроили его для использования того же виртуального окружения, которое вы создали (вероятно, он его автоматически обнаружит), чтобы у вас были автозавершение и подсветка ошибок.
Например:
@ -401,13 +409,13 @@ Hello World
/// tip | Совет
Обычно это делается **один раз**, когда вы создаете виртуальную среду.
Обычно это делается **один раз**, при создании виртуального окружения.
///
## Деактивация виртуальной среды
## Деактивация виртуального окружения
После завершения работы над проектом вы можете **деактивировать** виртуальную среду.
Когда вы завершите работу над проектом, вы можете **деактивировать** виртуальное окружение.
<divclass="termy">
@ -417,55 +425,53 @@ $ deactivate
</div>
Таким образом, при запуске `python`, он не будет использовать версию из этой виртуальной среды с установленными там пакетами.
Таким образом, когда вы запускаете `python`, он не будет пытаться запустить его из того виртуального окружения с установленными в нём пакетами.
## Готовность к работе
Теперь вы готовы приступить к работе над своим проектом.
Теперь вы готовы начать работу над своим проектом.
/// tip | Совет
Хотите понять всё, что написано выше?
Хотите понять, что означает все вышеперечисленное?
Продолжайте чтение. 👇🤓
///
## Зачем виртуальные среды
## Зачем использовать виртуальные окружения
Для работы с FastAPI вам нужно установить <ahref="https://www.python.org/"class="external-link"target="_blank">Python</a>.
После этого вам потребуется установить FastAPI и другие необходимые **пакеты**.
После этого вам нужно будет **установить** FastAPI и любые другие **пакеты**, которые вы хотите использовать.
Чтобы установить пакеты, вы обычно используете команду `pip`, которая идет в комплекте с Python (либо аналогичные альтернативы).
Для установки пакетов обычно используют команду `pip`, которая идёт вместе с Python (или аналогичные альтернативы).
Тем не менее, если вы просто используете `pip` напрямую, пакеты будут устанавливаться в ваш **глобальный Python-окружение** (глобальная установка Python).
Тем не менее, если вы просто используете `pip` напрямую, то пакеты будут установлены в ваш **глобальный Python** (глобальная установка Python).
### Проблема
Так в чём проблема установки пакетов в глобальную среду Python?
Так в чем проблема установки пакетов в глобальную среду Python?
В какой-то момент вы вероятно напишете множество различных программ, зависящих от **разных пакетов**. Некоторые из этих проектов будут зависеть от **разных версий** одного и того же пакета. 😱
В какой-то момент вам, вероятно, придется писать много разных программ, которые зависят от **разных пакетов**. И некоторые из этих проектов, над которыми вы работаете, будут зависеть от **разных версий** одного и того же пакета. 😱
Например, вы могли бы создать проект, называемый `philosophers-stone`, который зависит от другого пакета **`harry`, используя версию `1`**. По этому вам нужно установить `harry`.
Например, вы можете создать проект под названием `philosophers-stone`, эта программа зависит от другого пакета под названием **`harry`, используемого версии `1`**. Поэтому вам нужно установить `harry`.
Затем, в какой-то момент позже, вы создаёте другой проект, называемый `prisoner-of-azkaban`, и этот проект тоже зависит от `harry`, но ему нужна версия**`harry``3`**.
Затем, в какой-то момент позже, вы создаёте другой проект под названием `prisoner-of-azkaban`, и этот проект также зависит от `harry`, но этот проект нуждается в**`harry` версии`3`**.
Но теперь проблема в том, если вы устанавливаете пакеты глобально (в глобальной среде) вместо локальной **виртуальной среды**, вы должны выбрать, какую версию `harry` установить.
Но теперь проблема заключается в том, что если пакеты установлены глобально (в глобальной среде), а не в локальной **виртуальной среде**, вам придётся выбирать, какую версию `harry` установить.
Если вы хотите запустить `philosophers-stone`, вы должны сначала установить `harry` версии `1`, например, с помощью:
Если вы хотите запустить `philosophers-stone`, вам нужно сначала установить `harry` версии `1`, например, с помощью:
<divclass="termy">
@ -475,7 +481,7 @@ $ pip install "harry==1"
</div>
И в итоге у вас установлена `harry` версии `1` в глобальной среде Python.
И тогда у вас будет установлена версия `harry``1` в вашем глобальном Python.
```mermaid
flowchart LR
@ -487,7 +493,7 @@ flowchart LR
end
```
Но затем, если вы хотите запустить `prisoner-of-azkaban`, вы должны удалить `harry` версии `1` и установить `harry` версии `3` (или просто установка версии `3` автоматически удалит версию `1`).
Но если вы хотите запустить `prisoner-of-azkaban`, вам нужно будет удалить `harry` версии `1` и установить `harry` версии `3` (или просто установка версии `3` автоматически удалит версию `1`).
<divclass="termy">
@ -497,9 +503,9 @@ $ pip install "harry==3"
</div>
И вы получили `harry` версии `3`, установленный в вашей глобальной среде Python.
И затем у вас будет установлена версия `harry``3` в вашем глобальном Python.
И если вы попробуете снова запустить `philosophers-stone`, то есть вероятность, что он **не будет работать**, так как зависит от`harry` версии `1`.
И если вы снова попытаетесь запустить `philosophers-stone`, есть вероятность, что он **не будет работать**, потому что ему нужен`harry` версии `1`.
```mermaid
flowchart LR
@ -518,47 +524,47 @@ flowchart LR
/// tip | Совет
В Python-пакетах обычно стараются изо всех сил **избегать критических изменений** в **новых версиях**, но лучше перестраховаться и планово устанавливать новые версии, а затем запускать тесты, чтобы проверить, работает ли всё правильно.
В пакетах Python очень часто стараются изо всех сил избегать внесения критических изменений в новых версиях, но лучше перестраховаться и намеренно устанавливать новые версии, а затем запускать тесты, чтобы проверить, все ли работает правильно.
///
Теперь представьте, что это происходит с **многими** другим пакеты, от которых зависят все ваши **проекты**. С этим очень сложно справиться. И вы, вероятно, в конечном итоге запустите некоторые проекты с некоторыми **несовместимыми версиями** пакетов и не будете знать, почему что-то не работает.
Теперь представьте это с **множеством** других **пакетов**, от которых зависят все ваши **проекты**. Это очень сложно управляемо. И, вероятно, вы будете запускать некоторые проекты с некоторыми **несовместимыми версиями** пакетов и не будете знать, почему что-то не работает.
Кроме того, в зависимости от вашей операционной системы (например, Linux, Windows, macOS), она могла поставляться с уже установленным Python. И в этом случае она вероятно имела некоторые пакеты, предварительно установленные с определёнными версиями, **необходимыми вашей системой**. Если вы устанавливаете пакеты в глобальную среду Python, вы можете в итоге **сломать** некоторые программы, которые были полностью частью вашей операционной системы.
Кроме того, в зависимости от вашей операционной системы (например, Linux, Windows, macOS), она могла быть поставлена с уже установленным Python. И в этом случае, вероятно, были предустановлены некоторые пакеты с определёнными версиями, **нужными вашей системе**. Если вы устанавливаете пакеты в глобальной среде Python, вы можете **поломать** некоторые из программ, которые поставляются с вашей операционной системой.
## Где устанавливаются пакеты
Когда вы устанавливаете Python, это создаёт на вашем компьютере некоторые каталоги с некоторыми файлами.
Когда вы устанавливаете Python, это создаёт некоторые директории с файлами на вашем компьютере.
Некоторые из этих каталогов являются теми, которые хранят все установленные вами пакеты.
Некоторые из этих директорий предназначены для хранения всех устанавливаемых вами пакетов.
Когда вы запускаете:
<divclass="termy">
```console
// Не запускайте это сейчас, это лишь пример 🤓
// Не запускайте это сейчас, это просто пример 🤓
$ pip install "fastapi[standard]"
---> 100%
```
</div>
Это загрузит сжатый файл с кодом FastAPI, обычно с<ahref="https://pypi.org/project/fastapi/"class="external-link"target="_blank">PyPI</a>.
Это скачается как сжатый файл с кодом FastAPI, обычно из<ahref="https://pypi.org/project/fastapi/"class="external-link"target="_blank">PyPI</a>.
Он также **загрузит** файлы для других пакетов, от которых зависит FastAPI.
Он также **скачает** файлы для других пакетов, от которых зависит FastAPI.
Затем он **извлечёт** все эти файлы и поместит их в директорию на вашем компьютере.
По умолчанию, он сохранит загруженные и извлечённые файлы в директории, которая идёт в комплекте с вашей установкой Python, то есть в **глобальной среде**.
По умолчанию, он разместит те загруженные и извлечённые файлы в директории, которая идёт с вашей установкой Python, это ваш **глобальный Python**.
## Что такое виртуальные среды
## Что такое виртуальные окружения?
Решением проблем использования всех пакетов в глобальной среде является использование **виртуальной среды для каждого проекта**, над которым вы работаете.
Решением проблем того, что все пакеты находятся в глобальной среде, является использование **виртуального окружения для каждого проекта**, над которым вы работаете.
Виртуальная среда — это **директория**, очень похожая на глобальную, в которой вы можете устанавливать пакеты для конкретного проекта.
Виртуальное окружение — это **директория**, очень похожая на глобальную, где вы можете устанавливать пакеты для проекта.
Таким образом, каждый проект будет иметь собственную виртуальную среду (`.venv` директории) со своими пакетами.
Таким образом, каждый проект будет иметь своё собственное виртуальное окружение (директорию `.venv`) со своими пакетами.
```mermaid
flowchart TB
@ -577,9 +583,9 @@ flowchart TB
stone-project ~~~ azkaban-project
```
## Что означает активация виртуальной среды
## Что означает активация виртуального окружения?
Когда вы активируете виртуальную среду, например, используя:
Когда вы активируете виртуальное окружение, например, с помощью:
//// tab | Linux, macOS
@ -607,7 +613,7 @@ $ .venv\Scripts\Activate.ps1
//// tab | Windows Bash
Или, если вы используете Bash для Windows (например, <ahref="https://gitforwindows.org/"class="external-link"target="_blank">Git Bash</a>):
Или если вы используете Bash для Windows (например, <ahref="https://gitforwindows.org/"class="external-link"target="_blank">Git Bash</a>):
Эта команда создаст или изменит некоторые [переменные окружения](environment-variables.md){.internal-link target=_blank}, которые будут доступны для последующих команд.
Эта команда создаст или изменит некоторые [переменные окружения](environment-variables.md){.internal-link target=_blank}, которые будут доступны для следующих команд.
Одна из этих переменных — это переменная `PATH`.
Одна из этих переменных — переменная `PATH`.
/// tip | Совет
Вы можете узнать больше о переменной окружения `PATH` в разделе [Переменные окружения](environment-variables.md#path-environment-variable){.internal-link target=_blank}.
Вы можете узнать больше о переменной `PATH` в разделе [Переменные окружения](environment-variables.md#path-environment-variable){.internal-link target=_blank}.
///
Активация виртуальной среды добавляет её путь`.venv/bin` (на Linux и macOS) или `.venv\Scripts` (на Windows) в переменную окружения `PATH`.
Активация виртуального окружения добавляет путь его директории`.venv/bin` (на Linux и macOS) или `.venv\Scripts` (на Windows) в переменную окружения `PATH`.
Предположим, что до активации среды переменная `PATH` выглядела так:
Скажем, до активации окружения переменная `PATH` выглядела так:
//// tab | Linux, macOS
@ -660,7 +666,7 @@ C:\Windows\System32
////
После активации виртуальной среды переменная `PATH` будет выглядеть примерно так:
После активации виртуального окружения переменная `PATH` будет выглядеть примерно так:
Важной деталью является то, что путь к виртуальной среде будет помещён в **начало** переменной `PATH`. Система найдёт его **до** того, как найдёт какой-либо другой Python. Таким образом, при запуске команды `python` будет использоваться Python **из виртуальной среды**, а не любой другой `python` (например, `python` из глобальной среды).
Важная деталь заключается в том, что он помещает путь виртуального окружения в **начало** переменной `PATH`. Система обнаружит это **раньше**, чем любой другой Python. Таким образом, когда вы запускаете `python`, он будет использовать Python **из виртуального окружения**, а не любой другой `python` (например, `python` из глобального окружения).
Активация виртуальной среды также изменяет несколько других вещей, но это одна из самых важных функцией.
Активация виртуального окружения также меняет несколько других вещей, но это одна из самых важных функций.
## Проверка виртуальной среды
## Проверка виртуального окружения
Когда вы проверяете, активна ли виртуальная среда, например, используя:
Когда вы проверяете, активно ли виртуальное окружение, например, с помощью:
Это означает, что программа `python`, которая будет использоваться, это та, что находится **в виртуальной среде**.
Это означает, что программа `python`, которая будет использоваться, — это программа **из виртуального окружения**.
Вы используете `which` на Linux и macOS и `Get-Command` на Windows PowerShell.
Команда работает следующим образом: она проходит через переменную окружения `PATH`, проходя **каждый путь в порядке** поиска программы под названием `python`. После того, как она найдёт её, она **отобразит путь** к этой программе.
Работа этой команды заключается в том, что она проверяет переменную окружения `PATH`, проходя по **каждому пути по порядку** в поисках программы под названием `python`. Как только она её находит, она **показывает вам путь** к этой программе.
Самая важная часть заключается в том, что когда вы вызываете `python`, это та самая программа "`python`", которая будет выполнена.
Наиболее важной частью является то, что когда вы вызываете `python`, это тот самый "`python`", который будет запущен.
Таким образом, вы можете убедиться, что находитесь в правильной виртуальной среде.
Таким образом, вы можете убедиться, что находитесь в правильном виртуальном окружении.
/// tip | Совет
Легко активировать одну виртуальную среду, получить один Python, а затем **перейти к другому проекту**.
Легко активировать одно виртуальное окружение, а затем **перейти к другому проекту**.
И второй проект **не будет работать**, потому что вы используете **неправильный Python** из виртуальной среды для другого проекта.
И второй проект **не будет работать**, потому что вы используете **неправильный Python**, из виртуального окружения другого проекта.
Полезно иметь возможность проверять, какой `python` используется. 🤓
Полезно уметь проверять, какой `python` используется. 🤓
///
## Зачем деактивировать виртуальную среду
## Зачем деактивировать виртуальное окружение?
Например, вы можете работать над проектом `philosophers-stone`, **активируя эту виртуальную среду**, устанавливая пакеты и работая в этой среде.
Например, вы можете работать над проектом `philosophers-stone`, **активировать это виртуальное окружение**, устанавливать пакеты и работать в этой среде.
А затем вы хотите работать над **другим проектом**`prisoner-of-azkaban`.
А затем вы хотите поработать над **другим проектом**`prisoner-of-azkaban`.
Вы переходите к этому проекту:
Вы переходите в этот проект:
<divclass="termy">
@ -782,7 +788,7 @@ $ cd ~/code/prisoner-of-azkaban
</div>
Если вы не деактивируете виртуальную среду для `philosophers-stone`, то когда вы запускаете `python` в терминале, он попытается использовать Python из `philosophers-stone`.
Если вы не деактивируете виртуальное окружение для `philosophers-stone`, когда вы запускаете `python` в терминале, он попытается использовать Python из `philosophers-stone`.
Но если вы деактивируете виртуальную среду и активируете новую для `prisoner-of-askaban`, то когда вы запускаете `python`, он будет использоваться Python из виртуальной среды в проекте`prisoner-of-azkaban`.
Но если вы деактивируете виртуальное окружение и активируете новое для `prisoner-of-askaban`, то, когда вы запускаете `python`, он будет использовать Python из виртуального окружения в`prisoner-of-azkaban`.
<divclass="termy">
```console
$ cd ~/code/prisoner-of-azkaban
// Вам не нужно находиться в старой директории для деактивации, вы можете сделать это из любого места, даже после перехода к другому проекту 😎
// Вам не нужно находиться в старой директории для деактивации, вы можете сделать это, где угодно, даже после перехода к другому проекту 😎
$ deactivate
// Активируйте виртуальную среду в `prisoner-of-azkaban/.venv` 🚀
// Активируйте виртуальное окружение в prisoner-of-azkaban/.venv 🚀
$ source .venv/bin/activate
// Теперь, когда вы запускаете python, он найдёт пакет sirius, установленный в этой виртуальной среде ✨
@ -822,23 +828,23 @@ $ python main.py
## Альтернативы
Это простое руководство поможет вам начать работу и научит тому, как всё работает **внутри**.
Это простое руководство, чтобы начать работу и показать, как всё **работает изнутри**.
Существуют также **альтернативные** решения для управления виртуальными средами разработки, зависимостями пакетов (требования), проектами.
Существует много **альтернатив** для управления виртуальными окружениями, зависимостями пакетов, проектами.
Когда вы будете готовы и захотите использовать инструмент для **управления всем проектом**, зависимостями пакетов, виртуальными средами и т.д., я бы рекомендовал вам попробовать <ahref="https://github.com/astral-sh/uv"class="external-link"target="_blank">uv</a>.
Когда вы будете готовы и захотите использовать инструмент для **полного управления проектом**, зависимостями пакетов, виртуальными окружениями и т.д., я бы предложил вам попробовать <ahref="https://github.com/astral-sh/uv"class="external-link"target="_blank">uv</a>.
`uv` может делать много всего, он может:
`uv` может сделать множество вещей, он может:
* **Установить Python** для вас, включая разные версии
* Управлять **виртуальной средой** для ваших проектов
* Управлять **виртуальным окружением** для ваших проектов
* Устанавливать **пакеты**
* Управлять зависимостями пакетов **и версиями** для вашего проекта
* Обеспечить наличие **точного** набора пакетов и версий для установки, включая их зависимости, так что вы можете быть уверены, что ваш проект будет работать в production точно так же, как и на вашем компьютере во время разработки, это называется *locking*
* И многое другое
* Управлять зависимостями пакетов и версиями для вашего проекта
* Гарантировать, что у вас есть **точный** набор пакетов и версий для установки, включая их зависимости, так что вы можете быть уверены, что вы можете запустить ваш проект в продакшн точно так же, как на вашем компьютере во время разработки, это называется **locking**
* И многие другие вещи
## Заключение
Если вы прочитали и поняли всё это, теперь **вы знаете гораздо больше** о виртуальных средах, чем многие другие разработчики. 🤓
Если вы прочли и поняли всё это, теперь **вы знаете гораздо больше** о виртуальных окружениях, чем многие разработчики. 🤓
Знание этих деталей, скорее всего, будет полезно вам в будущем, когда вы будете отлаживать что-то, что кажется сложным, но вы будете знать, **как всё работает внутри**. 😎
Понимание этих деталей, вероятно, будет полезно в будущем, когда вы будете отлаживать что-то, кажущееся сложным, но вы будете знать, **как всё работает под капотом**. 😎