# Модель відповіді — Тип, що повертається

Ви можете оголосити тип, який використовуватиметься у відповіді, за допомогою *анотації типу, що повертається* *функцією операцією шляху* (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`, щоб повертати лише явно встановлені значення.
