mirror of
https://github.com/fastapi/fastapi.git
synced 2025-12-29 01:00:51 -05:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebdeda2de6 | ||
|
|
8fa19a6faa | ||
|
|
3ecb4c5389 | ||
|
|
9d0d8828cc | ||
|
|
df35896a0e | ||
|
|
8f64d09ee0 | ||
|
|
3b09dd8e01 | ||
|
|
c30821ff6e | ||
|
|
666890ac7f | ||
|
|
937af92ba7 | ||
|
|
1cf4b8c2de | ||
|
|
dcb223d850 | ||
|
|
baeeafa1e1 | ||
|
|
487f940e91 | ||
|
|
e9c33debaa | ||
|
|
041a37bb1f | ||
|
|
28038f19cf | ||
|
|
9026f3b1bd | ||
|
|
8d9ef5d343 | ||
|
|
b2923282ca | ||
|
|
ac160885e1 | ||
|
|
7d9195a248 |
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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. 🤓
|
||||
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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}.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
追加情報は`Field`や`Query`、`Body`などで宣言することができます。そしてそれは生成されたJSONスキーマに含まれます。
|
||||
|
||||
後に例を用いて宣言を学ぶ際に、追加情報を句悪方法を学べます。
|
||||
後に例を用いて宣言を学ぶ際に、追加情報を追加する方法を学べます。
|
||||
|
||||
## まとめ
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
## `jsonable_encoder`の使用
|
||||
|
||||
JSON互換のデータのみを受信するデータベース`fase_db`があるとしましょう。
|
||||
JSON互換のデータのみを受信するデータベース`fake_db`があるとしましょう。
|
||||
|
||||
例えば、`datetime`オブジェクトはJSONと互換性がないので、このデーターベースには受け取られません。
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ Pythonの例外なので、`return`ではなく、`raise`です。
|
||||
|
||||
`HTTPException`を発生させる際には、`str`だけでなく、JSONに変換できる任意の値を`detail`パラメータとして渡すことができます。
|
||||
|
||||
`dist`や`list`などを渡すことができます。
|
||||
`dict`や`list`などを渡すことができます。
|
||||
|
||||
これらは **FastAPI** によって自動的に処理され、JSONに変換されます。
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
21
docs/ru/docs/advanced/index.md
Normal file
21
docs/ru/docs/advanced/index.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Расширенное руководство пользователя
|
||||
|
||||
## Дополнительные возможности
|
||||
|
||||
Основное [Учебник - Руководство пользователя](../tutorial/index.md){.internal-link target=_blank} должно быть достаточно, чтобы познакомить вас со всеми основными функциями **FastAPI**.
|
||||
|
||||
В следующих разделах вы увидите другие варианты, конфигурации и дополнительные возможности.
|
||||
|
||||
/// tip
|
||||
|
||||
Следующие разделы **не обязательно являются "продвинутыми"**.
|
||||
|
||||
И вполне возможно, что для вашего случая использования решение находится в одном из них.
|
||||
|
||||
///
|
||||
|
||||
## Сначала прочитайте Учебник - Руководство пользователя
|
||||
|
||||
Вы все еще можете использовать большинство функций **FastAPI** со знаниями из [Учебник - Руководство пользователя](../tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
И следующие разделы предполагают, что вы уже прочитали его, и предполагают, что вы знаете эти основные идеи.
|
||||
358
docs/uk/docs/tutorial/response-model.md
Normal file
358
docs/uk/docs/tutorial/response-model.md
Normal 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`, щоб повертати лише явно встановлені значення.
|
||||
104
docs/uk/docs/tutorial/security/index.md
Normal file
104
docs/uk/docs/tutorial/security/index.md
Normal 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.
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
156
tests/test_union_forms.py
Normal 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",
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user