Compare commits

...

22 Commits

Author SHA1 Message Date
Sebastián Ramírez
ebdeda2de6 🔖 Release version 0.115.14 2025-06-26 17:26:32 +02:00
Sebastián Ramírez
8fa19a6faa 📝 Update release notes 2025-06-26 17:24:56 +02:00
github-actions
3ecb4c5389 📝 Update release notes
[skip ci]
2025-06-26 15:23:20 +00:00
Patrick Arminio
9d0d8828cc 🐛 Fix support for unions when using Form (#13827) 2025-06-26 17:22:53 +02:00
github-actions
df35896a0e 📝 Update release notes
[skip ci]
2025-06-25 09:13:15 +00:00
pre-commit-ci[bot]
8f64d09ee0 ⬆ [pre-commit.ci] pre-commit autoupdate (#13823)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.13 → v0.12.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.13...v0.12.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-06-25 11:12:53 +02:00
github-actions
3b09dd8e01 📝 Update release notes
[skip ci]
2025-06-24 19:14:26 +00:00
Valentyn
c30821ff6e 🌐 Add Ukrainian translation for docs/uk/docs/tutorial/response-model.md (#13792)
* 🌐 Add Ukrainian translation for docs/uk/docs/tutorial/response-model.md

* 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks

* Fix review comments

---------

Co-authored-by: Valentyn Druzhynin <v.druzhynin@zakaz.global>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-06-24 21:14:01 +02:00
github-actions
666890ac7f 📝 Update release notes
[skip ci]
2025-06-24 18:58:10 +00:00
Valentyn
937af92ba7 🌐 Add Ukrainian translation for docs/uk/docs/tutorial/security/index.md (#13805)
Fixes after review

Co-authored-by: Valentyn Druzhynin <v.druzhynin@zakaz.global>
2025-06-24 20:57:48 +02:00
github-actions
1cf4b8c2de 📝 Update release notes
[skip ci]
2025-06-22 14:37:15 +00:00
Hirokatsu Endo
dcb223d850 ✏️ Fix typo in docs/ja/docs/tutorial/encoder.md (#13815) 2025-06-22 16:36:05 +02:00
github-actions
baeeafa1e1 📝 Update release notes
[skip ci]
2025-06-22 14:35:47 +00:00
Hirokatsu Endo
487f940e91 ✏️ Fix typo in docs/ja/docs/tutorial/handling-errors.md (#13814) 2025-06-22 16:35:27 +02:00
github-actions
e9c33debaa 📝 Update release notes
[skip ci]
2025-06-22 14:35:16 +00:00
Hirokatsu Endo
041a37bb1f ✏️ Fix typo in docs/ja/docs/tutorial/body-fields.md (#13802) 2025-06-22 16:34:53 +02:00
github-actions
28038f19cf 📝 Update release notes
[skip ci]
2025-06-18 18:22:42 +00:00
Naves
9026f3b1bd Misprint (#13800) 2025-06-18 20:22:18 +02:00
github-actions
8d9ef5d343 📝 Update release notes
[skip ci]
2025-06-18 08:41:19 +00:00
Naves
b2923282ca 🌐 Add Russian translation for docs/ru/docs/advanced/index.md (#13797)
* Add Russian Translation for `docs/ru/docs/advanced/index.md`

* 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-06-18 10:40:59 +02:00
github-actions
ac160885e1 📝 Update release notes
[skip ci]
2025-06-17 11:54:19 +00:00
Nolan Di Mare Sullivan
7d9195a248 📝 Update Speakeasy URL to Speakeasy Sandbox (#13697) 2025-06-17 13:53:56 +02:00
18 changed files with 699 additions and 15 deletions

View File

@@ -14,7 +14,7 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.13
rev: v0.12.0
hooks:
- id: ruff
args:

View File

@@ -56,7 +56,7 @@ The key features are:
<a href="https://www.coderabbit.ai/?utm_source=fastapi&utm_medium=badge&utm_campaign=fastapi" target="_blank" title="Cut Code Review Time & Bugs in Half with CodeRabbit"><img src="https://fastapi.tiangolo.com/img/sponsors/coderabbit.png"></a>
<a href="https://subtotal.com/?utm_source=fastapi&utm_medium=sponsorship&utm_campaign=open-source" target="_blank" title="The Gold Standard in Retail Account Linking"><img src="https://fastapi.tiangolo.com/img/sponsors/subtotal.svg"></a>
<a href="https://databento.com/" target="_blank" title="Pay as you go for market data"><img src="https://fastapi.tiangolo.com/img/sponsors/databento.svg"></a>
<a href="https://speakeasy.com?utm_source=fastapi+repo&utm_medium=github+sponsorship" target="_blank" title="SDKs for your API | Speakeasy"><img src="https://fastapi.tiangolo.com/img/sponsors/speakeasy.png"></a>
<a href="https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship" target="_blank" title="SDKs for your API | Speakeasy"><img src="https://fastapi.tiangolo.com/img/sponsors/speakeasy.png"></a>
<a href="https://www.svix.com/" target="_blank" title="Svix - Webhooks as a service"><img src="https://fastapi.tiangolo.com/img/sponsors/svix.svg"></a>
<a href="https://www.stainlessapi.com/?utm_source=fastapi&utm_medium=referral" target="_blank" title="Stainless | Generate best-in-class SDKs"><img src="https://fastapi.tiangolo.com/img/sponsors/stainless.png"></a>
<a href="https://www.permit.io/blog/implement-authorization-in-fastapi?utm_source=github&utm_medium=referral&utm_campaign=fastapi" target="_blank" title="Fine-Grained Authorization for FastAPI"><img src="https://fastapi.tiangolo.com/img/sponsors/permit.png"></a>

View File

@@ -20,7 +20,7 @@ Einige von diesen ✨ [**sponsern FastAPI**](../help-fastapi.md#den-autor-sponse
Und es zeigt deren wahres Engagement für FastAPI und seine **Community** (Sie), da diese Ihnen nicht nur einen **guten Service** bieten möchten, sondern auch sicherstellen möchten, dass Sie über ein **gutes und gesundes Framework** verfügen, FastAPI. 🙇
Beispielsweise könnten Sie <a href="https://speakeasy.com/?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a> ausprobieren.
Beispielsweise könnten Sie <a href="https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a> ausprobieren.
Es gibt auch mehrere andere Unternehmen, welche ähnliche Dienste anbieten und die Sie online suchen und finden können. 🤓

View File

@@ -30,7 +30,7 @@ silver:
- url: https://databento.com/
title: Pay as you go for market data
img: https://fastapi.tiangolo.com/img/sponsors/databento.svg
- url: https://speakeasy.com?utm_source=fastapi+repo&utm_medium=github+sponsorship
- url: https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship
title: SDKs for your API | Speakeasy
img: https://fastapi.tiangolo.com/img/sponsors/speakeasy.png
- url: https://www.svix.com/

View File

@@ -22,7 +22,7 @@ And it shows their true commitment to FastAPI and its **community** (you), as th
For example, you might want to try:
* <a href="https://speakeasy.com/?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>
* <a href="https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>
* <a href="https://www.stainlessapi.com/?utm_source=fastapi&utm_medium=referral" class="external-link" target="_blank">Stainless</a>
* <a href="https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi" class="external-link" target="_blank">liblab</a>

View File

@@ -58,7 +58,7 @@ You could put your XML content in a string, put that in a `Response`, and return
## Notes
When you return a `Response` directly its data is not validated, converted (serialized), nor documented automatically.
When you return a `Response` directly its data is not validated, converted (serialized), or documented automatically.
But you can still document it as described in [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}.

View File

@@ -7,6 +7,30 @@ hide:
## Latest Changes
## 0.115.14
### Fixes
* 🐛 Fix support for unions when using `Form`. PR [#13827](https://github.com/fastapi/fastapi/pull/13827) by [@patrick91](https://github.com/patrick91).
### Docs
* ✏️ Fix grammar mistake in `docs/en/docs/advanced/response-directly.md`. PR [#13800](https://github.com/fastapi/fastapi/pull/13800) by [@NavesSapnis](https://github.com/NavesSapnis).
* 📝 Update Speakeasy URL to Speakeasy Sandbox. PR [#13697](https://github.com/fastapi/fastapi/pull/13697) by [@ndimares](https://github.com/ndimares).
### Translations
* 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/response-model.md`. PR [#13792](https://github.com/fastapi/fastapi/pull/13792) by [@valentinDruzhinin](https://github.com/valentinDruzhinin).
* 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/security/index.md`. PR [#13805](https://github.com/fastapi/fastapi/pull/13805) by [@valentinDruzhinin](https://github.com/valentinDruzhinin).
* ✏️ Fix typo in `docs/ja/docs/tutorial/encoder.md`. PR [#13815](https://github.com/fastapi/fastapi/pull/13815) by [@ruzia](https://github.com/ruzia).
* ✏️ Fix typo in `docs/ja/docs/tutorial/handling-errors.md`. PR [#13814](https://github.com/fastapi/fastapi/pull/13814) by [@ruzia](https://github.com/ruzia).
* ✏️ Fix typo in `docs/ja/docs/tutorial/body-fields.md`. PR [#13802](https://github.com/fastapi/fastapi/pull/13802) by [@ruzia](https://github.com/ruzia).
* 🌐 Add Russian translation for `docs/ru/docs/advanced/index.md`. PR [#13797](https://github.com/fastapi/fastapi/pull/13797) by [@NavesSapnis](https://github.com/NavesSapnis).
### Internal
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#13823](https://github.com/fastapi/fastapi/pull/13823) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
## 0.115.13
### Fixes

View File

@@ -22,7 +22,7 @@ Y muestra su verdadero compromiso con FastAPI y su **comunidad** (tú), ya que n
Por ejemplo, podrías querer probar:
* <a href="https://speakeasy.com/?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>
* <a href="https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>
* <a href="https://www.stainlessapi.com/?utm_source=fastapi&utm_medium=referral" class="external-link" target="_blank">Stainless</a>
* <a href="https://developers.liblab.com/tutorials/sdk-for-fastapi/?utm_source=fastapi" class="external-link" target="_blank">liblab</a>

View File

@@ -44,7 +44,7 @@
追加情報は`Field``Query``Body`などで宣言することができます。そしてそれは生成されたJSONスキーマに含まれます。
後に例を用いて宣言を学ぶ際に、追加情報を句悪方法を学べます。
後に例を用いて宣言を学ぶ際に、追加情報を追加する方法を学べます。
## まとめ

View File

@@ -8,7 +8,7 @@
## `jsonable_encoder`の使用
JSON互換のデータのみを受信するデータベース`fase_db`があるとしましょう。
JSON互換のデータのみを受信するデータベース`fake_db`があるとしましょう。
例えば、`datetime`オブジェクトはJSONと互換性がないので、このデーターベースには受け取られません。

View File

@@ -63,7 +63,7 @@ Pythonの例外なので、`return`ではなく、`raise`です。
`HTTPException`を発生させる際には、`str`だけでなく、JSONに変換できる任意の値を`detail`パラメータとして渡すことができます。
`dist``list`などを渡すことができます。
`dict``list`などを渡すことができます。
これらは **FastAPI** によって自動的に処理され、JSONに変換されます。

View File

@@ -22,7 +22,7 @@ E isso mostra o verdadeiro compromisso deles com o FastAPI e sua **comunidade**
Por exemplo, você pode querer experimentar:
* <a href="https://speakeasy.com/?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>
* <a href="https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>
* <a href="https://www.stainlessapi.com/?utm_source=fastapi&utm_medium=referral" class="external-link" target="_blank">Stainless</a>
* <a href="https://developers.liblab.com/tutorials/sdk-for-fastapi/?utm_source=fastapi" class="external-link" target="_blank">liblab</a>

View File

@@ -0,0 +1,21 @@
# Расширенное руководство пользователя
## Дополнительные возможности
Основное [Учебник - Руководство пользователя](../tutorial/index.md){.internal-link target=_blank} должно быть достаточно, чтобы познакомить вас со всеми основными функциями **FastAPI**.
В следующих разделах вы увидите другие варианты, конфигурации и дополнительные возможности.
/// tip
Следующие разделы **не обязательно являются "продвинутыми"**.
И вполне возможно, что для вашего случая использования решение находится в одном из них.
///
## Сначала прочитайте Учебник - Руководство пользователя
Вы все еще можете использовать большинство функций **FastAPI** со знаниями из [Учебник - Руководство пользователя](../tutorial/index.md){.internal-link target=_blank}.
И следующие разделы предполагают, что вы уже прочитали его, и предполагают, что вы знаете эти основные идеи.

View File

@@ -0,0 +1,358 @@
# Модель відповіді — Тип, що повертається
Ви можете оголосити тип, який використовуватиметься у відповіді, за допомогою *анотації типу, що повертається* *функцією операцією шляху* (path operation)
**Анотацію типу** можна вказати так само як і для вхідних **параметрів** функції: це можуть бути моделі Pydantic, списки (lists), словники (dictionaries), скалярні значення, як-от цілі числа (integers), булеві значення (booleans) тощо.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
FastAPI використовуватиме цей тип, щоб:
* **Перевірити правильність** повернених даних.
* Якщо дані не валідні (наприклад, відсутнє поле), це означає, що Ваш код додатку працює некоректно і не повертає те, що повинен. У такому випадку FastAPI поверне помилку сервера, замість того щоб віддати недопустимі дані. Так Ви та Ваші клієнти будете впевнені, що отримуєте очікувані дані у правильному форматі.
* Додати **JSON Schema** відповіді до специфікації OpenAPI в *операціях шляху*.
* Це буде використано в **автоматичній документації**.
* А також інструментами, які автоматично генерують клієнтський код.
Але найголовніше:
* FastAPI **обмежить та відфільтрує** вихідні дані відповідно до типу, вказаного у відповіді.
* Це особливо важливо для **безпеки**. Деталі нижче.
## Параметр `response_model`
Іноді Вам потрібно або зручно повертати інші типи даних, ніж ті, що зазначені як тип відповіді.
Наприклад, Ви можете **повертати словник** або об’єкт бази даних, але **оголосити модель Pydantic** як модель відповіді. Тоді модель Pydantic автоматично оброблятиме валідацію, документацію тощо.
Якщо Ви додасте анотацію типу для повернення, редактор коду або mypy можуть поскаржитися, що функція повертає інший тип (наприклад, dict замість Item).
У таких випадках можна скористатися параметром `response_model` в декораторі маршруту (наприклад, @app.get()).
Параметр `response_model` працює з будь-яким *оператором шляху*:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* тощо.
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *}
/// note | Примітка
Зверніть увагу, що `response_model` є параметром методу-декоратора (`get`, `post`, тощо), а не *функцією операцією шляху* (path operation function), як це робиться з параметрами або тілом запиту.
///
`response_model` приймає такий самий тип, який Ви б вказали для поля моделі Pydantic. Тобто це може бути як Pydantic-модель, так і, наприклад, `list` із моделей Pydantic — `List[Item]`.
FastAPI використовуватиме `response_model` для створення документації, валідації даних та — найважливіше — **перетворення та фільтрації вихідних даних** згідно з оголошеним типом.
/// tip | Порада
Якщо у Вас увімкнено сувору перевірку типів у редакторі, mypy тощо, Ви можете оголосити тип повернення функції як `Any`.
Таким чином, Ви повідомляєте редактору, що свідомо повертаєте будь-що. Але FastAPI усе одно виконуватиме створення документації, валідацію, фільтрацію тощо за допомогою параметра `response_model`.
///
### Пріоритет `response_model`
Якщо Ви вказуєте і тип повернення, і `response_model`, то FastAPI використовуватиме `response_model` з пріоритетом.
Таким чином, Ви можете додати правильні анотації типів до ваших функцій, навіть якщо вони повертають тип, відмінний від `response_model`. Це буде корисно для редакторів коду та інструментів, таких як mypy. І при цьому FastAPI продовжить виконувати валідацію даних, генерувати документацію тощо на основі `response_model`.
Ви також можете використати `response_model=None`, щоб вимкнути створення моделі відповіді для цієї *операції шляху*. Це може знадобитися, якщо Ви додаєте анотації типів до об'єктів, які не є допустимими полями Pydantic — приклад цього Ви побачите в одному з наступних розділів.
## Повернути ті самі вхідні дані
Тут ми оголошуємо модель `UserIn`, яка містить звичайний текстовий пароль:
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *}
/// info | Інформація
Щоб використовувати `EmailStr`, спочатку встановіть <a href="https://github.com/JoshData/python-email-validator" class="external-link" target="_blank">`email-validator`</a>.
Переконайтесь, що Ви створили [віртуальне середовище](../virtual-environments.md){.internal-link target=_blank}, активували його, а потім встановили пакет, наприклад:
```console
$ pip install email-validator
```
or with:
```console
$ pip install "pydantic[email]"
```
///
І ми використовуємо цю модель, щоб оголосити і вхідні, і вихідні дані:
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *}
Тепер, коли браузер створює користувача з паролем, API поверне той самий пароль у відповіді.
У цьому випадку це може не бути проблемою, адже саме користувач надіслав пароль.
Але якщо ми використаємо цю ж модель для іншої операції шляху, ми можемо випадково надіслати паролі наших користувачів кожному клієнту.
/// danger | Обережно
Ніколи не зберігайте пароль користувача у відкритому вигляді та не надсилайте його у відповіді, якщо тільки Ви не знаєте всі ризики і точно розумієте, що робите.
///
## Додайте окрему вихідну модель
Замість цього ми можемо створити вхідну модель з відкритим паролем і вихідну модель без нього:
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *}
Тут, навіть якщо *функція операції шляху* повертає об'єкт користувача, який містить пароль:
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *}
...ми оголосили `response_model` як нашу модель `UserOut`, яка не містить пароля:
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *}
Таким чином, **FastAPI** автоматично відфільтрує всі дані, які не вказані у вихідній моделі (за допомогою Pydantic).
### `response_model` або тип повернення
У цьому випадку, оскільки дві моделі різні, якщо ми анотуємо тип повернення функції як `UserOut`, редактор і такі інструменти, як mypy, видадуть помилку, бо фактично ми повертаємо інший тип.
Тому в цьому прикладі ми використовуємо параметр `response_model`, а не анотацію типу повернення.
...але читайте далі, щоб дізнатися, як обійти це обмеження.
## Тип повернення і фільтрація даних
Продовжимо з попереднього прикладу. Ми хотіли **анотувати функцію одним типом**, але при цьому повертати з неї більше даних.
Ми хочемо, щоб FastAPI продовжував **фільтрувати** ці дані за допомогою response_model. Тобто навіть якщо функція повертає більше інформації, у відповіді будуть лише ті поля, які вказані у response_model.
У попередньому прикладі, оскільки класи були різні, нам довелося використовувати параметр `response_model`. Але це означає, що ми не отримуємо підтримки з боку редактора коду та інструментів перевірки типів щодо типу, який повертає функція.
Проте в більшості випадків, коли нам потрібно зробити щось подібне, ми просто хочемо, щоб модель **відфільтрувала або прибрала** частину даних, як у цьому прикладі.
У таких випадках ми можемо використати класи та спадкування, щоб скористатися **анотаціями типів** функцій — це дає кращу підтримку з боку редактора та інструментів типу mypy, і при цьому FastAPI продовжує виконувати **фільтрацію даних** у відповіді.
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *}
Завдяки цьому ми отримуємо підтримку інструментів — від редакторів і mypy, оскільки цей код є коректним з точки зору типів, — але ми також отримуємо фільтрацію даних від FastAPI.
Як це працює? Давайте розберемося. 🤓
### Типи та підтримка інструментів
Спершу подивимось, як це бачать редактори, mypy та інші інструменти.
`BaseUser` має базові поля. Потім `UserIn` успадковує `BaseUser` і додає поле `password`, отже, він матиме всі поля з обох моделей.
Ми зазначаємо тип повернення функції як `BaseUser`, але фактично повертаємо екземпляр `UserIn`.
Редактор, mypy та інші інструменти не скаржитимуться на це, тому що з точки зору типізації `UserIn` є підкласом `BaseUser`, а це означає, що він є `валідним` типом, коли очікується будь-що, що є `BaseUser`.
### Фільтрація даних у FastAPI
Тепер для FastAPI він бачить тип повернення і переконується, що те, що Ви повертаєте, містить **тільки** поля, які оголошені у цьому типі.
FastAPI виконує кілька внутрішніх операцій з Pydantic, щоб гарантувати, що правила наслідування класів не застосовуються для фільтрації повернених даних, інакше Ви могли б повернути значно більше даних, ніж очікували.
Таким чином, Ви отримуєте найкраще з двох світів: анотації типів **з підтримкою інструментів** і **фільтрацію даних**.
## Подивитись у документації
Коли Ви дивитесь автоматичну документацію, Ви можете побачити, що вхідна модель і вихідна модель мають власну JSON-схему:
<img src="/img/tutorial/response-model/image01.png">
І обидві моделі використовуються для інтерактивної API-документації:
<img src="/img/tutorial/response-model/image02.png">
## Інші анотації типів повернення
Існують випадки, коли Ви повертаєте щось, що не є допустимим полем Pydantic, але анотуєте це у функції лише для того, щоб отримати підтримку від інструментів (редактора, mypy тощо).
### Повернення Response напряму
Найпоширенішим випадком буде [повернення Response напряму, як пояснюється пізніше у розширеній документації](../advanced/response-directly.md){.internal-link target=_blank}.
{* ../../docs_src/response_model/tutorial003_02.py hl[8,10:11] *}
Цей простий випадок автоматично обробляється FastAPI, тому що анотація типу повернення — це клас (або підклас) `Response`.
І інструменти також будуть задоволені, бо і `RedirectResponse`, і `JSONResponse` є підкласами `Response`, отже анотація типу коректна.
### Анотація підкласу Response
Також можна використовувати підклас `Response` у анотації типу:
{* ../../docs_src/response_model/tutorial003_03.py hl[8:9] *}
Це теж працюватиме, бо `RedirectResponse` — підклас `Response`, і FastAPI автоматично обробить цей простий випадок.
### Некоректні анотації типу повернення
Але коли Ви повертаєте якийсь інший довільний об’єкт, що не є валідним типом Pydantic (наприклад, об’єкт бази даних), і анотуєте його так у функції, FastAPI спробує створити Pydantic модель відповіді на основі цієї анотації типу, і це завершиться помилкою.
Те саме станеться, якщо Ви використовуєте <abbr title="Об'єднання (union) кількох типів означає: «будь-який з цих типів».">union</abbr> між різними типами, де один або більше не є валідними типами Pydantic, наприклад, це спричинить помилку 💥:
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
...це не працює, тому що тип анотації не є типом Pydantic і не є просто класом `Response` або його підкласом, а є об’єднанням (union) — або `Response`, або `dict`.
### Відключення Моделі Відповіді
Продовжуючи приклад вище, можливо, Ви не хочете використовувати стандартну валідацію даних, автоматичну документацію, фільтрацію тощо, які FastAPI виконує за замовчуванням.
Але ви все одно можете залишити анотацію типу у функції, щоб зберегти підтримку з боку інструментів, таких як редактори коду або статичні перевірки типів (наприклад, mypy).
У такому випадку ви можете вимкнути генерацію моделі відповіді, встановивши `response_model=None`:
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
Це змусить FastAPI пропустити генерацію моделі відповіді, і таким чином Ви зможете використовувати будь-які анотації типів повернення без впливу на вашу FastAPI аплікацію. 🤓
## Параметри кодування моделі відповіді
Ваша модель відповіді може мати значення за замовчуванням, наприклад:
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *}
* `description: Union[str, None] = None` (або `str | None = None` у Python 3.10) має значення за замовчуванням `None`.
* `tax: float = 10.5` має значення за замовчуванням `10.5`.
* `tags: List[str] = []` має значення за замовчуванням порожній список: `[]`.
Але Ви можете захотіти не включати їх у результат, якщо вони фактично не були збережені.
Наприклад, якщо у Вас є моделі з багатьма необов’язковими атрибутами у NoSQL базі даних, але Ви не хочете відправляти дуже довгі JSON-відповіді, повні значень за замовчуванням.
### Використовуйте параметр `response_model_exclude_unset`
Ви можете встановити параметр декоратора шляху `response_model_exclude_unset=True`:
{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *}
і ці значення за замовчуванням не будуть включені у відповідь, тільки фактично встановлені значення.
Отже, якщо Ви надішлете запит до цього оператора шляху для елемента з item_id `foo`, відповідь (без включення значень за замовчуванням) буде:
```JSON
{
"name": "Foo",
"price": 50.2
}
```
/// info | Інформація
У Pydantic версії 1 метод називався `.dict()`, він був застарілий (але ще підтримується) у Pydantic версії 2 і перейменований у `.model_dump()`.
Приклади тут використовують `.dict()` для сумісності з Pydantic v1, але Вам слід використовувати `.model_dump()`, якщо Ви можете використовувати Pydantic v2.
///
/// info | Інформація
FastAPI використовує `.dict()` моделі Pydantic з <a href="https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict" class="external-link" target="_blank">параметром `exclude_unset`</a>, щоб досягти цього.
///
/// info | Інформація
Ви також можете використовувати:
* `response_model_exclude_defaults=True`
* `response_model_exclude_none=True`
як описано в <a href="https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict" class="external-link" target="_blank">документації Pydantic</a> for `exclude_defaults` та `exclude_none`.
///
#### Дані зі значеннями для полів із типовими значеннями
Але якщо Ваші дані мають значення для полів моделі з типовими значеннями, як у елемента з item_id `bar`:
```Python hl_lines="3 5"
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
```
вони будуть включені у відповідь.
#### Дані з тими самими значеннями, що й типові
Якщо дані мають ті самі значення, що й типові, як у елемента з item_id `baz`:
```Python hl_lines="3 5-6"
{
"name": "Baz",
"description": None,
"price": 50.2,
"tax": 10.5,
"tags": []
}
```
FastAPI достатньо розумний (насправді, Pydantic достатньо розумний), щоб зрозуміти, що, хоча `description`, `tax` і `tags` мають ті самі значення, що й типові, вони були встановлені явно (а не взяті як значення за замовчуванням).
Отже, вони будуть включені у JSON-відповідь.
/// tip | Порада
Зверніть увагу, що типові значення можуть бути будь-якими, не лише `None`.
Це може бути list (`[]`), `float` 10.5 тощо.
///
### `response_model_include` та `response_model_exclude`
Ви також можете використовувати параметри *декоратора операції шляху* `response_model_include` та `response_model_exclude`.
Вони приймають `set` (множину) рядків (`str`) з іменами атрибутів, які потрібно включити (пропускаючи інші) або виключити (включаючи інші).
Це можна використовувати як швидкий спосіб, якщо у Вас є лише одна модель Pydantic і Ви хочете видалити деякі дані з виводу.
/// tip | Порада
Але все ж рекомендується використовувати описані вище підходи, із застосуванням кількох класів, замість цих параметрів.
Це тому, що JSON Schema, який генерується у вашому OpenAPI додатку (і в документації), все одно буде відповідати повній моделі, навіть якщо Ви використовуєте `response_model_include` або `response_model_exclude` для виключення деяких атрибутів.
Це також стосується `response_model_by_alias`, який працює подібним чином.
///
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *}
/// tip | Порада
Синтаксис `{"name", "description"}` створює `set` з цими двома значеннями.
Він еквівалентний `set(["name", "description"])`.
///
#### Використання `list` замість `set`
Якщо Ви забудете використати `set` і натомість застосуєте `list` або `tuple`, FastAPI все одно перетворить це на `set`, і все працюватиме правильно:
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
## Підсумок
Використовуйте параметр `response_model` *декоратора операції шляху*, щоб визначати моделі відповіді, особливо щоб гарантувати фільтрацію приватних даних.
Використовуйте `response_model_exclude_unset`, щоб повертати лише явно встановлені значення.

View File

@@ -0,0 +1,104 @@
# Безпека
Існує багато способів реалізувати безпеку, автентифікацію та авторизацію.
Це зазвичай складна і "непроста" тема.
У багатьох фреймворках і системах забезпечення безпеки та автентифікації займає величезну частину зусиль і коду (іноді — понад 50% всього написаного коду).
**FastAPI** надає кілька інструментів, які допоможуть Вам впоратися з **безпекою** легко, швидко, стандартним способом, без необхідності вивчати всі специфікації безпеки.
Але спочатку — кілька коротких понять.
## Поспішаєте?
Якщо Вам не цікаві всі ці терміни й просто потрібно *швидко* додати автентифікацію за логіном і паролем — переходьте до наступних розділів.
## OAuth2
OAuth2 — це специфікація, що описує кілька способів обробки автентифікації та авторизації.
Це досить об'ємна специфікація, яка охоплює складні випадки використання.
Вона включає способи автентифікації через "третю сторону".
Саме це лежить в основі "входу через Google, Facebook, X (Twitter), GitHub" тощо.
### OAuth 1
Раніше існував OAuth 1, який значно відрізняється від OAuth2 і є складнішим, оскільки містив специфікації для шифрування комунікацій.
Зараз майже не використовується.
OAuth2 не вказує, як саме шифрувати з'єднання — воно очікує, що ваш застосунок працює через HTTPS.
/// tip | Порада
У розділі про **деплой** Ви побачите, як налаштувати HTTPS безкоштовно з Traefik та Let's Encrypt.
///
## OpenID Connect
OpenID Connect — ще одна специфікація, побудована на основі **OAuth2**.
Вона розширює OAuth2, уточнюючи деякі неоднозначності для досягнення кращої сумісності.
Наприклад, вхід через Google використовує OpenID Connect (який базується на OAuth2).
Але вхід через Facebook — ні. Він має власну реалізацію на базі OAuth2.
### OpenID (не "OpenID Connect")
Існувала також специфікація "OpenID", яка намагалася розвʼязати ті самі задачі, що й **OpenID Connect**, але не базувалась на OAuth2.
Це була зовсім інша система, і сьогодні вона майже не використовується.
## OpenAPI
OpenAPI (раніше Swagger) — це специфікація для побудови API (тепер під егідою Linux Foundation).
**FastAPI** базується на **OpenAPI**.
Завдяки цьому Ви отримуєте автоматичну інтерактивну документацію, генерацію коду та багато іншого.
OpenAPI дозволяє описувати різні "схеми" безпеки.
Використовуючи їх, Ви можете скористатися всіма цими інструментами, що базуються на стандартах, зокрема інтерактивними системами документації.
OpenAPI визначає такі схеми безпеки:
* `apiKey`: специфічний для застосунку ключ, який може передаватися через:
* Параметр запиту.
* Заголовок.
* Cookie.
* `http`: стандартні методи HTTP-автентифікації, включаючи:
* `bearer`: заголовок `Authorization` зі значенням `Bearer` та токеном. Це успадковано з OAuth2.
* HTTP Basic автентифікація
* HTTP Digest, тощо.
* `oauth2`: усі способи обробки безпеки за допомогою OAuth2 (так звані «потоки»).
* Деякі з цих потоків підходять для створення власного провайдера автентифікації OAuth 2.0 (наприклад, Google, Facebook, X (Twitter), GitHub тощо):
* `implicit`— неявний
* `clientCredentials`— облікові дані клієнта
* `authorizationCode` — код авторизації
* Але є один окремий «потік», який ідеально підходить для реалізації автентифікації всередині одного додатку:
* `password`: у наступних розділах буде приклад використання цього потоку.
* `openIdConnect`: дозволяє автоматично виявляти параметри автентифікації OAuth2.
* Це автоматичне виявлення визначається у специфікації OpenID Connect.
/// tip | Порада
Інтеграція інших провайдерів автентифікації/авторизації, таких як Google, Facebook, X (Twitter), GitHub тощо — також можлива і відносно проста.
Найскладніше — це створити власного провайдера автентифікації/авторизації, як Google чи Facebook. Але **FastAPI** надає Вам інструменти, щоб зробити це легко, беручи на себе важку частину роботи.
///
## Інструменти **FastAPI**
FastAPI надає кілька інструментів для кожної з описаних схем безпеки в модулі `fastapi.security`, які спрощують використання цих механізмів захисту.
У наступних розділах Ви побачите, як додати безпеку до свого API за допомогою цих інструментів **FastAPI**.
А також побачите, як вона автоматично інтегрується в інтерактивну документацію вашого API.

View File

@@ -1,6 +1,6 @@
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
__version__ = "0.115.13"
__version__ = "0.115.14"
from starlette import status as status

View File

@@ -816,6 +816,25 @@ def request_params_to_args(
return values, errors
def is_union_of_base_models(field_type: Any) -> bool:
"""Check if field type is a Union where all members are BaseModel subclasses."""
from fastapi.types import UnionType
origin = get_origin(field_type)
# Check if it's a Union type (covers both typing.Union and types.UnionType in Python 3.10+)
if origin is not Union and origin is not UnionType:
return False
union_args = get_args(field_type)
for arg in union_args:
if not lenient_issubclass(arg, BaseModel):
return False
return True
def _should_embed_body_fields(fields: List[ModelField]) -> bool:
if not fields:
return False
@@ -829,10 +848,12 @@ def _should_embed_body_fields(fields: List[ModelField]) -> bool:
# If it explicitly specifies it is embedded, it has to be embedded
if getattr(first_field.field_info, "embed", None):
return True
# If it's a Form (or File) field, it has to be a BaseModel to be top level
# If it's a Form (or File) field, it has to be a BaseModel (or a union of BaseModels) to be top level
# otherwise it has to be embedded, so that the key value pair can be extracted
if isinstance(first_field.field_info, params.Form) and not lenient_issubclass(
first_field.type_, BaseModel
if (
isinstance(first_field.field_info, params.Form)
and not lenient_issubclass(first_field.type_, BaseModel)
and not is_union_of_base_models(first_field.type_)
):
return True
return False

156
tests/test_union_forms.py Normal file
View File

@@ -0,0 +1,156 @@
from typing import Union
from fastapi import FastAPI, Form
from fastapi.testclient import TestClient
from pydantic import BaseModel
from typing_extensions import Annotated
app = FastAPI()
class UserForm(BaseModel):
name: str
email: str
class CompanyForm(BaseModel):
company_name: str
industry: str
@app.post("/form-union/")
def post_union_form(data: Annotated[Union[UserForm, CompanyForm], Form()]):
return {"received": data}
client = TestClient(app)
def test_post_user_form():
response = client.post(
"/form-union/", data={"name": "John Doe", "email": "john@example.com"}
)
assert response.status_code == 200, response.text
assert response.json() == {
"received": {"name": "John Doe", "email": "john@example.com"}
}
def test_post_company_form():
response = client.post(
"/form-union/", data={"company_name": "Tech Corp", "industry": "Technology"}
)
assert response.status_code == 200, response.text
assert response.json() == {
"received": {"company_name": "Tech Corp", "industry": "Technology"}
}
def test_invalid_form_data():
response = client.post(
"/form-union/",
data={"name": "John", "company_name": "Tech Corp"},
)
assert response.status_code == 422, response.text
def test_empty_form():
response = client.post("/form-union/")
assert response.status_code == 422, response.text
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/form-union/": {
"post": {
"summary": "Post Union Form",
"operationId": "post_union_form_form_union__post",
"requestBody": {
"content": {
"application/x-www-form-urlencoded": {
"schema": {
"anyOf": [
{"$ref": "#/components/schemas/UserForm"},
{"$ref": "#/components/schemas/CompanyForm"},
],
"title": "Data",
}
}
},
"required": True,
},
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
}
},
"components": {
"schemas": {
"CompanyForm": {
"properties": {
"company_name": {"type": "string", "title": "Company Name"},
"industry": {"type": "string", "title": "Industry"},
},
"type": "object",
"required": ["company_name", "industry"],
"title": "CompanyForm",
},
"HTTPValidationError": {
"properties": {
"detail": {
"items": {"$ref": "#/components/schemas/ValidationError"},
"type": "array",
"title": "Detail",
}
},
"type": "object",
"title": "HTTPValidationError",
},
"UserForm": {
"properties": {
"name": {"type": "string", "title": "Name"},
"email": {"type": "string", "title": "Email"},
},
"type": "object",
"required": ["name", "email"],
"title": "UserForm",
},
"ValidationError": {
"properties": {
"loc": {
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
"type": "array",
"title": "Location",
},
"msg": {"type": "string", "title": "Message"},
"type": {"type": "string", "title": "Error Type"},
},
"type": "object",
"required": ["loc", "msg", "type"],
"title": "ValidationError",
},
}
},
}