fastapi/docs/uk/docs/tutorial/response-model.md

25 KiB
Raw Blame History

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

Ви можете оголосити тип, який використовуватиметься у відповіді, анотувавши тип повернення функції операції шляху.

Анотації типів можна використовувати так само, як і для вхідних даних у параметрах функції: можна використовувати моделі Pydantic, списки, словники, скалярні значення, як-от цілі числа, булеві значення тощо.

{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}

FastAPI використовуватиме цей тип повернення, щоб:

  • Перевірити правильність повернених даних.
    • Якщо дані не валідні (наприклад, відсутнє поле), це означає, що ваш код застосунку зламаний, не повертає те, що повинен, і буде повернуто помилку сервера замість некоректних даних. Так ви та ваші клієнти можете бути впевнені, що отримаєте дані й очікувану структуру даних.
  • Додати JSON Schema для відповіді в OpenAPI операції шляху.
    • Це буде використано в автоматичній документації.
    • Це також буде використано інструментами, які автоматично генерують клієнтський код.

Але найголовніше:

  • Це обмежить та відфільтрує вихідні дані до того, що визначено в типі повернення.
    • Це особливо важливо для безпеки, нижче ми побачимо про це більше.

Параметр response_model

Є випадки, коли вам потрібно або ви хочете повертати дані, які не зовсім відповідають тому, що оголошено типом.

Наприклад, ви можете захотіти повертати словник або об’єкт бази даних, але оголосити його як модель Pydantic. Таким чином модель Pydantic виконуватиме всю документацію даних, валідацію тощо для об’єкта, який ви повернули (наприклад, словника або об’єкта бази даних).

Якщо ви додали анотацію типу повернення, інструменти та редактори скаржитимуться (коректною) помилкою, повідомляючи, що ваша функція повертає тип (наприклад, dict), який відрізняється від того, що ви оголосили (наприклад, модель Pydantic).

У таких випадках можна скористатися параметром декоратора операції шляху response_model замість типу повернення.

Ви можете використовувати параметр 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 тощо). А не вашої функції операції шляху, як усі параметри та тіло.

///

response_model приймає такий самий тип, який ви б вказали для поля моделі Pydantic, тобто це може бути модель Pydantic, але також це може бути, наприклад, list моделей Pydantic, як-от List[Item].

FastAPI використовуватиме цей response_model для виконання всієї документації даних, валідації тощо, а також для перетворення та фільтрації вихідних даних до оголошеного типу.

/// tip | Порада

Якщо у вас увімкнено сувору перевірку типів у редакторі, mypy тощо, ви можете оголосити тип повернення функції як Any.

Таким чином, ви повідомляєте редактору, що свідомо повертаєте будь-що. Але FastAPI усе одно виконуватиме документацію даних, валідацію, фільтрацію тощо за допомогою response_model.

///

Пріоритет response_model

Якщо ви оголошуєте і тип повернення, і response_model, то response_model матиме пріоритет і буде використаний FastAPI.

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

Саме тому в цьому прикладі нам треба оголосити це через параметр response_model.

...але читайте далі нижче, щоб побачити, як це обійти.

Тип повернення і фільтрація даних

Продовжимо з попереднього прикладу. Ми хотіли анотувати функцію одним типом, але хотіли мати змогу повертати з функції те, що насправді містить більше даних.

Ми хочемо, щоб FastAPI продовжував фільтрувати дані, використовуючи модель відповіді. Тобто навіть якщо функція повертає більше даних, відповідь міститиме лише поля, оголошені в моделі відповіді.

У попередньому прикладі, оскільки класи були різні, нам довелося використовувати параметр response_model. Але це також означає, що ми не отримуємо підтримки від редактора та інструментів, які перевіряють тип повернення функції.

Проте в більшості випадків, коли нам потрібно зробити щось подібне, ми просто хочемо, щоб модель відфільтрувала/прибрала частину даних, як у цьому прикладі.

І в таких випадках ми можемо використати класи та спадкування, щоб скористатися анотаціями типів функцій і отримати кращу підтримку в редакторі та інструментах, і при цьому зберегти фільтрацію даних у 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 Schema:

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

Інші анотації типів повернення

Можуть бути випадки, коли ви повертаєте щось, що не є валідним полем Pydantic, і анотуєте це у функції лише для того, щоб отримати підтримку від інструментів (редактора, mypy тощо).

Повернути Response напряму

Найпоширенішим випадком буде повернення Response напряму, як пояснюється пізніше у розширеній документації{.internal-link target=_blank}.

{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *}

Цей простий випадок автоматично обробляється FastAPI, тому що анотація типу повернення — це клас (або підклас) Response.

І інструменти також будуть задоволені, бо і RedirectResponse, і JSONResponse є підкласами Response, отже анотація типу коректна.

Анотувати підклас Response

Ви також можете використати підклас Response в анотації типу:

{* ../../docs_src/response_model/tutorial003_03_py39.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] *}

і ці значення за замовчуванням не будуть включені у відповідь, лише значення, які фактично встановлені.

Отже, якщо ви надішлете запит до цієї операції шляху для елемента з ID foo, відповідь (без включення значень за замовчуванням) буде:

{
    "name": "Foo",
    "price": 50.2
}

/// info | Інформація

Ви також можете використовувати:

  • response_model_exclude_defaults=True
  • response_model_exclude_none=True

як описано в документації Pydantic для exclude_defaults та exclude_none.

///

Дані зі значеннями для полів із типовими значеннями

Але якщо ваші дані мають значення для полів моделі з типовими значеннями, як у елемента з ID bar:

{
    "name": "Bar",
    "description": "The bartenders",
    "price": 62,
    "tax": 20.2
}

вони будуть включені у відповідь.

Дані з тими самими значеннями, що й типові

Якщо дані мають ті самі значення, що й типові, як у елемента з 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, щоб повертати лише явно встановлені значення.