25 KiB
Модель відповіді — Тип, що повертається
Ви можете оголосити тип, який використовуватиметься у відповіді, за допомогою анотації типу, що повертається функцією операцією шляху (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
, спочатку встановіть email-validator
.
Переконайтесь, що Ви створили віртуальне середовище{.internal-link target=_blank}, активували його, а потім встановили пакет, наприклад:
$ pip install email-validator
or with:
$ 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-схему:

І обидві моделі використовуються для інтерактивної API-документації:

Інші анотації типів повернення
Існують випадки, коли Ви повертаєте щось, що не є допустимим полем Pydantic, але анотуєте це у функції лише для того, щоб отримати підтримку від інструментів (редактора, mypy тощо).
Повернення Response напряму
Найпоширенішим випадком буде повернення Response напряму, як пояснюється пізніше у розширеній документації{.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 модель відповіді на основі цієї анотації типу, і це завершиться помилкою.
Те саме станеться, якщо Ви використовуєте union між різними типами, де один або більше не є валідними типами 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
, відповідь (без включення значень за замовчуванням) буде:
{
"name": "Foo",
"price": 50.2
}
/// info | Інформація
У Pydantic версії 1 метод називався .dict()
, він був застарілий (але ще підтримується) у Pydantic версії 2 і перейменований у .model_dump()
.
Приклади тут використовують .dict()
для сумісності з Pydantic v1, але Вам слід використовувати .model_dump()
, якщо Ви можете використовувати Pydantic v2.
///
/// info | Інформація
FastAPI використовує .dict()
моделі Pydantic з параметром exclude_unset
, щоб досягти цього.
///
/// info | Інформація
Ви також можете використовувати:
response_model_exclude_defaults=True
response_model_exclude_none=True
як описано в документації Pydantic for exclude_defaults
та exclude_none
.
///
Дані зі значеннями для полів із типовими значеннями
Але якщо Ваші дані мають значення для полів моделі з типовими значеннями, як у елемента з item_id bar
:
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
вони будуть включені у відповідь.
Дані з тими самими значеннями, що й типові
Якщо дані мають ті самі значення, що й типові, як у елемента з item_id baz
:
{
"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
, щоб повертати лише явно встановлені значення.