Sync fastapi docs from b5ca1324 on 2025-12-07
Issue Manager / issue-manager (push) Has been cancelled
Build Docs / changes (push) Has been cancelled
Build Docs / langs (push) Has been cancelled
Build Docs / build-docs (push) Has been cancelled
Build Docs / docs-all-green (push) Has been cancelled
Conflict detector / main (push) Has been cancelled
Test Redistribute / test-redistribute (fastapi) (push) Has been cancelled
Test Redistribute / test-redistribute (fastapi-slim) (push) Has been cancelled
Test Redistribute / test-redistribute-alls-green (push) Has been cancelled
Test / lint (push) Has been cancelled
Test / test (pydantic-v1, 3.10) (push) Has been cancelled
Test / test (pydantic-v1, 3.11) (push) Has been cancelled
Test / test (pydantic-v1, 3.13) (push) Has been cancelled
Test / test (pydantic-v1, 3.8) (push) Has been cancelled
Test / test (pydantic-v1, 3.9) (push) Has been cancelled
Test / test (pydantic-v2, 3.10) (push) Has been cancelled
Test / test (pydantic-v2, 3.11) (push) Has been cancelled
Test / test (pydantic-v2, 3.12) (push) Has been cancelled
Test / test (pydantic-v2, 3.13) (push) Has been cancelled
Test / test (pydantic-v2, 3.14) (push) Has been cancelled
Test / test (pydantic-v2, 3.8) (push) Has been cancelled
Test / test (pydantic-v2, 3.9) (push) Has been cancelled
Test / coverage-combine (push) Has been cancelled
Test / check (push) Has been cancelled
Label Approved / label-approved (push) Has been cancelled
FastAPI People Contributors / job (push) Has been cancelled
FastAPI People Sponsors / job (push) Has been cancelled
Update Topic Repos / topic-repos (push) Has been cancelled
FastAPI People / job (push) Has been cancelled
Test / test (pydantic-v1, 3.12) (push) Has been cancelled
Issue Manager / issue-manager (push) Has been cancelled
Build Docs / changes (push) Has been cancelled
Build Docs / langs (push) Has been cancelled
Build Docs / build-docs (push) Has been cancelled
Build Docs / docs-all-green (push) Has been cancelled
Conflict detector / main (push) Has been cancelled
Test Redistribute / test-redistribute (fastapi) (push) Has been cancelled
Test Redistribute / test-redistribute (fastapi-slim) (push) Has been cancelled
Test Redistribute / test-redistribute-alls-green (push) Has been cancelled
Test / lint (push) Has been cancelled
Test / test (pydantic-v1, 3.10) (push) Has been cancelled
Test / test (pydantic-v1, 3.11) (push) Has been cancelled
Test / test (pydantic-v1, 3.13) (push) Has been cancelled
Test / test (pydantic-v1, 3.8) (push) Has been cancelled
Test / test (pydantic-v1, 3.9) (push) Has been cancelled
Test / test (pydantic-v2, 3.10) (push) Has been cancelled
Test / test (pydantic-v2, 3.11) (push) Has been cancelled
Test / test (pydantic-v2, 3.12) (push) Has been cancelled
Test / test (pydantic-v2, 3.13) (push) Has been cancelled
Test / test (pydantic-v2, 3.14) (push) Has been cancelled
Test / test (pydantic-v2, 3.8) (push) Has been cancelled
Test / test (pydantic-v2, 3.9) (push) Has been cancelled
Test / coverage-combine (push) Has been cancelled
Test / check (push) Has been cancelled
Label Approved / label-approved (push) Has been cancelled
FastAPI People Contributors / job (push) Has been cancelled
FastAPI People Sponsors / job (push) Has been cancelled
Update Topic Repos / topic-repos (push) Has been cancelled
FastAPI People / job (push) Has been cancelled
Test / test (pydantic-v1, 3.12) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,483 @@
|
||||
# Альтернативи, натхнення та порівняння
|
||||
|
||||
Що надихнуло на створення **FastAPI**, який він у порінянні з іншими альтернативами та чого він у них навчився.
|
||||
|
||||
## Вступ
|
||||
|
||||
**FastAPI** не існувало б, якби не попередні роботи інших.
|
||||
|
||||
Раніше було створено багато інструментів, які надихнули на його створення.
|
||||
|
||||
Я кілька років уникав створення нового фреймворку. Спочатку я спробував вирішити всі функції, охоплені **FastAPI**, використовуючи багато різних фреймворків, плагінів та інструментів.
|
||||
|
||||
Але в якийсь момент не було іншого виходу, окрім створення чогось, що надавало б усі ці функції, взявши найкращі ідеї з попередніх інструментів і поєднавши їх найкращим чином, використовуючи мовні функції, які навіть не були доступні раніше (Python 3.6+ підказки типів).
|
||||
|
||||
## Попередні інструменти
|
||||
|
||||
### <a href="https://www.djangoproject.com/" class="external-link" target="_blank">Django</a>
|
||||
|
||||
Це найпопулярніший фреймворк Python, який користується широкою довірою. Він використовується для створення таких систем, як Instagram.
|
||||
|
||||
Він відносно тісно пов’язаний з реляційними базами даних (наприклад, MySQL або PostgreSQL), тому мати базу даних NoSQL (наприклад, Couchbase, MongoDB, Cassandra тощо) як основний механізм зберігання не дуже просто.
|
||||
|
||||
Він був створений для створення HTML у серверній частині, а не для створення API, які використовуються сучасним інтерфейсом (як-от React, Vue.js і Angular) або іншими системами (як-от <abbr title="Internet of Things">IoT</abbr > пристрої), які спілкуються з ним.
|
||||
|
||||
### <a href="https://www.django-rest-framework.org/" class="external-link" target="_blank">Django REST Framework</a>
|
||||
|
||||
Фреймворк Django REST був створений як гнучкий інструментарій для створення веб-інтерфейсів API використовуючи Django в основі, щоб покращити його можливості API.
|
||||
|
||||
Його використовують багато компаній, включаючи Mozilla, Red Hat і Eventbrite.
|
||||
|
||||
Це був один із перших прикладів **автоматичної документації API**, і саме це була одна з перших ідей, яка надихнула на «пошук» **FastAPI**.
|
||||
|
||||
/// note | Примітка
|
||||
|
||||
Django REST Framework створив Том Крісті. Той самий творець Starlette і Uvicorn, на яких базується **FastAPI**.
|
||||
|
||||
///
|
||||
|
||||
/// check | Надихнуло **FastAPI** на
|
||||
|
||||
Мати автоматичний веб-інтерфейс документації API.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://flask.palletsprojects.com" class="external-link" target="_blank">Flask</a>
|
||||
|
||||
Flask — це «мікрофреймворк», він не включає інтеграцію бази даних, а також багато речей, які за замовчуванням є в Django.
|
||||
|
||||
Ця простота та гнучкість дозволяють використовувати бази даних NoSQL як основну систему зберігання даних.
|
||||
|
||||
Оскільки він дуже простий, він порівняно легкий та інтуїтивний для освоєння, хоча в деяких моментах документація стає дещо технічною.
|
||||
|
||||
Він також зазвичай використовується для інших програм, яким не обов’язково потрібна база даних, керування користувачами або будь-яка з багатьох функцій, які є попередньо вбудованими в Django. Хоча багато з цих функцій можна додати за допомогою плагінів.
|
||||
|
||||
Відокремлення частин було ключовою особливістю, яку я хотів зберегти, при цьому залишаючись «мікрофреймворком», який можна розширити, щоб охопити саме те, що потрібно.
|
||||
|
||||
Враховуючи простоту Flask, він здавався хорошим підходом для створення API. Наступним, що знайшов, був «Django REST Framework» для Flask.
|
||||
|
||||
/// check | Надихнуло **FastAPI** на
|
||||
|
||||
Бути мікрофреймоворком. Зробити легким комбінування та поєднання необхідних інструментів та частин.
|
||||
|
||||
Мати просту та легку у використанні систему маршрутизації.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://requests.readthedocs.io" class="external-link" target="_blank">Requests</a>
|
||||
|
||||
**FastAPI** насправді не є альтернативою **Requests**. Сфера їх застосування дуже різна.
|
||||
|
||||
Насправді цілком звична річ використовувати Requests *всередині* програми FastAPI.
|
||||
|
||||
Але все ж FastAPI черпав натхнення з Requests.
|
||||
|
||||
**Requests** — це бібліотека для *взаємодії* з API (як клієнт), а **FastAPI** — це бібліотека для *створення* API (як сервер).
|
||||
|
||||
Вони більш-менш знаходяться на протилежних кінцях, доповнюючи одна одну.
|
||||
|
||||
Requests мають дуже простий та інтуїтивно зрозумілий дизайн, дуже простий у використанні, з розумними параметрами за замовчуванням. Але в той же час він дуже потужний і налаштовується.
|
||||
|
||||
Ось чому, як сказано на офіційному сайті:
|
||||
|
||||
> Requests є одним із найбільш завантажуваних пакетів Python усіх часів
|
||||
|
||||
Використовувати його дуже просто. Наприклад, щоб виконати запит `GET`, ви повинні написати:
|
||||
|
||||
```Python
|
||||
response = requests.get("http://example.com/some/url")
|
||||
```
|
||||
|
||||
Відповідна операція *роуту* API FastAPI може виглядати так:
|
||||
|
||||
```Python hl_lines="1"
|
||||
@app.get("/some/url")
|
||||
def read_url():
|
||||
return {"message": "Hello World"}
|
||||
```
|
||||
|
||||
Зверніть увагу на схожість у `requests.get(...)` і `@app.get(...)`.
|
||||
|
||||
/// check | Надихнуло **FastAPI** на
|
||||
|
||||
* Майте простий та інтуїтивно зрозумілий API.
|
||||
* Використовуйте імена (операції) методів HTTP безпосередньо, простим та інтуїтивно зрозумілим способом.
|
||||
* Розумні параметри за замовчуванням, але потужні налаштування.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://swagger.io/" class="external-link" target="_blank">Swagger</a> / <a href="https://github.com/OAI /OpenAPI-Specification/" class="external-link" target="_blank">OpenAPI</a>
|
||||
|
||||
Головною функцією, яку я хотів від Django REST Framework, була автоматична API документація.
|
||||
|
||||
Потім я виявив, що існує стандарт для документування API з використанням JSON (або YAML, розширення JSON) під назвою Swagger.
|
||||
|
||||
І вже був створений веб-інтерфейс користувача для Swagger API. Отже, можливість генерувати документацію Swagger для API дозволить використовувати цей веб-інтерфейс автоматично.
|
||||
|
||||
У якийсь момент Swagger було передано Linux Foundation, щоб перейменувати його на OpenAPI.
|
||||
|
||||
Тому, коли говорять про версію 2.0, прийнято говорити «Swagger», а про версію 3+ «OpenAPI».
|
||||
|
||||
/// check | Надихнуло **FastAPI** на
|
||||
|
||||
Прийняти і використовувати відкритий стандарт для специфікацій API замість спеціальної схеми.
|
||||
|
||||
Інтегрувати інструменти інтерфейсу на основі стандартів:
|
||||
|
||||
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Інтерфейс Swagger</a>
|
||||
* <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>
|
||||
|
||||
Ці два було обрано через те, що вони досить популярні та стабільні, але, виконавши швидкий пошук, ви можете знайти десятки додаткових альтернативних інтерфейсів для OpenAPI (які можна використовувати з **FastAPI**).
|
||||
|
||||
///
|
||||
|
||||
### Фреймворки REST для Flask
|
||||
|
||||
Існує кілька фреймворків Flask REST, але, витративши час і роботу на їх дослідження, я виявив, що багато з них припинено або залишено, з кількома постійними проблемами, які зробили їх непридатними.
|
||||
|
||||
### <a href="https://marshmallow.readthedocs.io/en/stable/" class="external-link" target="_blank">Marshmallow</a>
|
||||
|
||||
Однією з головних функцій, необхідних для систем API, є "<abbr title="також звана marshalling, conversion">серіалізація</abbr>", яка бере дані з коду (Python) і перетворює їх на щось, що можна надіслати через мережу. Наприклад, перетворення об’єкта, що містить дані з бази даних, на об’єкт JSON. Перетворення об’єктів `datetime` на строки тощо.
|
||||
|
||||
Іншою важливою функцією, необхідною для API, є перевірка даних, яка забезпечує дійсність даних за певними параметрами. Наприклад, що деяке поле є `int`, а не деяка випадкова строка. Це особливо корисно для вхідних даних.
|
||||
|
||||
Без системи перевірки даних вам довелося б виконувати всі перевірки вручну, у коді.
|
||||
|
||||
Marshmallow створено для забезпечення цих функцій. Це чудова бібліотека, і я часто нею користувався раніше.
|
||||
|
||||
Але він був створений до того, як існували підказки типу Python. Отже, щоб визначити кожну <abbr title="визначення того, як дані повинні бути сформовані">схему</abbr>, вам потрібно використовувати спеціальні утиліти та класи, надані Marshmallow.
|
||||
|
||||
/// check | Надихнуло **FastAPI** на
|
||||
|
||||
Використовувати код для автоматичного визначення "схем", які надають типи даних і перевірку.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a>
|
||||
|
||||
Іншою важливою функцією, необхідною для API, є <abbr title="читання та перетворення даних Python">аналіз</abbr> даних із вхідних запитів.
|
||||
|
||||
Webargs — це інструмент, створений, щоб забезпечити це поверх кількох фреймворків, включаючи Flask.
|
||||
|
||||
Він використовує Marshmallow в основі для перевірки даних. І створений тими ж розробниками.
|
||||
|
||||
Це чудовий інструмент, і я також часто використовував його, перш ніж створити **FastAPI**.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Webargs був створений тими ж розробниками Marshmallow.
|
||||
|
||||
///
|
||||
|
||||
/// check | Надихнуло **FastAPI** на
|
||||
|
||||
Мати автоматичну перевірку даних вхідного запиту.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://apispec.readthedocs.io/en/stable/" class="external-link" target="_blank">APISpec</a>
|
||||
|
||||
Marshmallow і Webargs забезпечують перевірку, аналіз і серіалізацію як плагіни.
|
||||
|
||||
Але документація досі відсутня. Потім було створено APISpec.
|
||||
|
||||
Це плагін для багатьох фреймворків (також є плагін для Starlette).
|
||||
|
||||
Принцип роботи полягає в тому, що ви пишете визначення схеми, використовуючи формат YAML, у docstring кожної функції, що обробляє маршрут.
|
||||
|
||||
І він генерує схеми OpenAPI.
|
||||
|
||||
Так це працює у Flask, Starlette, Responder тощо.
|
||||
|
||||
Але потім ми знову маємо проблему наявності мікросинтаксису всередині Python строки (великий YAML).
|
||||
|
||||
Редактор тут нічим не може допомогти. І якщо ми змінимо параметри чи схеми Marshmallow і забудемо також змінити цю строку документа YAML, згенерована схема буде застарілою.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
APISpec був створений тими ж розробниками Marshmallow.
|
||||
|
||||
///
|
||||
|
||||
/// check | Надихнуло **FastAPI** на
|
||||
|
||||
Підтримувати відкритий стандарт API, OpenAPI.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://flask-apispec.readthedocs.io/en/latest/" class="external-link" target="_blank">Flask-apispec</a>
|
||||
|
||||
Це плагін Flask, який об’єднує Webargs, Marshmallow і APISpec.
|
||||
|
||||
Він використовує інформацію з Webargs і Marshmallow для автоматичного створення схем OpenAPI за допомогою APISpec.
|
||||
|
||||
Це чудовий інструмент, дуже недооцінений. Він має бути набагато популярнішим, ніж багато плагінів Flask. Це може бути пов’язано з тим, що його документація надто стисла й абстрактна.
|
||||
|
||||
Це вирішило необхідність писати YAML (інший синтаксис) всередині рядків документів Python.
|
||||
|
||||
Ця комбінація Flask, Flask-apispec із Marshmallow і Webargs була моїм улюбленим бекенд-стеком до створення **FastAPI**.
|
||||
|
||||
Їі використання призвело до створення кількох генераторів повного стека Flask. Це основний стек, який я (та кілька зовнішніх команд) використовував досі:
|
||||
|
||||
* <a href="https://github.com/tiangolo/full-stack" class="external-link" target="_blank">https://github.com/tiangolo/full-stack</a>
|
||||
* <a href="https://github.com/tiangolo/full-stack-flask-couchbase" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchbase</a>
|
||||
* <a href="https://github.com/tiangolo/full-stack-flask-couchdb" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchdb</a>
|
||||
|
||||
І ці самі генератори повного стеку були основою [**FastAPI** генераторів проектів](project-generation.md){.internal-link target=_blank}.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Flask-apispec був створений тими ж розробниками Marshmallow.
|
||||
|
||||
///
|
||||
|
||||
/// check | Надихнуло **FastAPI** на
|
||||
|
||||
Створення схеми OpenAPI автоматично з того самого коду, який визначає серіалізацію та перевірку.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://nestjs.com/" class="external-link" target="_blank">NestJS</a> (та <a href="https://angular.io/ " class="external-link" target="_blank">Angular</a>)
|
||||
|
||||
Це навіть не Python, NestJS — це фреймворк NodeJS JavaScript (TypeScript), натхненний Angular.
|
||||
|
||||
Це досягає чогось подібного до того, що можна зробити з Flask-apispec.
|
||||
|
||||
Він має інтегровану систему впровадження залежностей, натхненну Angular two. Він потребує попередньої реєстрації «injectables» (як і всі інші системи впровадження залежностей, які я знаю), тому це збільшує багатослівність та повторення коду.
|
||||
|
||||
Оскільки параметри описані за допомогою типів TypeScript (подібно до підказок типу Python), підтримка редактора досить хороша.
|
||||
|
||||
Але оскільки дані TypeScript не зберігаються після компіляції в JavaScript, вони не можуть покладатися на типи для визначення перевірки, серіалізації та документації одночасно. Через це та деякі дизайнерські рішення, щоб отримати перевірку, серіалізацію та автоматичну генерацію схеми, потрібно додати декоратори в багатьох місцях. Таким чином код стає досить багатослівним.
|
||||
|
||||
Він не дуже добре обробляє вкладені моделі. Отже, якщо тіло JSON у запиті є об’єктом JSON із внутрішніми полями, які, у свою чергу, є вкладеними об’єктами JSON, його неможливо належним чином задокументувати та перевірити.
|
||||
|
||||
/// check | Надихнуло **FastAPI** на
|
||||
|
||||
Використовувати типи Python, щоб мати чудову підтримку редактора.
|
||||
|
||||
Мати потужну систему впровадження залежностей. Знайдіть спосіб звести до мінімуму повторення коду.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://sanic.readthedocs.io/en/latest/" class="external-link" target="_blank">Sanic</a>
|
||||
|
||||
Це був один із перших надзвичайно швидких фреймворків Python на основі `asyncio`. Він був дуже схожий на Flask.
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Він використовував <a href="https://github.com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a> замість стандартного циклу Python `asyncio`. Ось що зробило його таким швидким.
|
||||
|
||||
Це явно надихнуло Uvicorn і Starlette, які зараз швидші за Sanic у відкритих тестах.
|
||||
|
||||
///
|
||||
|
||||
/// check | Надихнуло **FastAPI** на
|
||||
|
||||
Знайти спосіб отримати божевільну продуктивність.
|
||||
|
||||
Ось чому **FastAPI** базується на Starlette, оскільки це найшвидша доступна структура (перевірена тестами сторонніх розробників).
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://falconframework.org/" class="external-link" target="_blank">Falcon</a>
|
||||
|
||||
Falcon — ще один високопродуктивний фреймворк Python, він розроблений як мінімальний і працює як основа інших фреймворків, таких як Hug.
|
||||
|
||||
Він розроблений таким чином, щоб мати функції, які отримують два параметри, один «запит» і один «відповідь». Потім ви «читаєте» частини запиту та «записуєте» частини у відповідь. Через такий дизайн неможливо оголосити параметри запиту та тіла за допомогою стандартних підказок типу Python як параметри функції.
|
||||
|
||||
Таким чином, перевірка даних, серіалізація та документація повинні виконуватися в коді, а не автоматично. Або вони повинні бути реалізовані як фреймворк поверх Falcon, як Hug. Така сама відмінність спостерігається в інших фреймворках, натхненних дизайном Falcon, що мають один об’єкт запиту та один об’єкт відповіді як параметри.
|
||||
|
||||
/// check | Надихнуло **FastAPI** на
|
||||
|
||||
Знайти способи отримати чудову продуктивність.
|
||||
|
||||
Разом із Hug (оскільки Hug базується на Falcon) надихнув **FastAPI** оголосити параметр `response` у функціях.
|
||||
|
||||
Хоча у FastAPI це необов’язково, і використовується в основному для встановлення заголовків, файлів cookie та альтернативних кодів стану.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://moltenframework.com/" class="external-link" target="_blank">Molten</a>
|
||||
|
||||
Я відкрив для себе Molten на перших етапах створення **FastAPI**. І він має досить схожі ідеї:
|
||||
|
||||
* Базується на підказках типу Python.
|
||||
* Перевірка та документація цих типів.
|
||||
* Система впровадження залежностей.
|
||||
|
||||
Він не використовує перевірку даних, серіалізацію та бібліотеку документації сторонніх розробників, як Pydantic, він має свою власну. Таким чином, ці визначення типів даних не можна було б використовувати повторно так легко.
|
||||
|
||||
Це вимагає трохи більш докладних конфігурацій. І оскільки він заснований на WSGI (замість ASGI), він не призначений для використання високопродуктивних інструментів, таких як Uvicorn, Starlette і Sanic.
|
||||
|
||||
Система впровадження залежностей вимагає попередньої реєстрації залежностей, і залежності вирішуються на основі оголошених типів. Отже, неможливо оголосити більше ніж один «компонент», який надає певний тип.
|
||||
|
||||
Маршрути оголошуються в одному місці з використанням функцій, оголошених в інших місцях (замість використання декораторів, які можна розмістити безпосередньо поверх функції, яка обробляє кінцеву точку). Це ближче до того, як це робить Django, ніж до Flask (і Starlette). Він розділяє в коді речі, які відносно тісно пов’язані.
|
||||
|
||||
/// check | Надихнуло **FastAPI** на
|
||||
|
||||
Визначити додаткові перевірки для типів даних, використовуючи значення "за замовчуванням" атрибутів моделі. Це покращує підтримку редактора, а раніше вона була недоступна в Pydantic.
|
||||
|
||||
Це фактично надихнуло оновити частини Pydantic, щоб підтримувати той самий стиль оголошення перевірки (всі ці функції вже доступні в Pydantic).
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://github.com/hugapi/hug" class="external-link" target="_blank">Hug</a>
|
||||
|
||||
Hug був одним із перших фреймворків, який реалізував оголошення типів параметрів API за допомогою підказок типу Python. Це була чудова ідея, яка надихнула інші інструменти зробити те саме.
|
||||
|
||||
Він використовував спеціальні типи у своїх оголошеннях замість стандартних типів Python, але це все одно був величезний крок вперед.
|
||||
|
||||
Це також був один із перших фреймворків, який генерував спеціальну схему, що оголошувала весь API у JSON.
|
||||
|
||||
Він не базувався на таких стандартах, як OpenAPI та JSON Schema. Тому було б непросто інтегрувати його з іншими інструментами, як-от Swagger UI. Але знову ж таки, це була дуже інноваційна ідея.
|
||||
|
||||
Він має цікаву незвичайну функцію: використовуючи ту саму структуру, можна створювати API, а також CLI.
|
||||
|
||||
Оскільки він заснований на попередньому стандарті для синхронних веб-фреймворків Python (WSGI), він не може працювати з Websockets та іншими речами, хоча він також має високу продуктивність.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Hug створив Тімоті Крослі, той самий творець <a href="https://github.com/timothycrosley/isort" class="external-link" target="_blank">`isort`</a>, чудовий інструмент для автоматичного сортування імпорту у файлах Python.
|
||||
|
||||
///
|
||||
|
||||
/// check | Надихнуло **FastAPI** на
|
||||
|
||||
Hug надихнув частину APIStar і був одним із найбільш перспективних інструментів, поряд із APIStar.
|
||||
|
||||
Hug надихнув **FastAPI** на використання підказок типу Python для оголошення параметрів і автоматичного створення схеми, що визначає API.
|
||||
|
||||
Hug надихнув **FastAPI** оголосити параметр `response` у функціях для встановлення заголовків і файлів cookie.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://github.com/encode/apistar" class="external-link" target="_blank">APIStar</a> (<= 0,5)
|
||||
|
||||
Безпосередньо перед тим, як вирішити створити **FastAPI**, я знайшов сервер **APIStar**. Він мав майже все, що я шукав, і мав чудовий дизайн.
|
||||
|
||||
Це була одна з перших реалізацій фреймворку, що використовує підказки типу Python для оголошення параметрів і запитів, яку я коли-небудь бачив (до NestJS і Molten). Я знайшов його більш-менш одночасно з Hug. Але APIStar використовував стандарт OpenAPI.
|
||||
|
||||
Він мав автоматичну перевірку даних, серіалізацію даних і генерацію схеми OpenAPI на основі підказок того самого типу в кількох місцях.
|
||||
|
||||
Визначення схеми тіла не використовували ті самі підказки типу Python, як Pydantic, воно було трохи схоже на Marshmallow, тому підтримка редактора була б не такою хорошою, але все ж APIStar був найкращим доступним варіантом.
|
||||
|
||||
Він мав найкращі показники продуктивності на той час (перевершив лише Starlette).
|
||||
|
||||
Спочатку він не мав автоматичного веб-інтерфейсу документації API, але я знав, що можу додати до нього інтерфейс користувача Swagger.
|
||||
|
||||
Він мав систему введення залежностей. Він вимагав попередньої реєстрації компонентів, як і інші інструменти, розглянуті вище. Але все одно це була чудова функція.
|
||||
|
||||
Я ніколи не міг використовувати його в повноцінному проекті, оскільки він не мав інтеграції безпеки, тому я не міг замінити всі функції, які мав, генераторами повного стеку на основі Flask-apispec. У моїх невиконаних проектах я мав створити запит на вилучення, додавши цю функцію.
|
||||
|
||||
Але потім фокус проекту змінився.
|
||||
|
||||
Це вже не був веб-фреймворк API, оскільки творцю потрібно було зосередитися на Starlette.
|
||||
|
||||
Тепер APIStar — це набір інструментів для перевірки специфікацій OpenAPI, а не веб-фреймворк.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
APIStar створив Том Крісті. Той самий хлопець, який створив:
|
||||
|
||||
* Django REST Framework
|
||||
* Starlette (на якому базується **FastAPI**)
|
||||
* Uvicorn (використовується Starlette і **FastAPI**)
|
||||
|
||||
///
|
||||
|
||||
/// check | Надихнуло **FastAPI** на
|
||||
|
||||
Існувати.
|
||||
|
||||
Ідею оголошення кількох речей (перевірки даних, серіалізації та документації) за допомогою тих самих типів Python, які в той же час забезпечували чудову підтримку редактора, я вважав геніальною ідеєю.
|
||||
|
||||
І після тривалого пошуку подібної структури та тестування багатьох різних альтернатив, APIStar став найкращим доступним варіантом.
|
||||
|
||||
Потім APIStar перестав існувати як сервер, і було створено Starlette, який став новою кращою основою для такої системи. Це стало останнім джерелом натхнення для створення **FastAPI**. Я вважаю **FastAPI** «духовним спадкоємцем» APIStar, удосконалюючи та розширюючи функції, систему введення тексту та інші частини на основі досвіду, отриманого від усіх цих попередніх інструментів.
|
||||
|
||||
///
|
||||
|
||||
## Використовується **FastAPI**
|
||||
|
||||
### <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>
|
||||
|
||||
Pydantic — це бібліотека для визначення перевірки даних, серіалізації та документації (за допомогою схеми JSON) на основі підказок типу Python.
|
||||
|
||||
Це робить його надзвичайно інтуїтивним.
|
||||
|
||||
Його можна порівняти з Marshmallow. Хоча він швидший за Marshmallow у тестах. Оскільки він базується на тих самих підказках типу Python, підтримка редактора чудова.
|
||||
|
||||
/// check | **FastAPI** використовує його для
|
||||
|
||||
Виконання перевірки всіх даних, серіалізації даних і автоматичної документацію моделі (на основі схеми JSON).
|
||||
|
||||
Потім **FastAPI** бере ці дані схеми JSON і розміщує їх у OpenAPI, окремо від усіх інших речей, які він робить.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a>
|
||||
|
||||
Starlette — це легкий фреймворк/набір інструментів <abbr title="The new standard for build asynchronous Python web">ASGI</abbr>, який ідеально підходить для створення високопродуктивних asyncio сервісів.
|
||||
|
||||
Він дуже простий та інтуїтивно зрозумілий. Його розроблено таким чином, щоб його можна було легко розширювати та мати модульні компоненти.
|
||||
|
||||
Він має:
|
||||
|
||||
* Серйозно вражаючу продуктивність.
|
||||
* Підтримку WebSocket.
|
||||
* Фонові завдання в процесі.
|
||||
* Події запуску та завершення роботи.
|
||||
* Тестового клієнта, побудований на HTTPX.
|
||||
* CORS, GZip, статичні файли, потокові відповіді.
|
||||
* Підтримку сеансів і файлів cookie.
|
||||
* 100% покриття тестом.
|
||||
* 100% анотовану кодову базу.
|
||||
* Кілька жорстких залежностей.
|
||||
|
||||
Starlette наразі є найшвидшим фреймворком Python із перевірених. Перевершує лише Uvicorn, який є не фреймворком, а сервером.
|
||||
|
||||
Starlette надає всі основні функції веб-мікрофреймворку.
|
||||
|
||||
Але він не забезпечує автоматичної перевірки даних, серіалізації чи документації.
|
||||
|
||||
Це одна з головних речей, які **FastAPI** додає зверху, все на основі підказок типу Python (з використанням Pydantic). Це, а також система впровадження залежностей, утиліти безпеки, створення схеми OpenAPI тощо.
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
ASGI — це новий «стандарт», який розробляється членами основної команди Django. Це ще не «стандарт Python» (PEP), хоча вони в процесі цього.
|
||||
|
||||
Тим не менш, він уже використовується як «стандарт» кількома інструментами. Це значно покращує сумісність, оскільки ви можете переключити Uvicorn на будь-який інший сервер ASGI (наприклад, Daphne або Hypercorn), або ви можете додати інструменти, сумісні з ASGI, як-от `python-socketio`.
|
||||
|
||||
///
|
||||
|
||||
/// check | **FastAPI** використовує його для
|
||||
|
||||
Керування всіма основними веб-частинами. Додавання функцій зверху.
|
||||
|
||||
Сам клас `FastAPI` безпосередньо успадковує клас `Starlette`.
|
||||
|
||||
Отже, усе, що ви можете робити зі Starlette, ви можете робити це безпосередньо за допомогою **FastAPI**, оскільки це, по суті, Starlette на стероїдах.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a>
|
||||
|
||||
Uvicorn — це блискавичний сервер ASGI, побудований на uvloop і httptools.
|
||||
|
||||
Це не веб-фреймворк, а сервер. Наприклад, він не надає інструментів для маршрутизації. Це те, що фреймворк на кшталт Starlette (або **FastAPI**) забезпечить поверх нього.
|
||||
|
||||
Це рекомендований сервер для Starlette і **FastAPI**.
|
||||
|
||||
/// check | **FastAPI** рекомендує це як
|
||||
|
||||
Основний веб-сервер для запуску програм **FastAPI**.
|
||||
|
||||
Ви можете поєднати його з Gunicorn, щоб мати асинхронний багатопроцесний сервер.
|
||||
|
||||
Додаткову інформацію див. у розділі [Розгортання](deployment/index.md){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
## Орієнтири та швидкість
|
||||
|
||||
Щоб зрозуміти, порівняти та побачити різницю між Uvicorn, Starlette і FastAPI, перегляньте розділ про [Бенчмарки](benchmarks.md){.internal-link target=_blank}.
|
||||
@@ -0,0 +1,83 @@
|
||||
# FastAPI CLI
|
||||
|
||||
**FastAPI CLI** це програма командного рядка, яку Ви можете використовувати, щоб обслуговувати Ваш додаток FastAPI, керувати Вашими FastApi проектами, тощо.
|
||||
|
||||
Коли Ви встановлюєте FastApi (тобто виконуєте `pip install "fastapi[standard]"`), Ви також встановлюєте пакунок `fastapi-cli`, цей пакунок надає команду `fastapi` в терміналі.
|
||||
|
||||
Для запуску Вашого FastAPI проекту для розробки, Ви можете скористатись командою `fastapi dev`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:single">main.py</u>
|
||||
<font color="#3465A4">INFO </font> Using path <font color="#3465A4">main.py</font>
|
||||
<font color="#3465A4">INFO </font> Resolved absolute path <font color="#75507B">/home/user/code/awesomeapp/</font><font color="#AD7FA8">main.py</font>
|
||||
<font color="#3465A4">INFO </font> Searching for package file structure from directories with <font color="#3465A4">__init__.py</font> files
|
||||
<font color="#3465A4">INFO </font> Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
|
||||
|
||||
╭─ <font color="#8AE234"><b>Python module file</b></font> ─╮
|
||||
│ │
|
||||
│ 🐍 main.py │
|
||||
│ │
|
||||
╰──────────────────────╯
|
||||
|
||||
<font color="#3465A4">INFO </font> Importing module <font color="#4E9A06">main</font>
|
||||
<font color="#3465A4">INFO </font> Found importable FastAPI app
|
||||
|
||||
╭─ <font color="#8AE234"><b>Importable FastAPI app</b></font> ─╮
|
||||
│ │
|
||||
│ <span style="background-color:#272822"><font color="#FF4689">from</font></span><span style="background-color:#272822"><font color="#F8F8F2"> main </font></span><span style="background-color:#272822"><font color="#FF4689">import</font></span><span style="background-color:#272822"><font color="#F8F8F2"> app</font></span><span style="background-color:#272822"> </span> │
|
||||
│ │
|
||||
╰──────────────────────────╯
|
||||
|
||||
<font color="#3465A4">INFO </font> Using import string <font color="#8AE234"><b>main:app</b></font>
|
||||
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">╭────────── FastAPI CLI - Development mode ───────────╮</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ Serving at: http://127.0.0.1:8000 │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ API docs: http://127.0.0.1:8000/docs │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ Running in development mode, for production use: │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ </font></span><span style="background-color:#C4A000"><font color="#555753"><b>fastapi run</b></font></span><span style="background-color:#C4A000"><font color="#2E3436"> │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">╰─────────────────────────────────────────────────────╯</font></span>
|
||||
|
||||
<font color="#4E9A06">INFO</font>: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
|
||||
<font color="#4E9A06">INFO</font>: Uvicorn running on <b>http://127.0.0.1:8000</b> (Press CTRL+C to quit)
|
||||
<font color="#4E9A06">INFO</font>: Started reloader process [<font color="#34E2E2"><b>2265862</b></font>] using <font color="#34E2E2"><b>WatchFiles</b></font>
|
||||
<font color="#4E9A06">INFO</font>: Started server process [<font color="#06989A">2265873</font>]
|
||||
<font color="#4E9A06">INFO</font>: Waiting for application startup.
|
||||
<font color="#4E9A06">INFO</font>: Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Програма командного рядка `fastapi` це **FastAPI CLI**.
|
||||
|
||||
FastAPI CLI приймає шлях до Вашої Python програми (напр. `main.py`) і автоматично виявляє екземпляр `FastAPI` (зазвичай названий `app`), обирає коректний процес імпорту, а потім обслуговує його.
|
||||
|
||||
Натомість, для запуску у продакшн використовуйте `fastapi run`. 🚀
|
||||
|
||||
Всередині **FastAPI CLI** використовує <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a>, високопродуктивний, production-ready, ASGI cервер. 😎
|
||||
|
||||
## `fastapi dev`
|
||||
|
||||
Використання `fastapi dev` ініціює режим розробки.
|
||||
|
||||
За замовчуванням, **автоматичне перезавантаження** увімкнене, автоматично перезавантажуючи сервер кожного разу, коли Ви змінюєте Ваш код. Це ресурсо-затратно, та може бути менш стабільним, ніж коли воно вимкнене. Ви повинні використовувати його тільки під час розробки. Воно також слухає IP-адресу `127.0.0.1`, що є IP Вашого девайсу для самостійної комунікації з самим собою (`localhost`).
|
||||
|
||||
## `fastapi run`
|
||||
|
||||
Виконання `fastapi run` запустить FastAPI у продакшн-режимі за замовчуванням.
|
||||
|
||||
За замовчуванням, **автоматичне перезавантаження** вимкнене. Воно також прослуховує IP-адресу `0.0.0.0`, що означає всі доступні IP адреси, тим самим даючи змогу будь-кому комунікувати з девайсом. Так Ви зазвичай будете запускати його у продакшн, наприклад у контейнері.
|
||||
|
||||
В більшості випадків Ви можете (і маєте) мати "termination proxy", який обробляє HTTPS для Вас, це залежить від способу розгортання вашого додатку, Ваш провайдер може зробити це для Вас, або Вам потрібно налаштувати його самостійно.
|
||||
|
||||
/// tip
|
||||
|
||||
Ви можете дізнатись більше про це у [документації про розгортування](deployment/index.md){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
@@ -0,0 +1,189 @@
|
||||
# Функціональні можливості
|
||||
|
||||
## Функціональні можливості FastAPI
|
||||
|
||||
**FastAPI** надає вам такі можливості:
|
||||
|
||||
### Використання відкритих стандартів
|
||||
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> для створення API, включаючи оголошення <abbr title="також відомі як: endpoints, маршрути">шляхів</abbr>, <abbr title="також відомі як HTTP-методи, наприклад, POST, GET, PUT, DELETE">операцій</abbr>, параметрів, тіл запитів, безпеки тощо.
|
||||
* Автоматична документація моделей даних за допомогою <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (оскільки OpenAPI базується саме на JSON Schema).
|
||||
* Розроблено на основі цих стандартів після ретельного аналізу, а не як додатковий рівень поверх основної архітектури.
|
||||
* Це також дає змогу автоматично **генерувати код клієнта** багатьма мовами.
|
||||
|
||||
### Автоматична генерація документації
|
||||
|
||||
Інтерактивна документація API та вебінтерфейс для його дослідження. Оскільки фреймворк базується на OpenAPI, є кілька варіантів, два з яких включені за замовчуванням.
|
||||
|
||||
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank"><strong>Swagger UI</strong></a> — дозволяє інтерактивно переглядати API, викликати та тестувати його прямо у браузері.
|
||||
|
||||

|
||||
|
||||
* Альтернативна документація API за допомогою <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank"><strong>ReDoc</strong></a>.
|
||||
|
||||

|
||||
|
||||
### Тільки сучасний Python
|
||||
|
||||
FastAPI використовує стандартні **типи Python** (завдяки Pydantic). Вам не потрібно вивчати новий синтаксис — лише стандартний сучасний Python.
|
||||
|
||||
Якщо вам потрібне коротке нагадування про використання типів у Python (навіть якщо ви не використовуєте FastAPI), перегляньте короткий підручник: [Вступ до типів Python](python-types.md){.internal-link target=_blank}.
|
||||
|
||||
Ось приклад стандартного Python-коду з типами:
|
||||
|
||||
```Python
|
||||
from datetime import date
|
||||
from pydantic import BaseModel
|
||||
|
||||
# Оголошення змінної як str
|
||||
# з підтримкою автодоповнення у редакторі
|
||||
def main(user_id: str):
|
||||
return user_id
|
||||
|
||||
# Модель Pydantic
|
||||
class User(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
joined: date
|
||||
```
|
||||
|
||||
Приклад використання цієї моделі:
|
||||
|
||||
```Python
|
||||
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
|
||||
|
||||
second_user_data = {
|
||||
"id": 4,
|
||||
"name": "Mary",
|
||||
"joined": "2018-11-30",
|
||||
}
|
||||
|
||||
my_second_user: User = User(**second_user_data)
|
||||
```
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
`**second_user_data` означає:
|
||||
|
||||
Передати ключі та значення словника `second_user_data` як аргументи у вигляді "ключ-значення", еквівалентно `User(id=4, name="Mary", joined="2018-11-30")`.
|
||||
|
||||
///
|
||||
|
||||
### Підтримка редакторів (IDE)
|
||||
|
||||
Фреймворк спроєктований так, щоб бути легким і інтуїтивно зрозумілим. Усі рішення тестувалися у різних редакторах ще до початку розробки, щоб забезпечити найкращий досвід програмування.
|
||||
|
||||
За результатами опитувань розробників Python <a href="https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features" class="external-link" target="_blank">однією з найпопулярніших функцій є "автодоповнення"</a>.
|
||||
|
||||
**FastAPI** повністю підтримує автодоповнення у всіх місцях, тому вам рідко доведеться повертатися до документації.
|
||||
|
||||
Приклад автодоповнення у редакторах:
|
||||
|
||||
* у <a href="https://code.visualstudio.com/" class="external-link" target="_blank">Visual Studio Code</a>:
|
||||
|
||||

|
||||
|
||||
* у <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>:
|
||||
|
||||

|
||||
|
||||
### Короткий код
|
||||
FastAPI має розумні налаштування **за замовчуванням**, але всі параметри можна налаштовувати відповідно до ваших потреб. Однак за замовчуванням все "просто працює".
|
||||
|
||||
### Валідація
|
||||
* Підтримка валідації для більшості (або всіх?) **типів даних Python**, зокрема:
|
||||
* JSON-об'єктів (`dict`).
|
||||
* JSON-списків (`list`) з визначенням типів елементів.
|
||||
* Рядків (`str`) із мінімальною та максимальною довжиною.
|
||||
* Чисел (`int`, `float`) з обмеженнями мінімальних та максимальних значень тощо.
|
||||
|
||||
* Валідація складніших типів, таких як:
|
||||
* URL.
|
||||
* Email.
|
||||
* UUID.
|
||||
* ...та інші.
|
||||
|
||||
Уся валідація виконується через надійний та перевірений **Pydantic**.
|
||||
|
||||
### Безпека та автентифікація
|
||||
|
||||
**FastAPI** підтримує вбудовану автентифікацію та авторизацію, без прив’язки до конкретних баз даних чи моделей даних.
|
||||
|
||||
Підтримуються всі схеми безпеки OpenAPI, включаючи:
|
||||
|
||||
* HTTP Basic.
|
||||
* **OAuth2** (також із підтримкою **JWT-токенів**). Див. підручник: [OAuth2 із JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}.
|
||||
* Ключі API в:
|
||||
* Заголовках.
|
||||
* Параметрах запиту.
|
||||
* Cookies тощо.
|
||||
|
||||
А також усі можливості безпеки від Starlette (зокрема **сесійні cookies**).
|
||||
|
||||
Усі вони створені як багаторазові інструменти та компоненти, які легко інтегруються з вашими системами, сховищами даних, реляційними та NoSQL базами даних тощо.
|
||||
|
||||
### Впровадження залежностей
|
||||
|
||||
**FastAPI** містить надзвичайно просту у використанні, але потужну систему впровадження залежностей.
|
||||
|
||||
* Залежності можуть мати власні залежності, утворюючи ієрархію або **"граф залежностей"**.
|
||||
* Усі залежності автоматично керуються фреймворком.
|
||||
* Усі залежності можуть отримувати дані з запитів і розширювати **обмеження операції за шляхом** та автоматичну документацію.
|
||||
* **Автоматична валідація** навіть для параметрів *операцій шляху*, визначених у залежностях.
|
||||
* Підтримка складних систем автентифікації користувачів, **з'єднань із базами даних** тощо.
|
||||
* **Жодних обмежень** щодо використання баз даних, фронтендів тощо, але водночас проста інтеграція з усіма ними.
|
||||
|
||||
### Немає обмежень на "плагіни"
|
||||
|
||||
Або іншими словами, вони не потрібні – просто імпортуйте та використовуйте необхідний код.
|
||||
|
||||
Будь-яка інтеграція спроєктована настільки просто (з використанням залежностей), що ви можете створити "плагін" для свого застосунку всього у 2 рядках коду, використовуючи ту саму структуру та синтаксис, що й для ваших *операцій шляху*.
|
||||
|
||||
### Протестовано
|
||||
|
||||
* 100% <abbr title="Обсяг коду, що автоматично тестується">покриття тестами</abbr>.
|
||||
* 100% <abbr title="Анотації типів у Python, завдяки яким ваш редактор і зовнішні інструменти можуть надавати кращу підтримку">анотована типами</abbr> кодова база.
|
||||
* Використовується у робочих середовищах.
|
||||
|
||||
## Можливості Starlette
|
||||
|
||||
**FastAPI** повністю сумісний із (та побудований на основі) <a href="https://www.starlette.dev/" class="external-link" target="_blank"><strong>Starlette</strong></a>. Тому будь-який додатковий код Starlette, який ви маєте, також працюватиме.
|
||||
|
||||
**FastAPI** фактично є підкласом **Starlette**. Тому, якщо ви вже знайомі зі Starlette або використовуєте його, більшість функціональності працюватиме так само.
|
||||
|
||||
З **FastAPI** ви отримуєте всі можливості **Starlette** (адже FastAPI — це, по суті, Starlette на стероїдах):
|
||||
|
||||
* Разюча продуктивність. Це <a href="https://github.com/encode/starlette#performance" class="external-link" target="_blank">один із найшвидших фреймворків на Python</a>, на рівні з **NodeJS** і **Go**.
|
||||
* Підтримка **WebSocket**.
|
||||
* Фонові задачі у процесі.
|
||||
* Події запуску та завершення роботи.
|
||||
* Клієнт для тестування, побудований на HTTPX.
|
||||
* Підтримка **CORS**, **GZip**, статичних файлів, потокових відповідей.
|
||||
* Підтримка **сесій** і **cookie**.
|
||||
* 100% покриття тестами.
|
||||
* 100% анотована типами кодова база.
|
||||
|
||||
## Можливості Pydantic
|
||||
|
||||
**FastAPI** повністю сумісний із (та побудований на основі) <a href="https://docs.pydantic.dev/" class="external-link" target="_blank"><strong>Pydantic</strong></a>. Тому будь-який додатковий код Pydantic, який ви маєте, також працюватиме.
|
||||
|
||||
Включаючи зовнішні бібліотеки, побудовані також на Pydantic, такі як <abbr title="Object-Relational Mapper">ORM</abbr>, <abbr title="Object-Document Mapper">ODM</abbr> для баз даних.
|
||||
|
||||
Це також означає, що в багатьох випадках ви можете передати той самий об'єкт, який отримуєте з запиту, **безпосередньо в базу даних**, оскільки все автоматично перевіряється.
|
||||
|
||||
Те ж саме відбувається й у зворотному напрямку — у багатьох випадках ви можете просто передати об'єкт, який отримуєте з бази даних, **безпосередньо клієнту**.
|
||||
|
||||
З **FastAPI** ви отримуєте всі можливості **Pydantic** (адже FastAPI базується на Pydantic для обробки всіх даних):
|
||||
|
||||
* **Ніякої плутанини** :
|
||||
* Не потрібно вчити нову мову для визначення схем.
|
||||
* Якщо ви знаєте типи Python, ви знаєте, як використовувати Pydantic.
|
||||
* Легко працює з вашим **<abbr title="Інтегроване середовище розробки, схоже на редактор коду">IDE</abbr>/<abbr title="Програма, яка перевіряє помилки в коді">лінтером</abbr>/мозком**:
|
||||
* Оскільки структури даних Pydantic є просто екземплярами класів, які ви визначаєте; автодоповнення, лінтинг, mypy і ваша інтуїція повинні добре працювати з вашими перевіреними даними.
|
||||
* Валідація **складних структур**:
|
||||
* Використання ієрархічних моделей Pydantic. Python `typing`, `List` і `Dict` тощо.
|
||||
* Валідатори дозволяють чітко і просто визначати, перевіряти й документувати складні схеми даних у вигляді JSON-схеми.
|
||||
* Ви можете мати глибоко **вкладені JSON об'єкти** та перевірити та анотувати їх всі.
|
||||
* **Розширюваність**:
|
||||
* Pydantic дозволяє визначати користувацькі типи даних або розширювати валідацію методами в моделі декоратором `validator`.
|
||||
* 100% покриття тестами.
|
||||
@@ -0,0 +1,463 @@
|
||||
<p align="center">
|
||||
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<em>Готовий до продакшину, високопродуктивний, простий у вивченні та швидкий для написання коду фреймворк</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
**Документація**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
|
||||
|
||||
**Програмний код**: <a href="https://github.com/fastapi/fastapi" target="_blank">https://github.com/fastapi/fastapi</a>
|
||||
|
||||
---
|
||||
|
||||
FastAPI - це сучасний, швидкий (високопродуктивний), вебфреймворк для створення API за допомогою Python,в основі якого лежить стандартна анотація типів Python.
|
||||
|
||||
Ключові особливості:
|
||||
|
||||
* **Швидкий**: Дуже висока продуктивність, на рівні з **NodeJS** та **Go** (завдяки Starlette та Pydantic). [Один із найшвидших фреймворків](#performance).
|
||||
|
||||
* **Швидке написання коду**: Пришвидшує розробку функціоналу приблизно на 200%-300%. *
|
||||
* **Менше помилок**: Зменшить кількість помилок спричинених людиною (розробником) на 40%. *
|
||||
* **Інтуїтивний**: Чудова підтримка редакторами коду. <abbr title="Також відоме як auto-complete, autocompletion, IntelliSense.">Доповнення</abbr> всюди. Зменште час на налагодження.
|
||||
* **Простий**: Спроектований, для легкого використання та навчання. Знадобиться менше часу на читання документації.
|
||||
* **Короткий**: Зведе до мінімуму дублювання коду. Кожен оголошений параметр може виконувати кілька функцій.
|
||||
* **Надійний**: Ви матимете стабільний код готовий до продакшину з автоматичною інтерактивною документацією.
|
||||
* **Стандартизований**: Оснований та повністю сумісний з відкритими стандартами для API: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (попередньо відомий як Swagger) та <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
|
||||
|
||||
<small>* оцінка на основі тестів внутрішньої команди розробників, створення продуктових застосунків.</small>
|
||||
|
||||
## Спонсори
|
||||
|
||||
<!-- sponsors -->
|
||||
|
||||
{% if sponsors %}
|
||||
{% for sponsor in sponsors.gold -%}
|
||||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
|
||||
{% endfor -%}
|
||||
{%- for sponsor in sponsors.silver -%}
|
||||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<!-- /sponsors -->
|
||||
|
||||
<a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">Other sponsors</a>
|
||||
|
||||
## Враження
|
||||
|
||||
"_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/fastapi/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_I’m over the moon excited about **FastAPI**. It’s so fun!_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://x.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that._"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="https://github.com/hugapi/hug" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]_"
|
||||
|
||||
"_We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <a href="https://spacy.io" target="_blank">spaCy</a> creators</strong> <a href="https://x.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://x.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
## **Typer**, FastAPI CLI
|
||||
|
||||
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
|
||||
|
||||
Створюючи <abbr title="Command Line Interface">CLI</abbr> застосунок для використання в терміналі, замість веб-API зверніть увагу на <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
|
||||
|
||||
**Typer** є молодшим братом FastAPI. І це **FastAPI для CLI**. ⌨️ 🚀
|
||||
|
||||
## Вимоги
|
||||
|
||||
FastAPI стоїть на плечах гігантів:
|
||||
|
||||
* <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> для web частини.
|
||||
* <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> для частини даних.
|
||||
|
||||
## Вставновлення
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install fastapi
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Вам також знадобиться сервер ASGI для продакшину, наприклад <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a> або <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install uvicorn[standard]
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Приклад
|
||||
|
||||
### Створіть
|
||||
|
||||
* Створіть файл `main.py` з:
|
||||
|
||||
```Python
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
<details markdown="1">
|
||||
<summary>Або використайте <code>async def</code>...</summary>
|
||||
|
||||
Якщо ваш код використовує `async` / `await`, скористайтеся `async def`:
|
||||
|
||||
```Python hl_lines="9 14"
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
**Примітка**:
|
||||
|
||||
Стикнувшись з проблемами, не зайвим буде ознайомитися з розділом _"In a hurry?"_ про <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` та `await` у документації</a>.
|
||||
|
||||
</details>
|
||||
|
||||
### Запустіть
|
||||
|
||||
Запустіть server з:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
|
||||
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
INFO: Started reloader process [28720]
|
||||
INFO: Started server process [28722]
|
||||
INFO: Waiting for application startup.
|
||||
INFO: Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
<details markdown="1">
|
||||
<summary>Про команди <code>uvicorn main:app --reload</code>...</summary>
|
||||
|
||||
Команда `uvicorn main:app` посилається на:
|
||||
|
||||
* `main`: файл `main.py` ("Модуль" Python).
|
||||
* `app`: об’єкт створений усередині `main.py` рядком `app = FastAPI()`.
|
||||
* `--reload`: перезапускає сервер після зміни коду. Використовуйте виключно для розробки.
|
||||
|
||||
</details>
|
||||
|
||||
### Перевірте
|
||||
|
||||
Відкрийте браузер та введіть адресу <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
|
||||
|
||||
Ви побачите у відповідь подібний JSON:
|
||||
|
||||
```JSON
|
||||
{"item_id": 5, "q": "somequery"}
|
||||
```
|
||||
|
||||
Ви вже створили API, який:
|
||||
|
||||
* Отримує HTTP запити за _шляхами_ `/` та `/items/{item_id}`.
|
||||
* Обидва _шляхи_ приймають `GET` <em>операції</em> (також відомі як HTTP _методи_).
|
||||
* _Шлях_ `/items/{item_id}` містить _параметр шляху_ `item_id` який має бути типу `int`.
|
||||
* _Шлях_ `/items/{item_id}` містить необовʼязковий `str` _параметр запиту_ `q`.
|
||||
|
||||
### Інтерактивні документації API
|
||||
|
||||
Перейдемо сюди <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
Ви побачите автоматичну інтерактивну API документацію (створену завдяки <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
|
||||
|
||||

|
||||
|
||||
### Альтернативні документації API
|
||||
|
||||
Тепер перейдемо сюди <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
|
||||
Ви побачите альтернативну автоматичну документацію (створену завдяки <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
|
||||
|
||||

|
||||
|
||||
## Приклад оновлення
|
||||
|
||||
Тепер модифікуйте файл `main.py`, щоб отримати вміст запиту `PUT`.
|
||||
|
||||
Оголошуйте вміст запиту за допомогою стандартних типів Python завдяки Pydantic.
|
||||
|
||||
```Python hl_lines="4 9-12 25-27"
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
price: float
|
||||
is_offer: Union[bool, None] = None
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
def update_item(item_id: int, item: Item):
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
Сервер повинен автоматично перезавантажуватися (тому що Ви додали `--reload` до `uvicorn` команди вище).
|
||||
|
||||
### Оновлення інтерактивної API документації
|
||||
|
||||
Тепер перейдемо сюди <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
* Інтерактивна документація API буде автоматично оновлена, включаючи новий вміст:
|
||||
|
||||

|
||||
|
||||
* Натисніть кнопку "Try it out", це дозволить вам заповнити параметри та безпосередньо взаємодіяти з API:
|
||||
|
||||

|
||||
|
||||
* Потім натисніть кнопку "Execute", інтерфейс користувача зв'яжеться з вашим API, надішле параметри, у відповідь отримає результати та покаже їх на екрані:
|
||||
|
||||

|
||||
|
||||
### Оновлення альтернативної API документації
|
||||
|
||||
Зараз перейдемо <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
|
||||
* Альтернативна документація також показуватиме новий параметр і вміст запиту:
|
||||
|
||||

|
||||
|
||||
### Підсумки
|
||||
|
||||
Таким чином, Ви **один раз** оголошуєте типи параметрів, тіла тощо, як параметри функції.
|
||||
|
||||
Ви робите це за допомогою стандартних сучасних типів Python.
|
||||
|
||||
Вам не потрібно вивчати новий синтаксис, методи чи класи конкретної бібліотеки тощо.
|
||||
|
||||
Використовуючи стандартний **Python**.
|
||||
|
||||
Наприклад, для `int`:
|
||||
|
||||
```Python
|
||||
item_id: int
|
||||
```
|
||||
|
||||
або для більш складної моделі `Item`:
|
||||
|
||||
```Python
|
||||
item: Item
|
||||
```
|
||||
|
||||
...і з цим єдиним оголошенням Ви отримуєте:
|
||||
|
||||
* Підтримку редактора, включаючи:
|
||||
* Варіанти заповнення.
|
||||
* Перевірку типів.
|
||||
* Перевірку даних:
|
||||
* Автоматичні та зрозумілі помилки, у разі некоректних даних.
|
||||
* Перевірка навіть для JSON з високим рівнем вкладеності.
|
||||
* <abbr title="також відомий як: serialization, parsing, marshalling">Перетворення</abbr> вхідних даних: з мережі до даних і типів Python. Читання з:
|
||||
* JSON.
|
||||
* Параметрів шляху.
|
||||
* Параметрів запиту.
|
||||
* Cookies.
|
||||
* Headers.
|
||||
* Forms.
|
||||
* Файлів.
|
||||
* <abbr title="також відомий як: serialization, parsing, marshalling">Перетворення</abbr> вихідних даних: з типів і даних Python до мережевих даних (як JSON):
|
||||
* Конвертація Python типів (`str`, `int`, `float`, `bool`, `list`, тощо).
|
||||
* `datetime` об'єкти.
|
||||
* `UUID` об'єкти.
|
||||
* Моделі бази даних.
|
||||
* ...та багато іншого.
|
||||
* Автоматичну інтерактивну документацію API, включаючи 2 альтернативні інтерфейси користувача:
|
||||
* Swagger UI.
|
||||
* ReDoc.
|
||||
|
||||
---
|
||||
|
||||
Повертаючись до попереднього прикладу коду, **FastAPI**:
|
||||
|
||||
* Підтвердить наявність `item_id` у шляху для запитів `GET` та `PUT`.
|
||||
* Підтвердить, що `item_id` має тип `int` для запитів `GET` and `PUT`.
|
||||
* Якщо це не так, клієнт побачить корисну, зрозумілу помилку.
|
||||
* Перевірить, чи є необов'язковий параметр запиту з назвою `q` (а саме `http://127.0.0.1:8000/items/foo?q=somequery`) для запитів `GET`.
|
||||
* Оскільки параметр `q` оголошено як `= None`, він необов'язковий.
|
||||
* За відсутності `None` він був би обов'язковим (як і вміст у випадку з `PUT`).
|
||||
* Для запитів `PUT` із `/items/{item_id}`, читає вміст як JSON:
|
||||
* Перевірить, чи має обов'язковий атрибут `name` тип `str`.
|
||||
* Перевірить, чи має обов'язковий атрибут `price` тип `float`.
|
||||
* Перевірить, чи існує необов'язковий атрибут `is_offer` та чи має він тип `bool`.
|
||||
* Усе це також працюватиме для глибоко вкладених об'єктів JSON.
|
||||
* Автоматично конвертує із та в JSON.
|
||||
* Документує все за допомогою OpenAPI, який може бути використано в:
|
||||
* Інтерактивних системах документації.
|
||||
* Системах автоматичної генерації клієнтського коду для багатьох мов.
|
||||
* Надає безпосередньо 2 вебінтерфейси інтерактивної документації.
|
||||
|
||||
---
|
||||
|
||||
Ми лише трішки доторкнулися до коду, але Ви вже маєте уявлення про те, як все працює.
|
||||
|
||||
Спробуйте змінити рядок:
|
||||
|
||||
```Python
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
...із:
|
||||
|
||||
```Python
|
||||
... "item_name": item.name ...
|
||||
```
|
||||
|
||||
...на:
|
||||
|
||||
```Python
|
||||
... "item_price": item.price ...
|
||||
```
|
||||
|
||||
...і побачите, як ваш редактор автоматично заповнюватиме атрибути та знатиме їхні типи:
|
||||
|
||||

|
||||
|
||||
Для більш повного ознайомлення з додатковими функціями, перегляньте <a href="https://fastapi.tiangolo.com/tutorial/">Туторіал - Посібник Користувача</a>.
|
||||
|
||||
**Spoiler alert**: туторіал - посібник користувача містить:
|
||||
|
||||
* Оголошення **параметрів** з інших місць як: **headers**, **cookies**, **form fields** та **files**.
|
||||
* Як встановити **перевірку обмежень** як `maximum_length` або `regex`.
|
||||
* Дуже потужна і проста у використанні система **<abbr title="також відома як: components, resources, providers, services, injectables">Ін'єкція Залежностей</abbr>**.
|
||||
* Безпека та автентифікація, включаючи підтримку **OAuth2** з **JWT tokens** та **HTTP Basic** автентифікацію.
|
||||
* Досконаліші (але однаково прості) техніки для оголошення **глибоко вкладених моделей JSON** (завдяки Pydantic).
|
||||
* Багато додаткових функцій (завдяки Starlette) як-от:
|
||||
* **WebSockets**
|
||||
* надзвичайно прості тести на основі HTTPX та `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ...та більше.
|
||||
|
||||
## Продуктивність
|
||||
|
||||
Незалежні тести TechEmpower показують що застосунки **FastAPI**, які працюють під керуванням Uvicorn <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">є одними з найшвидших серед доступних фреймворків в Python</a>, поступаючись лише Starlette та Uvicorn (які внутрішньо використовуються в FastAPI). (*)
|
||||
|
||||
Щоб дізнатися більше про це, перегляньте розділ <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
|
||||
|
||||
## Необов'язкові залежності
|
||||
|
||||
Pydantic використовує:
|
||||
|
||||
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email-validator</code></a> - для валідації електронної пошти.
|
||||
* <a href="https://docs.pydantic.dev/latest/usage/pydantic_settings/" target="_blank"><code>pydantic-settings</code></a> - для управління налаштуваннями.
|
||||
* <a href="https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/" target="_blank"><code>pydantic-extra-types</code></a> - для додаткових типів, що можуть бути використані з Pydantic.
|
||||
|
||||
|
||||
Starlette використовує:
|
||||
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Необхідно, якщо Ви хочете використовувати `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Необхідно, якщо Ви хочете використовувати шаблони як конфігурацію за замовчуванням.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Необхідно, якщо Ви хочете підтримувати <abbr title="перетворення рядка, який надходить із запиту HTTP, на дані Python">"розбір"</abbr> форми за допомогою `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Необхідно для підтримки `SessionMiddleware`.
|
||||
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Необхідно для підтримки Starlette `SchemaGenerator` (ймовірно, вам це не потрібно з FastAPI).
|
||||
|
||||
FastAPI / Starlette використовують:
|
||||
|
||||
* <a href="https://www.uvicorn.dev" target="_blank"><code>uvicorn</code></a> - для сервера, який завантажує та обслуговує вашу програму.
|
||||
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Необхідно, якщо Ви хочете використовувати `ORJSONResponse`.
|
||||
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Необхідно, якщо Ви хочете використовувати `UJSONResponse`.
|
||||
|
||||
Ви можете встановити все це за допомогою `pip install fastapi[all]`.
|
||||
|
||||
## Ліцензія
|
||||
|
||||
Цей проєкт ліцензовано згідно з умовами ліцензії MIT.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Навчання
|
||||
|
||||
У цьому розділі надані вступні та навчальні матеріали для вивчення FastAPI.
|
||||
|
||||
Це можна розглядати як **книгу**, **курс**, або **офіційний** та рекомендований спосіб освоїти FastAPI. 😎
|
||||
@@ -0,0 +1,489 @@
|
||||
# Вступ до типів Python
|
||||
|
||||
Python підтримує додаткові "підказки типу" ("type hints") (також звані "анотаціями типу" ("type annotations")).
|
||||
|
||||
Ці **"type hints"** є спеціальним синтаксисом, що дозволяє оголошувати <abbr title="наприклад: str, int, float, bool">тип</abbr> змінної.
|
||||
|
||||
За допомогою оголошення типів для ваших змінних, редактори та інструменти можуть надати вам кращу підтримку.
|
||||
|
||||
Це просто **швидкий посібник / нагадування** про анотації типів у Python. Він покриває лише мінімум, необхідний щоб використовувати їх з **FastAPI**... що насправді дуже мало.
|
||||
|
||||
**FastAPI** повністю базується на цих анотаціях типів, вони дають йому багато переваг.
|
||||
|
||||
Але навіть якщо ви ніколи не використаєте **FastAPI**, вам буде корисно дізнатись трохи про них.
|
||||
|
||||
/// note
|
||||
|
||||
Якщо ви експерт у Python і ви вже знаєте усе про анотації типів - перейдіть до наступного розділу.
|
||||
|
||||
///
|
||||
|
||||
## Мотивація
|
||||
|
||||
Давайте почнемо з простого прикладу:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial001.py *}
|
||||
|
||||
|
||||
Виклик цієї програми виводить:
|
||||
|
||||
```
|
||||
John Doe
|
||||
```
|
||||
|
||||
Функція виконує наступне:
|
||||
|
||||
* Бере `first_name` та `last_name`.
|
||||
* Конвертує кожну літеру кожного слова у верхній регістр за допомогою `title()`.
|
||||
* <abbr title="З’єднує їх, як одне ціле. З вмістом один за одним.">Конкатенує</abbr> їх разом із пробілом по середині.
|
||||
|
||||
{* ../../docs_src/python_types/tutorial001.py hl[2] *}
|
||||
|
||||
|
||||
### Редагуйте це
|
||||
|
||||
Це дуже проста програма.
|
||||
|
||||
Але тепер уявіть, що ви писали це з нуля.
|
||||
|
||||
У певний момент ви розпочали б визначення функції, у вас були б готові параметри...
|
||||
|
||||
Але тоді вам потрібно викликати "той метод, який переводить першу літеру у верхній регістр".
|
||||
|
||||
Це буде `upper`? Чи `uppercase`? `first_uppercase`? `capitalize`?
|
||||
|
||||
Тоді ви спробуєте давнього друга програміста - автозаповнення редактора коду.
|
||||
|
||||
Ви надрукуєте перший параметр функції, `first_name`, тоді крапку (`.`), а тоді натиснете `Ctrl+Space`, щоб запустити автозаповнення.
|
||||
|
||||
Але, на жаль, ви не отримаєте нічого корисного:
|
||||
|
||||
<img src="/img/python-types/image01.png">
|
||||
|
||||
### Додайте типи
|
||||
|
||||
Давайте змінимо один рядок з попередньої версії.
|
||||
|
||||
Ми змінимо саме цей фрагмент, параметри функції, з:
|
||||
|
||||
```Python
|
||||
first_name, last_name
|
||||
```
|
||||
|
||||
на:
|
||||
|
||||
```Python
|
||||
first_name: str, last_name: str
|
||||
```
|
||||
|
||||
Ось і все.
|
||||
|
||||
Це "type hints":
|
||||
|
||||
{* ../../docs_src/python_types/tutorial002.py hl[1] *}
|
||||
|
||||
|
||||
Це не те саме, що оголошення значень за замовчуванням, як це було б з:
|
||||
|
||||
```Python
|
||||
first_name="john", last_name="doe"
|
||||
```
|
||||
|
||||
Це зовсім інше.
|
||||
|
||||
Ми використовуємо двокрапку (`:`), не дорівнює (`=`).
|
||||
|
||||
І додавання анотації типу зазвичай не змінює того, що сталось би без них.
|
||||
|
||||
Але тепер, уявіть що ви посеред процесу створення функції, але з анотаціями типів.
|
||||
|
||||
В цей же момент, ви спробуєте викликати автозаповнення з допомогою `Ctrl+Space` і побачите:
|
||||
|
||||
<img src="/img/python-types/image02.png">
|
||||
|
||||
Разом з цим, ви можете прокручувати, переглядати опції, допоки ви не знайдете одну, що звучить схоже:
|
||||
|
||||
<img src="/img/python-types/image03.png">
|
||||
|
||||
## Більше мотивації
|
||||
|
||||
Перевірте цю функцію, вона вже має анотацію типу:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial003.py hl[1] *}
|
||||
|
||||
|
||||
Оскільки редактор знає типи змінних, ви не тільки отримаєте автозаповнення, ви також отримаєте перевірку помилок:
|
||||
|
||||
<img src="/img/python-types/image04.png">
|
||||
|
||||
Тепер ви знаєте, щоб виправити це, вам потрібно перетворити `age` у строку з допомогою `str(age)`:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial004.py hl[2] *}
|
||||
|
||||
|
||||
## Оголошення типів
|
||||
|
||||
Щойно ви побачили основне місце для оголошення анотацій типу. Як параметри функції.
|
||||
|
||||
Це також основне місце, де ви б їх використовували у **FastAPI**.
|
||||
|
||||
### Прості типи
|
||||
|
||||
Ви можете оголошувати усі стандартні типи у Python, не тільки `str`.
|
||||
|
||||
Ви можете використовувати, наприклад:
|
||||
|
||||
* `int`
|
||||
* `float`
|
||||
* `bool`
|
||||
* `bytes`
|
||||
|
||||
{* ../../docs_src/python_types/tutorial005.py hl[1] *}
|
||||
|
||||
|
||||
### Generic-типи з параметрами типів
|
||||
|
||||
Існують деякі структури даних, які можуть містити інші значення, наприклад `dict`, `list`, `set` та `tuple`. І внутрішні значення також можуть мати свій тип.
|
||||
|
||||
Ці типи, які мають внутрішні типи, називаються "**generic**" типами. І оголосити їх можна навіть із внутрішніми типами.
|
||||
|
||||
Щоб оголосити ці типи та внутрішні типи, ви можете використовувати стандартний модуль Python `typing`. Він існує спеціально для підтримки анотацій типів.
|
||||
|
||||
#### Новіші версії Python
|
||||
|
||||
Синтаксис із використанням `typing` **сумісний** з усіма версіями, від Python 3.6 до останніх, включаючи Python 3.9, Python 3.10 тощо.
|
||||
|
||||
У міру розвитку Python **новіші версії** мають покращену підтримку анотацій типів і в багатьох випадках вам навіть не потрібно буде імпортувати та використовувати модуль `typing` для оголошення анотацій типу.
|
||||
|
||||
Якщо ви можете вибрати новішу версію Python для свого проекту, ви зможете скористатися цією додатковою простотою. Дивіться кілька прикладів нижче.
|
||||
|
||||
#### List (список)
|
||||
|
||||
Наприклад, давайте визначимо змінну, яка буде `list` із `str`.
|
||||
|
||||
//// tab | Python 3.8 і вище
|
||||
|
||||
З модуля `typing`, імпортуємо `List` (з великої літери `L`):
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
Оголосимо змінну з тим самим синтаксисом двокрапки (`:`).
|
||||
|
||||
Як тип вкажемо `List`, який ви імпортували з `typing`.
|
||||
|
||||
Оскільки список є типом, який містить деякі внутрішні типи, ви поміщаєте їх у квадратні дужки:
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!> ../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9 і вище
|
||||
|
||||
Оголосимо змінну з тим самим синтаксисом двокрапки (`:`).
|
||||
|
||||
Як тип вкажемо `list`.
|
||||
|
||||
Оскільки список є типом, який містить деякі внутрішні типи, ви поміщаєте їх у квадратні дужки:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial006_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
/// info
|
||||
|
||||
Ці внутрішні типи в квадратних дужках називаються "параметрами типу".
|
||||
|
||||
У цьому випадку, `str` це параметр типу переданий у `List` (або `list` у Python 3.9 і вище).
|
||||
|
||||
///
|
||||
|
||||
Це означає: "змінна `items` це `list`, і кожен з елементів у цьому списку - `str`".
|
||||
|
||||
/// tip
|
||||
|
||||
Якщо ви використовуєте Python 3.9 і вище, вам не потрібно імпортувати `List` з `typing`, ви можете використовувати натомість тип `list`.
|
||||
|
||||
///
|
||||
|
||||
Зробивши це, ваш редактор може надати підтримку навіть під час обробки елементів зі списку:
|
||||
|
||||
<img src="/img/python-types/image05.png">
|
||||
|
||||
Без типів цього майже неможливо досягти.
|
||||
|
||||
Зверніть увагу, що змінна `item` є одним із елементів у списку `items`.
|
||||
|
||||
І все ж редактор знає, що це `str`, і надає підтримку для цього.
|
||||
|
||||
#### Tuple and Set (кортеж та набір)
|
||||
|
||||
Ви повинні зробити те ж саме, щоб оголосити `tuple` і `set`:
|
||||
|
||||
//// tab | Python 3.8 і вище
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial007.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9 і вище
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial007_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Це означає:
|
||||
|
||||
* Змінна `items_t` це `tuple` з 3 елементами, `int`, ще `int`, та `str`.
|
||||
* Змінна `items_s` це `set`, і кожен його елемент типу `bytes`.
|
||||
|
||||
#### Dict (словник)
|
||||
|
||||
Щоб оголосити `dict`, вам потрібно передати 2 параметри типу, розділені комами.
|
||||
|
||||
Перший параметр типу для ключа у `dict`.
|
||||
|
||||
Другий параметр типу для значення у `dict`:
|
||||
|
||||
//// tab | Python 3.8 і вище
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial008.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9 і вище
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial008_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Це означає:
|
||||
|
||||
* Змінна `prices` це `dict`:
|
||||
* Ключі цього `dict` типу `str` (наприклад, назва кожного елементу).
|
||||
* Значення цього `dict` типу `float` (наприклад, ціна кожного елементу).
|
||||
|
||||
#### Union (об'єднання)
|
||||
|
||||
Ви можете оголосити, що змінна може бути будь-яким із **кількох типів**, наприклад, `int` або `str`.
|
||||
|
||||
У Python 3.6 і вище (включаючи Python 3.10) ви можете використовувати тип `Union` з `typing` і вставляти в квадратні дужки можливі типи, які можна прийняти.
|
||||
|
||||
У Python 3.10 також є **альтернативний синтаксис**, у якому ви можете розділити можливі типи за допомогою <abbr title='також називають «побітовим "або" оператором», але це значення тут не актуальне'>вертикальної смуги (`|`)</abbr>.
|
||||
|
||||
//// tab | Python 3.8 і вище
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial008b.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10 і вище
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
В обох випадках це означає, що `item` може бути `int` або `str`.
|
||||
|
||||
#### Possibly `None` (Optional)
|
||||
|
||||
Ви можете оголосити, що значення може мати тип, наприклад `str`, але також може бути `None`.
|
||||
|
||||
У Python 3.6 і вище (включаючи Python 3.10) ви можете оголосити його, імпортувавши та використовуючи `Optional` з модуля `typing`.
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../docs_src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
Використання `Optional[str]` замість просто `str` дозволить редактору допомогти вам виявити помилки, коли ви могли б вважати, що значенням завжди є `str`, хоча насправді воно також може бути `None`.
|
||||
|
||||
`Optional[Something]` насправді є скороченням для `Union[Something, None]`, вони еквівалентні.
|
||||
|
||||
Це також означає, що в Python 3.10 ви можете використовувати `Something | None`:
|
||||
|
||||
//// tab | Python 3.8 і вище
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8 і вище - альтернатива
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial009b.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10 і вище
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial009_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
#### Generic типи
|
||||
|
||||
Ці типи, які приймають параметри типу у квадратних дужках, називаються **Generic types** or **Generics**, наприклад:
|
||||
|
||||
//// tab | Python 3.8 і вище
|
||||
|
||||
* `List`
|
||||
* `Tuple`
|
||||
* `Set`
|
||||
* `Dict`
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...та інші.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9 і вище
|
||||
|
||||
Ви можете використовувати ті самі вбудовані типи, як generic (з квадратними дужками та типами всередині):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
І те саме, що й у Python 3.8, із модуля `typing`:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...та інші.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10 і вище
|
||||
|
||||
Ви можете використовувати ті самі вбудовані типи, як generic (з квадратними дужками та типами всередині):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
І те саме, що й у Python 3.8, із модуля `typing`:
|
||||
|
||||
* `Union`
|
||||
* `Optional` (так само як у Python 3.8)
|
||||
* ...та інші.
|
||||
|
||||
У Python 3.10, як альтернатива використанню `Union` та `Optional`, ви можете використовувати <abbr title='також називають «побітовим "або" оператором», але це значення тут не актуальне'>вертикальну смугу (`|`)</abbr> щоб оголосити об'єднання типів.
|
||||
|
||||
////
|
||||
|
||||
### Класи як типи
|
||||
|
||||
Ви також можете оголосити клас як тип змінної.
|
||||
|
||||
Скажімо, у вас є клас `Person` з імʼям:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial010.py hl[1:3] *}
|
||||
|
||||
|
||||
Потім ви можете оголосити змінну типу `Person`:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial010.py hl[6] *}
|
||||
|
||||
|
||||
І знову ж таки, ви отримуєте всю підтримку редактора:
|
||||
|
||||
<img src="/img/python-types/image06.png">
|
||||
|
||||
## Pydantic моделі
|
||||
|
||||
<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> це бібліотека Python для валідації даних.
|
||||
|
||||
Ви оголошуєте «форму» даних як класи з атрибутами.
|
||||
|
||||
І кожен атрибут має тип.
|
||||
|
||||
Потім ви створюєте екземпляр цього класу з деякими значеннями, і він перевірить ці значення, перетворить їх у відповідний тип (якщо є потреба) і надасть вам об’єкт з усіма даними.
|
||||
|
||||
І ви отримуєте всю підтримку редактора з цим отриманим об’єктом.
|
||||
|
||||
Приклад з документації Pydantic:
|
||||
|
||||
//// tab | Python 3.8 і вище
|
||||
|
||||
```Python
|
||||
{!> ../../docs_src/python_types/tutorial011.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9 і вище
|
||||
|
||||
```Python
|
||||
{!> ../../docs_src/python_types/tutorial011_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10 і вище
|
||||
|
||||
```Python
|
||||
{!> ../../docs_src/python_types/tutorial011_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
/// info
|
||||
|
||||
Щоб дізнатись більше про <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic, перегляньте його документацію</a>.
|
||||
|
||||
///
|
||||
|
||||
**FastAPI** повністю базується на Pydantic.
|
||||
|
||||
Ви побачите набагато більше цього всього на практиці в [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
## Анотації типів у **FastAPI**
|
||||
|
||||
**FastAPI** використовує ці підказки для виконання кількох речей.
|
||||
|
||||
З **FastAPI** ви оголошуєте параметри з підказками типу, і отримуєте:
|
||||
|
||||
* **Підтримку редактора**.
|
||||
* **Перевірку типів**.
|
||||
|
||||
...і **FastAPI** використовує ті самі оголошення для:
|
||||
|
||||
* **Визначення вимог**: з параметрів шляху запиту, параметрів запиту, заголовків, тіл, залежностей тощо.
|
||||
* **Перетворення даних**: із запиту в необхідний тип.
|
||||
* **Перевірка даних**: що надходять від кожного запиту:
|
||||
* Генерування **автоматичних помилок**, що повертаються клієнту, коли дані недійсні.
|
||||
* **Документування** API за допомогою OpenAPI:
|
||||
* який потім використовується для автоматичної інтерактивної документації користувальницьких інтерфейсів.
|
||||
|
||||
Все це може здатися абстрактним. Не хвилюйтеся. Ви побачите все це в дії в [Туторіал - Посібник користувача](tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
Важливо те, що за допомогою стандартних типів Python в одному місці (замість того, щоб додавати більше класів, декораторів тощо), **FastAPI** зробить багато роботи за вас.
|
||||
|
||||
/// info
|
||||
|
||||
Якщо ви вже пройшли весь навчальний посібник і повернулися, щоб дізнатися більше про типи, ось хороший ресурс <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">"шпаргалка" від `mypy`</a>.
|
||||
|
||||
///
|
||||
@@ -0,0 +1,85 @@
|
||||
# Фонові задачі
|
||||
|
||||
Ви можете створювати фонові задачі, які будуть виконуватися *після* повернення відповіді.
|
||||
|
||||
Це корисно для операцій, які потрібно виконати після обробки запиту, але клієнту не обов’язково чекати завершення цієї операції перед отриманням відповіді.
|
||||
|
||||
Приклади використання:
|
||||
|
||||
* Надсилання email-сповіщень після виконання певної дії:
|
||||
* Підключення до поштового сервера та надсилання листа може займати кілька секунд. Ви можете відразу повернути відповідь, а email відправити у фоні.
|
||||
* Обробка даних:
|
||||
* Наприклад, якщо отримано файл, який потрібно обробити довготривалим процесом, можна повернути відповідь "Accepted" ("Прийнято", HTTP 202) і виконати обробку файлу у фоні.
|
||||
|
||||
## Використання `BackgroundTasks`
|
||||
|
||||
Спочатку імпортуйте `BackgroundTasks` і додайте його як параметр у Вашу *функцію операції шляху* (path operation function) до `BackgroundTasks`:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
|
||||
|
||||
**FastAPI** автоматично створить об'єкт `BackgroundTasks` і передасть його у цей параметр.
|
||||
|
||||
|
||||
## Створення функції задачі
|
||||
|
||||
Створіть функцію, яка буде виконувати фонову задачу.
|
||||
|
||||
Це звичайна функція, яка може отримувати параметри.
|
||||
|
||||
Вона може бути асинхронною `async def` або звичайною `def` функцією – **FastAPI** обробить її правильно.
|
||||
|
||||
У нашому випадку функція записує у файл (імітуючи надсилання email).
|
||||
|
||||
І оскільки операція запису не використовує `async` та `await`, ми визначаємо функцію як звичайну `def`:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
|
||||
|
||||
## Додавання фонової задачі
|
||||
|
||||
Усередині Вашої *функції обробки шляху*, передайте функцію задачі в об'єкт *background tasks*, використовуючи метод `.add_task()`:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
|
||||
|
||||
`.add_task()` приймає аргументи:
|
||||
|
||||
* Функція задача, яка буде виконуватися у фоновому режимі (`write_notification`). Зверніть увагу, що передається обʼєкт без дужок.
|
||||
* Будь-яка послідовність аргументів, які потрібно передати у функцію завдання у відповідному порядку (`email`).
|
||||
* Будь-які іменовані аргументи, які потрібно передати у функцію задачу (`message="some notification"`).
|
||||
|
||||
## Впровадження залежностей
|
||||
|
||||
Використання `BackgroundTasks` також працює з системою впровадження залежностей. Ви можете оголосити параметр типу `BackgroundTasks` на різних рівнях: у *функції операції шляху*, у залежності (dependable), у під залежності тощо.
|
||||
|
||||
**FastAPI** знає, як діяти в кожному випадку і як повторно використовувати один і той самий об'єкт, щоб усі фонові задачі були об’єднані та виконувалися у фоновому режимі після завершення основного запиту.
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
|
||||
|
||||
У цьому прикладі повідомлення будуть записані у файл `log.txt` *після* того, як відповідь буде надіслана.
|
||||
|
||||
Якщо у запиті був переданий query-параметр, він буде записаний у лог у фоновій задачі.
|
||||
|
||||
А потім інша фонова задача, яка створюється у *функції операції шляху*, запише повідомлення з використанням path параметра `email`.
|
||||
|
||||
## Технічні деталі
|
||||
|
||||
Клас `BackgroundTasks` походить безпосередньо з <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">`starlette.background`</a>.
|
||||
|
||||
Він імпортується безпосередньо у FastAPI, щоб Ви могли використовувати його з `fastapi` і випадково не імпортували `BackgroundTask` (без s в кінці) з `starlette.background`.
|
||||
|
||||
Якщо використовувати лише `BackgroundTasks` (а не `BackgroundTask`), то його можна передавати як параметр у *функції операції шляху*, і **FastAPI** подбає про все інше, так само як і про використання об'єкта `Request`.
|
||||
|
||||
Також можна використовувати `BackgroundTask` окремо в FastAPI, але для цього Вам доведеться створити об'єкт у коді та повернути Starlette `Response`, включаючи його.
|
||||
|
||||
Детальніше можна почитати в <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">офіційній документації Starlette про фонові задачі </a>.
|
||||
|
||||
## Застереження
|
||||
|
||||
Якщо Вам потрібно виконувати складні фонові обчислення, і при цьому нема потреби запускати їх у тому ж процесі (наприклад, не потрібно спільного доступу до пам’яті чи змінних), можливо, варто скористатися більш потужними інструментами, такими як <a href="https://docs.celeryq.dev" class="external-link" target="_blank">Celery</a>.
|
||||
|
||||
Такі інструменти зазвичай потребують складнішої конфігурації та менеджера черги повідомлень/завдань, наприклад, RabbitMQ або Redis. Однак вони дозволяють виконувати фонові задачі в кількох процесах і навіть на кількох серверах.
|
||||
|
||||
Якщо ж Вам потрібно отримати доступ до змінних і об’єктів із тієї ж **FastAPI** - програми або виконувати невеликі фонові завдання (наприклад, надсилати сповіщення електронною поштою), достатньо просто використовувати `BackgroundTasks`.
|
||||
|
||||
## Підсумок
|
||||
|
||||
Імпортуйте та використовуйте `BackgroundTasks` як параметр у *функціях операції шляху* та залежностях, щоб додавати фонові задачі.
|
||||
@@ -0,0 +1,60 @@
|
||||
# Тіло - Поля
|
||||
|
||||
Так само як ви можете визначати додаткову валідацію та метадані у параметрах *функції обробки шляху* за допомогою `Query`, `Path` та `Body`, ви можете визначати валідацію та метадані всередині моделей Pydantic за допомогою `Field` від Pydantic.
|
||||
|
||||
## Імпорт `Field`
|
||||
|
||||
Спочатку вам потрібно імпортувати це:
|
||||
|
||||
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *}
|
||||
|
||||
/// warning
|
||||
|
||||
Зверніть увагу, що `Field` імпортується прямо з `pydantic`, а не з `fastapi`, як всі інші (`Query`, `Path`, `Body` тощо).
|
||||
|
||||
///
|
||||
|
||||
## Оголошення атрибутів моделі
|
||||
|
||||
Ви можете використовувати `Field` з атрибутами моделі:
|
||||
|
||||
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *}
|
||||
|
||||
`Field` працює так само, як `Query`, `Path` і `Body`, у нього такі самі параметри тощо.
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Насправді, `Query`, `Path` та інші, що ви побачите далі, створюють об'єкти підкласів загального класу `Param`, котрий сам є підкласом класу `FieldInfo` з Pydantic.
|
||||
|
||||
І `Field` від Pydantic також повертає екземпляр `FieldInfo`.
|
||||
|
||||
`Body` також безпосередньо повертає об'єкти підкласу `FieldInfo`. І є інші підкласи, які ви побачите пізніше, що є підкласами класу Body.
|
||||
|
||||
Пам'ятайте, що коли ви імпортуєте 'Query', 'Path' та інше з 'fastapi', вони фактично є функціями, які повертають спеціальні класи.
|
||||
|
||||
///
|
||||
|
||||
/// tip
|
||||
|
||||
Зверніть увагу, що кожен атрибут моделі із типом, значенням за замовчуванням та `Field` має ту саму структуру, що й параметр *функції обробки шляху*, з `Field` замість `Path`, `Query` і `Body`.
|
||||
|
||||
///
|
||||
|
||||
## Додавання додаткової інформації
|
||||
|
||||
Ви можете визначити додаткову інформацію у `Field`, `Query`, `Body` тощо. І вона буде включена у згенеровану JSON схему.
|
||||
|
||||
Ви дізнаєтеся більше про додавання додаткової інформації пізніше у документації, коли вивчатимете визначення прикладів.
|
||||
|
||||
/// warning
|
||||
|
||||
Додаткові ключі, передані в `Field`, також будуть присутні у згенерованій схемі OpenAPI для вашого додатка.
|
||||
Оскільки ці ключі не обов'язково можуть бути частиною специфікації OpenAPI, деякі інструменти OpenAPI, наприклад, [OpenAPI валідатор](https://validator.swagger.io/), можуть не працювати з вашою згенерованою схемою.
|
||||
|
||||
///
|
||||
|
||||
## Підсумок
|
||||
|
||||
Ви можете використовувати `Field` з Pydantic для визначення додаткових перевірок та метаданих для атрибутів моделі.
|
||||
|
||||
Ви також можете використовувати додаткові іменовані аргументи для передачі додаткових метаданих JSON схеми.
|
||||
@@ -0,0 +1,170 @@
|
||||
# Тіло запиту - Декілька параметрів
|
||||
|
||||
Тепер, коли ми розглянули використання `Path` та `Query`, розгляньмо більш просунуті способи оголошення тіла запиту в **FastAPI**.
|
||||
|
||||
## Змішування `Path`, `Query` та параметрів тіла запиту
|
||||
|
||||
По-перше, звісно, Ви можете вільно змішувати оголошення параметрів `Path`, `Query` та тіла запиту, і **FastAPI** правильно їх обробить.
|
||||
|
||||
Також Ви можете оголосити параметри тіла як необов’язкові, встановивши для них значення за замовчуванням `None`:
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
|
||||
|
||||
/// note | Примітка
|
||||
|
||||
Зверніть увагу, що в цьому випадку параметр `item`, який береться з тіла запиту, є необов'язковим, оскільки має значення за замовчуванням `None`.
|
||||
|
||||
///
|
||||
|
||||
## Декілька параметрів тіла запиту
|
||||
|
||||
У попередньому прикладі *операція шляху* очікувала JSON з атрибутами `Item`, наприклад:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"description": "The pretender",
|
||||
"price": 42.0,
|
||||
"tax": 3.2
|
||||
}
|
||||
```
|
||||
Але Ви також можете оголосити декілька параметрів тіла, наприклад `item` та `user`:
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
|
||||
|
||||
У цьому випадку **FastAPI** розпізнає, що є кілька параметрів тіла (два параметри є моделями Pydantic).
|
||||
|
||||
Тому він використає назви параметрів як ключі (назви полів) у тілі запиту, очікуючи:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"item": {
|
||||
"name": "Foo",
|
||||
"description": "The pretender",
|
||||
"price": 42.0,
|
||||
"tax": 3.2
|
||||
},
|
||||
"user": {
|
||||
"username": "dave",
|
||||
"full_name": "Dave Grohl"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
/// note | Примітка
|
||||
|
||||
Зверніть увагу, що хоча `item` оголошено, так само як і раніше, тепер він очікується в тілі під ключем `item`.
|
||||
|
||||
///
|
||||
|
||||
**FastAPI** автоматично конвертує дані із запиту таким чином, щоб параметр `item` отримав свій вміст, і те ж саме стосується `user`.
|
||||
|
||||
Він виконає валідацію складених даних і задокументує їх відповідним чином у схемі OpenAPI та в автоматичній документації.
|
||||
|
||||
## Одиничні значення в тілі запиту
|
||||
|
||||
Так само як є `Query` і `Path` для визначення додаткових даних для параметрів запиту та шляху, **FastAPI** надає еквівалентний `Body`.
|
||||
|
||||
Наприклад, розширюючи попередню модель, Ви можете вирішити додати ще один ключ `importance` в те ж саме тіло запиту разом із `item` і `user`.
|
||||
|
||||
Якщо Ви оголосите його як є, то, оскільки це одиничне значення, **FastAPI** припускатиме, що це параметр запиту (query parameter).
|
||||
|
||||
Але Ви можете вказати **FastAPI** обробляти його як інший ключ тіла (body key), використовуючи `Body`:
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *}
|
||||
|
||||
У цьому випадку **FastAPI** очікуватиме тіло запиту у такому вигляді:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"item": {
|
||||
"name": "Foo",
|
||||
"description": "The pretender",
|
||||
"price": 42.0,
|
||||
"tax": 3.2
|
||||
},
|
||||
"user": {
|
||||
"username": "dave",
|
||||
"full_name": "Dave Grohl"
|
||||
},
|
||||
"importance": 5
|
||||
}
|
||||
```
|
||||
Знову ж таки, **FastAPI** конвертуватиме типи даних, перевірятиме їх, створюватиме документацію тощо.
|
||||
|
||||
## Декілька body та query параметрів
|
||||
|
||||
Звісно, Ви можете оголошувати додаткові query параметри запиту, коли це необхідно, на додаток до будь-яких параметрів тіла запиту.
|
||||
|
||||
Оскільки за замовчуванням окремі значення інтерпретуються як параметри запиту, Вам не потрібно явно додавати `Query`, можна просто використати:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
Або в Python 3.10 та вище:
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
Наприклад:
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
|
||||
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
`Body` також має ті самі додаткові параметри валідації та метаданих, що й `Query`, `Path` та інші, які Ви побачите пізніше.
|
||||
|
||||
///
|
||||
|
||||
## Вкладений поодинокий параметр тіла запиту
|
||||
|
||||
Припустимо, у вас є лише один параметр тіла запиту `item` з моделі Pydantic `Item`.
|
||||
|
||||
За замовчуванням **FastAPI** очікуватиме, що тіло запиту міститиме вміст безпосередньо.
|
||||
|
||||
Але якщо Ви хочете, щоб він очікував JSON з ключем `item`, а всередині — вміст моделі (так, як це відбувається при оголошенні додаткових параметрів тіла), Ви можете використати спеціальний параметр `Body` — `embed`:
|
||||
|
||||
```Python
|
||||
item: Item = Body(embed=True)
|
||||
```
|
||||
|
||||
як у прикладі:
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *}
|
||||
|
||||
У цьому випадку **FastAPI** очікуватиме тіло запиту такого вигляду:
|
||||
|
||||
```JSON hl_lines="2"
|
||||
{
|
||||
"item": {
|
||||
"name": "Foo",
|
||||
"description": "The pretender",
|
||||
"price": 42.0,
|
||||
"tax": 3.2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
замість:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"description": "The pretender",
|
||||
"price": 42.0,
|
||||
"tax": 3.2
|
||||
}
|
||||
```
|
||||
|
||||
## Підсумок
|
||||
|
||||
Ви можете додавати кілька параметрів тіла до Вашої *функції операції шляху* (*path operation function*), навіть якщо запит може мати лише одне тіло.
|
||||
|
||||
Але **FastAPI** обробить це, надасть Вам потрібні дані у функції, перевірить їх та задокументує коректну схему в *операції шляху*.
|
||||
|
||||
Також Ви можете оголошувати окремі значення, які будуть отримані як частина тіла запиту.
|
||||
|
||||
Крім того, Ви можете вказати **FastAPI** вбудовувати тіло в ключ, навіть якщо оголошено лише один параметр.
|
||||
@@ -0,0 +1,245 @@
|
||||
# Тіло запиту - Вкладені моделі
|
||||
|
||||
З **FastAPI** Ви можете визначати, перевіряти, документувати та використовувати моделі, які можуть бути вкладені на будь-яку глибину (завдяки Pydantic).
|
||||
|
||||
## Поля списку
|
||||
|
||||
Ви можете визначити атрибут як підтип. Наприклад, Python-список (`list`):
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
|
||||
|
||||
Це зробить `tags` списком, хоча не визначається тип елементів списку.
|
||||
|
||||
## Поля списку з параметром типу
|
||||
|
||||
Але Python має специфічний спосіб оголошення списків з внутрішніми типами або "параметрами типу":
|
||||
### Імпортуємо `List` з модуля typing
|
||||
|
||||
У Python 3.9 і вище можна використовувати стандартний `list` для оголошення таких типів, як ми побачимо нижче. 💡
|
||||
|
||||
Але в Python версії до 3.9 (від 3.6 і вище) спочатку потрібно імпортувати `List` з модуля стандартної бібліотеки Python `typing`:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
|
||||
|
||||
### Оголошення `list` з параметром типу
|
||||
|
||||
Щоб оголосити типи з параметрами типу (внутрішніми типами), такими як `list`, `dict`, `tuple`:
|
||||
|
||||
* Якщо Ви використовуєте версію Python до 3.9, імпортуйте їх відповідну версію з модуля `typing`.
|
||||
* Передайте внутрішні типи як "параметри типу", використовуючи квадратні дужки: `[` and `]`.
|
||||
|
||||
У Python 3.9 це буде виглядати так:
|
||||
|
||||
```Python
|
||||
my_list: list[str]
|
||||
```
|
||||
|
||||
У версіях Python до 3.9 це виглядає так:
|
||||
|
||||
```Python
|
||||
from typing import List
|
||||
|
||||
my_list: List[str]
|
||||
```
|
||||
|
||||
Це стандартний синтаксис Python для оголошення типів.
|
||||
|
||||
Використовуйте той самий стандартний синтаксис для атрибутів моделей з внутрішніми типами.
|
||||
|
||||
Отже, у нашому прикладі, ми можемо зробити `tags` саме "списком рядків":
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
|
||||
|
||||
## Типи множин
|
||||
|
||||
Але потім ми подумали, що теги не повинні повторюватися, вони, ймовірно, повинні бути унікальними рядками.
|
||||
|
||||
І Python має спеціальний тип даних для множин унікальних елементів — це `set`.
|
||||
|
||||
Тому ми можемо оголосити `tags` як множину рядків:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *}
|
||||
|
||||
Навіть якщо Ви отримаєте запит з дубльованими даними, він буде перетворений у множину унікальних елементів.
|
||||
|
||||
І коли Ви будете виводити ці дані, навіть якщо джерело містить дублікати, вони будуть виведені як множина унікальних елементів.
|
||||
|
||||
І це буде анотовано/документовано відповідно.
|
||||
|
||||
## Вкладені моделі
|
||||
|
||||
Кожен атрибут моделі Pydantic має тип.
|
||||
|
||||
Але цей тип сам може бути іншою моделлю Pydantic.
|
||||
|
||||
Отже, Ви можете оголосити глибоко вкладені JSON "об'єкти" з конкретними іменами атрибутів, типами та перевірками.
|
||||
|
||||
Усе це, вкладене без обмежень.
|
||||
|
||||
### Визначення підмоделі
|
||||
|
||||
Наприклад, ми можемо визначити модель `Image`:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
|
||||
|
||||
### Використання підмоделі як типу
|
||||
|
||||
А потім ми можемо використовувати її як тип атрибута:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *}
|
||||
|
||||
Це означатиме, що **FastAPI** очікуватиме тіло запиту такого вигляду:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"description": "The pretender",
|
||||
"price": 42.0,
|
||||
"tax": 3.2,
|
||||
"tags": ["rock", "metal", "bar"],
|
||||
"image": {
|
||||
"url": "http://example.com/baz.jpg",
|
||||
"name": "The Foo live"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Завдяки такій декларації у **FastAPI** Ви отримуєте:
|
||||
|
||||
* Підтримку в редакторі (автозавершення тощо), навіть для вкладених моделей
|
||||
* Конвертацію даних
|
||||
* Валідацію даних
|
||||
* Автоматичну документацію
|
||||
|
||||
## Спеціальні типи та валідація
|
||||
|
||||
Окрім звичайних типів, таких як `str`, `int`, `float`, та ін. Ви можете використовувати складніші типи, які наслідують `str`.
|
||||
|
||||
Щоб побачити всі доступні варіанти, ознайомтеся з оглядом <a href="https://docs.pydantic.dev/latest/concepts/types/" class="external-link" target="_blank">типів у Pydantic</a>. Деякі приклади будуть у наступних розділах.
|
||||
|
||||
Наприклад, у моделі `Image` є поле `url`, тому ми можемо оголосити його як `HttpUrl` від Pydantic замість `str`:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
|
||||
|
||||
Рядок буде перевірено як дійсну URL-адресу і задокументовано в JSON Schema / OpenAPI як URL.
|
||||
|
||||
## Атрибути зі списками підмоделей
|
||||
|
||||
У Pydantic Ви можете використовувати моделі як підтипи для `list`, `set` тощо:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
|
||||
|
||||
Це означає, що **FastAPI** буде очікувати (конвертувати, валідувати, документувати тощо) JSON тіло запиту у вигляді:
|
||||
|
||||
```JSON hl_lines="11"
|
||||
{
|
||||
"name": "Foo",
|
||||
"description": "The pretender",
|
||||
"price": 42.0,
|
||||
"tax": 3.2,
|
||||
"tags": [
|
||||
"rock",
|
||||
"metal",
|
||||
"bar"
|
||||
],
|
||||
"images": [
|
||||
{
|
||||
"url": "http://example.com/baz.jpg",
|
||||
"name": "The Foo live"
|
||||
},
|
||||
{
|
||||
"url": "http://example.com/dave.jpg",
|
||||
"name": "The Baz"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Зверніть увагу, що тепер ключ `images` містить список об'єктів зображень.
|
||||
|
||||
///
|
||||
|
||||
## Глибоко вкладені моделі
|
||||
|
||||
Ви можете визначати вкладені моделі довільної глибини:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Зверніть увагу, що в моделі `Offer` є список `Item`ів, які, своєю чергою, можуть мати необов'язковий список `Image`ів.
|
||||
|
||||
///
|
||||
|
||||
## Тіла запитів, що складаються зі списків
|
||||
|
||||
Якщо верхній рівень JSON тіла, яке Ви очікуєте, є JSON `масивом` (у Python — `list`), Ви можете оголосити тип у параметрі функції, як і в моделях Pydantic:
|
||||
|
||||
```Python
|
||||
images: List[Image]
|
||||
```
|
||||
або в Python 3.9 і вище:
|
||||
|
||||
```Python
|
||||
images: list[Image]
|
||||
```
|
||||
|
||||
наприклад:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *}
|
||||
|
||||
## Підтримка в редакторі всюди
|
||||
|
||||
Ви отримаєте підтримку в редакторі всюди.
|
||||
|
||||
Навіть для елементів у списках:
|
||||
|
||||
<img src="/img/tutorial/body-nested-models/image01.png">
|
||||
|
||||
Ви не змогли б отримати таку підтримку в редакторі, якби працювали напряму зі `dict`, а не з моделями Pydantic.
|
||||
|
||||
Але Вам не потрібно турбуватися про це: вхідні dict'и автоматично конвертуються, а вихідні дані автоматично перетворюються в JSON.
|
||||
|
||||
## Тіла з довільними `dict`
|
||||
|
||||
Ви також можете оголосити тіло як `dict` з ключами одного типу та значеннями іншого типу.
|
||||
|
||||
Це корисно, якщо Ви не знаєте наперед, які імена полів будуть дійсними (як у випадку з моделями Pydantic).
|
||||
|
||||
Це буде корисно, якщо Ви хочете приймати ключі, які заздалегідь невідомі.
|
||||
|
||||
---
|
||||
|
||||
Це також зручно, якщо Ви хочете мати ключі іншого типу (наприклад, `int`).
|
||||
|
||||
Ось що ми розглянемо далі.
|
||||
|
||||
У цьому випадку Ви можете приймати будь-який `dict`, якщо його ключі — це `int`, а значення — `float`:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial009_py39.py hl[7] *}
|
||||
|
||||
/// tip | Порада
|
||||
|
||||
Майте на увазі, що в JSON тілі ключі можуть бути лише рядками (`str`).
|
||||
|
||||
Але Pydantic автоматично конвертує дані.
|
||||
|
||||
Це означає, що навіть якщо клієнти вашого API надсилатимуть ключі у вигляді рядків, якщо вони містять цілі числа, Pydantic конвертує їх і проведе валідацію.
|
||||
|
||||
Тобто `dict`, який Ви отримаєте як `weights`, матиме ключі типу `int` та значення типу `float`.
|
||||
|
||||
///
|
||||
|
||||
## Підсумок
|
||||
|
||||
З **FastAPI** Ви маєте максимальну гнучкість завдяки моделям Pydantic, зберігаючи при цьому код простим, коротким та елегантним.
|
||||
|
||||
А також отримуєте всі переваги:
|
||||
|
||||
* Підтримка в редакторі (автодоповнення всюди!)
|
||||
* Конвертація даних (парсинг/сериалізація)
|
||||
* Валідація даних
|
||||
* Документація схем
|
||||
* Автоматичне створення документації
|
||||
@@ -0,0 +1,116 @@
|
||||
# Тіло – Оновлення
|
||||
|
||||
## Оновлення з використанням `PUT`
|
||||
|
||||
Щоб оновити елемент, Ви можете використати <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT" class="external-link" target="_blank">HTTP `PUT`</a> операцію.
|
||||
|
||||
Ви можете використати `jsonable_encoder`, щоб перетворити вхідні дані на такі, які можна зберігати як JSON (наприклад, у NoSQL базі даних). Наприклад, перетворюючи `datetime` у `str`.
|
||||
|
||||
{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *}
|
||||
|
||||
`PUT` використовується для отримання даних, які мають замінити чинні дані.
|
||||
|
||||
### Попередження про заміну
|
||||
|
||||
Це означає, що якщо Ви хочете оновити елемент `bar`, використовуючи `PUT` з тілом:
|
||||
|
||||
```Python
|
||||
{
|
||||
"name": "Barz",
|
||||
"price": 3,
|
||||
"description": None,
|
||||
}
|
||||
```
|
||||
|
||||
оскільки він не містить вже збереженого атрибута `"tax": 20.2`, модель введення прийме значення за замовчуванням `"tax": 10.5`.
|
||||
|
||||
І дані будуть збережені з цим "новим" значенням `tax` = `10.5`.
|
||||
|
||||
## Часткові оновлення з `PATCH`
|
||||
|
||||
Ви також можете використовувати операцію <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH" class="external-link" target="_blank">HTTP `PATCH`</a> для *часткового* оновлення даних.
|
||||
|
||||
Це означає, що Ви можете надіслати лише ті дані, які хочете оновити, залишаючи інші без змін.
|
||||
|
||||
/// note | Примітка
|
||||
|
||||
`PATCH` менш відомий і рідше використовується, ніж `PUT`.
|
||||
|
||||
І багато команд використовують лише `PUT`, навіть для часткових оновлень.
|
||||
|
||||
Ви **вільні** використовувати їх так, як хочете, **FastAPI** не накладає обмежень.
|
||||
|
||||
Але цей посібник показує Вам більш-менш як їх задумано використовувати.
|
||||
|
||||
///
|
||||
|
||||
### Використання параметра `exclude_unset` у Pydantic
|
||||
|
||||
Якщо Ви хочете отримати часткові оновлення, дуже зручно використовувати параметр `exclude_unset` у методі `.model_dump()` моделі Pydantic.
|
||||
|
||||
Наприклад: `item.model_dump(exclude_unset=True)`.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
У Pydantic v1 цей метод називався `.dict()`, він був застарілий (але все ще підтримується) у Pydantic v2, і був перейменований у `.model_dump()`.
|
||||
|
||||
Приклади тут використовують `.dict()` для сумісності з Pydantic v1, але Вам слід використовувати `.model_dump()`, якщо можете використовувати Pydantic v2.
|
||||
|
||||
///
|
||||
|
||||
Це створить `dict` лише з тими даними, які були явно встановлені під час створення моделі `item`, виключаючи значення за замовчуванням.
|
||||
|
||||
Тоді Ви можете використовувати це, щоб створити `dict` лише з даними, які були встановлені (надіслані у запиті), пропускаючи значення за замовчуванням:
|
||||
|
||||
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
|
||||
|
||||
### Використання параметра `update` у Pydantic
|
||||
|
||||
Тепер Ви можете створити копію наявної моделі за допомогою `.model_copy()`, і передати параметр `update` з `dict` , який містить дані для оновлення.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
У Pydantic v1 метод називався `.copy()`, він був застарілий (але все ще підтримується) у Pydantic v2, і був перейменований у `.model_copy()`.
|
||||
|
||||
Приклади тут використовують `.copy()` для сумісності з Pydantic v1, але якщо Ви можете використовувати Pydantic v2 — Вам слід використовувати `.model_copy()` замість цього.
|
||||
|
||||
///
|
||||
|
||||
Наприклад: `stored_item_model.model_copy(update=update_data)`:
|
||||
|
||||
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
|
||||
|
||||
### Підсумок часткових оновлень
|
||||
|
||||
У підсумку, щоб застосувати часткові оновлення, Ви:
|
||||
|
||||
* (Опціонально) використовуєте `PATCH` замість `PUT`.
|
||||
* Отримуєте збережені дані.
|
||||
* Поміщаєте ці дані в модель Pydantic.
|
||||
* Генеруєте `dict` без значень за замовчуванням з моделі введення (використовуючи `exclude_unset`).
|
||||
* Таким чином Ви оновите лише ті значення, які були явно задані користувачем, замість того, щоб перезаписувати вже збережені значення значеннями за замовчуванням з вашої моделі.
|
||||
* Створюєте копію збереженої моделі, оновлюючи її атрибути отриманими частковими оновленнями (використовуючи параметр `update`).
|
||||
* Перетворюєте скопійовану модель на щось, що можна зберегти у вашу БД (наприклад, використовуючи `jsonable_encoder`).
|
||||
* Це можна порівняти з повторним використанням методу `.model_dump()` моделі, але це гарантує (і перетворює) значення у типи даних, які можна перетворити на JSON, наприклад, `datetime` на `str`.
|
||||
* Зберігаєте дані у вашу БД.
|
||||
* Повертаєте оновлену модель.
|
||||
|
||||
{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *}
|
||||
|
||||
/// tip | Порада
|
||||
|
||||
Насправді Ви можете використовувати цю саму техніку і з операцією HTTP `PUT`.
|
||||
|
||||
Але приклад тут використовує `PATCH`, тому що він був створений саме для таких випадків.
|
||||
|
||||
///
|
||||
|
||||
/// note | Примітка
|
||||
|
||||
Зверніть увагу, що модель запиту все ще проходить валідацію.
|
||||
|
||||
Тож, якщо Ви хочете отримувати часткові оновлення, які можуть не містити жодного атрибута, Вам потрібно мати модель, де всі атрибути позначені як необов’язкові (зі значеннями за замовчуванням або `None`).
|
||||
|
||||
Щоб розрізняти моделі з усіма необов’язковими значеннями для **оновлення** і моделі з обов’язковими значеннями для **створення**, Ви можете скористатись ідеями, описаними у [Додаткові моделі](extra-models.md){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
@@ -0,0 +1,162 @@
|
||||
# Тіло запиту
|
||||
|
||||
Коли вам потрібно надіслати дані з клієнта (скажімо, браузера) до вашого API, ви надсилаєте їх як **тіло запиту**.
|
||||
|
||||
Тіло **запиту** — це дані, надіслані клієнтом до вашого API. Тіло **відповіді** — це дані, які ваш API надсилає клієнту.
|
||||
|
||||
Ваш API майже завжди має надсилати тіло **відповіді**. Але клієнтам не обов’язково потрібно постійно надсилати тіла **запитів**.
|
||||
|
||||
Щоб оголосити тіло **запиту**, ви використовуєте <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> моделі з усією їх потужністю та перевагами.
|
||||
|
||||
/// info
|
||||
|
||||
Щоб надіслати дані, ви повинні використовувати один із: `POST` (більш поширений), `PUT`, `DELETE` або `PATCH`.
|
||||
|
||||
Надсилання тіла із запитом `GET` має невизначену поведінку в специфікаціях, проте воно підтримується FastAPI лише для дуже складних/екстремальних випадків використання.
|
||||
|
||||
Оскільки це не рекомендується, інтерактивна документація з Swagger UI не відображатиме документацію для тіла запиту під час використання `GET`, і проксі-сервери в середині можуть не підтримувати її.
|
||||
|
||||
///
|
||||
|
||||
## Імпортуйте `BaseModel` від Pydantic
|
||||
|
||||
Спочатку вам потрібно імпортувати `BaseModel` з `pydantic`:
|
||||
|
||||
{* ../../docs_src/body/tutorial001.py hl[4] *}
|
||||
|
||||
## Створіть свою модель даних
|
||||
|
||||
Потім ви оголошуєте свою модель даних як клас, який успадковується від `BaseModel`.
|
||||
|
||||
Використовуйте стандартні типи Python для всіх атрибутів:
|
||||
|
||||
{* ../../docs_src/body/tutorial001.py hl[7:11] *}
|
||||
|
||||
Так само, як і при оголошенні параметрів запиту, коли атрибут моделі має значення за замовчуванням, він не є обов’язковим. В іншому випадку це потрібно. Використовуйте `None`, щоб зробити його необов'язковим.
|
||||
|
||||
Наприклад, ця модель вище оголошує JSON "`об'єкт`" (або Python `dict`), як:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"description": "An optional description",
|
||||
"price": 45.2,
|
||||
"tax": 3.5
|
||||
}
|
||||
```
|
||||
|
||||
...оскільки `description` і `tax` є необов'язковими (зі значенням за замовчуванням `None`), цей JSON "`об'єкт`" також буде дійсним:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"price": 45.2
|
||||
}
|
||||
```
|
||||
|
||||
## Оголоси її як параметр
|
||||
|
||||
Щоб додати модель даних до вашої *операції шляху*, оголосіть її так само, як ви оголосили параметри шляху та запиту:
|
||||
|
||||
{* ../../docs_src/body/tutorial001.py hl[18] *}
|
||||
|
||||
...і вкажіть її тип як модель, яку ви створили, `Item`.
|
||||
|
||||
## Результати
|
||||
|
||||
Лише з цим оголошенням типу Python **FastAPI** буде:
|
||||
|
||||
* Читати тіло запиту як JSON.
|
||||
* Перетворювати відповідні типи (якщо потрібно).
|
||||
* Валідувати дані.
|
||||
* Якщо дані недійсні, він поверне гарну та чітку помилку, вказуючи, де саме і які дані були неправильними.
|
||||
* Надавати отримані дані у параметрі `item`.
|
||||
* Оскільки ви оголосили його у функції як тип `Item`, ви також матимете всю підтримку редактора (автозаповнення, тощо) для всіх атрибутів та їх типів.
|
||||
* Генерувати <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> визначення для вашої моделі, ви також можете використовувати їх де завгодно, якщо це має сенс для вашого проекту.
|
||||
* Ці схеми будуть частиною згенерованої схеми OpenAPI і використовуватимуться автоматичною документацією інтерфейсу користувача.
|
||||
|
||||
## Автоматична документація
|
||||
|
||||
Схеми JSON ваших моделей будуть частиною вашої схеми, згенерованої OpenAPI, і будуть показані в інтерактивній API документації:
|
||||
|
||||
<img src="/img/tutorial/body/image01.png">
|
||||
|
||||
А також використовуватимуться в API документації всередині кожної *операції шляху*, якій вони потрібні:
|
||||
|
||||
<img src="/img/tutorial/body/image02.png">
|
||||
|
||||
## Підтримка редактора
|
||||
|
||||
У вашому редакторі, всередині вашої функції, ви будете отримувати підказки типу та завершення скрізь (це б не сталося, якби ви отримали `dict` замість моделі Pydantic):
|
||||
|
||||
<img src="/img/tutorial/body/image03.png">
|
||||
|
||||
Ви також отримуєте перевірку помилок на наявність операцій з неправильним типом:
|
||||
|
||||
<img src="/img/tutorial/body/image04.png">
|
||||
|
||||
Це не випадково, весь каркас був побудований навколо цього дизайну.
|
||||
|
||||
І він був ретельно перевірений на етапі проектування, перед будь-яким впровадженням, щоб переконатися, що він працюватиме з усіма редакторами.
|
||||
|
||||
Були навіть деякі зміни в самому Pydantic, щоб підтримати це.
|
||||
|
||||
Попередні скріншоти були зроблені у <a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>.
|
||||
|
||||
Але ви отримаєте ту саму підтримку редактора у <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> та більшість інших редакторів Python:
|
||||
|
||||
<img src="/img/tutorial/body/image05.png">
|
||||
|
||||
/// tip
|
||||
|
||||
Якщо ви використовуєте <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> як ваш редактор, ви можете використати <a href="https://github.com/koxudaxi/pydantic-pycharm-plugin/" class="external-link" target="_blank">Pydantic PyCharm Plugin</a>.
|
||||
|
||||
Він покращує підтримку редакторів для моделей Pydantic за допомогою:
|
||||
|
||||
* автозаповнення
|
||||
* перевірки типу
|
||||
* рефакторингу
|
||||
* пошуку
|
||||
* інспекції
|
||||
|
||||
///
|
||||
|
||||
## Використовуйте модель
|
||||
|
||||
Усередині функції ви можете отримати прямий доступ до всіх атрибутів об’єкта моделі:
|
||||
|
||||
{* ../../docs_src/body/tutorial002.py hl[21] *}
|
||||
|
||||
## Тіло запиту + параметри шляху
|
||||
|
||||
Ви можете одночасно оголошувати параметри шляху та тіло запиту.
|
||||
|
||||
**FastAPI** розпізнає, що параметри функції, які відповідають параметрам шляху, мають бути **взяті з шляху**, а параметри функції, які оголошуються як моделі Pydantic, **взяті з тіла запиту**.
|
||||
|
||||
{* ../../docs_src/body/tutorial003.py hl[17:18] *}
|
||||
|
||||
## Тіло запиту + шлях + параметри запиту
|
||||
|
||||
Ви також можете оголосити параметри **тіло**, **шлях** і **запит** одночасно.
|
||||
|
||||
**FastAPI** розпізнає кожен з них і візьме дані з потрібного місця.
|
||||
|
||||
{* ../../docs_src/body/tutorial004.py hl[18] *}
|
||||
|
||||
Параметри функції будуть розпізнаватися наступним чином:
|
||||
|
||||
* Якщо параметр також оголошено в **шляху**, він використовуватиметься як параметр шляху.
|
||||
* Якщо параметр має **сингулярний тип** (наприклад, `int`, `float`, `str`, `bool` тощо), він буде інтерпретуватися як параметр **запиту**.
|
||||
* Якщо параметр оголошується як тип **Pydantic моделі**, він інтерпретується як **тіло** запиту.
|
||||
|
||||
/// note
|
||||
|
||||
FastAPI буде знати, що значення "q" не є обов'язковим через значення за замовчуванням "= None".
|
||||
|
||||
`Optional` у `Optional[str]` не використовується FastAPI, але дозволить вашому редактору надати вам кращу підтримку та виявляти помилки.
|
||||
|
||||
///
|
||||
|
||||
## Без Pydantic
|
||||
|
||||
Якщо ви не хочете використовувати моделі Pydantic, ви також можете використовувати параметри **Body**. Перегляньте документацію для [Тіло – Кілька параметрів: сингулярні значення в тілі](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}.
|
||||
@@ -0,0 +1,76 @@
|
||||
# Моделі для Cookie-параметрів
|
||||
|
||||
Якщо у Вас є група **cookies** параметрів, які пов'язані між собою, Ви можете створити **Pydantic-модель**, щоб оголосити їх. 🍪
|
||||
|
||||
Це дозволить Вам повторно **використовувати модель** у **різних місцях**, а також оголосити валідацію та метадані для всіх параметрів одночасно. 😎
|
||||
|
||||
/// note | Нотатки
|
||||
|
||||
Це підтримується з версії FastAPI `0.115.0`. 🤓
|
||||
|
||||
///
|
||||
|
||||
/// tip | Порада
|
||||
|
||||
Ця ж техніка застосовується до `Query`, `Cookie`, та `Header`. 😎
|
||||
|
||||
///
|
||||
|
||||
## Cookie з Pydantic-моделлю
|
||||
|
||||
Оголосіть **cookie-параметри**, які Вам потрібні, у **Pydantic-моделі**, а потім оголосіть параметр як `Cookie`:
|
||||
|
||||
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
|
||||
|
||||
**FastAPI** буде **витягувати** дані для **кожного поля** з **cookie** параметрів, отриманих у запиті, і передавати Вам Pydantic-модель, яку Ви визначили.
|
||||
|
||||
## Перевірка у документації
|
||||
|
||||
Ви можете побачити визначені cookie в інтерфейсі документації за адресою `/docs`:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/cookie-param-models/image01.png">
|
||||
</div>
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Майте на увазі, що оскільки **браузери обробляють cookie** особливим чином і "за лаштунками", вони **не** дозволяють **JavaScript** легко з ними працювати.
|
||||
|
||||
Якщо Ви зайдете до **інтерфейсу документації API** за адресою `/docs`, Ви зможете побачити **документацію** для cookie у Ваших **операціях шляху**.
|
||||
|
||||
Але навіть якщо Ви заповните дані й натиснете "Execute", оскільки інтерфейс документації працює з **JavaScript**, cookie не будуть відправлені, і Ви побачите **помилку**, ніби Ви не ввели жодних значень.
|
||||
|
||||
///
|
||||
|
||||
## Заборона додаткових cookie
|
||||
|
||||
У деяких спеціальних випадках (ймовірно, не дуже поширених) Ви можете захотіти **обмежити** список cookie, які хочете отримувати.
|
||||
|
||||
Ваша API тепер має можливість контролювати власну <abbr title="Це жарт, якщо що. Це не має нічого спільного зі згодою на використання cookie, але це кумедно, що навіть API тепер може відхиляти бідні cookie. Ловіть печиво. 🍪">згоду на cookie</abbr>. 🤪🍪
|
||||
|
||||
Ви можете використовувати налаштування моделі Pydantic, щоб `заборонити` будь-які `додаткові` поля:
|
||||
|
||||
{* ../../docs_src/cookie_param_models/tutorial002_an_py39.py hl[10] *}
|
||||
|
||||
Якщо клієнт спробує надіслати якісь **додаткові cookie**, він отримає відповідь з **помилкою**.
|
||||
|
||||
Бідні банери cookie, які так старанно намагаються отримати Вашу згоду, щоб <abbr title="Це ще один жарт. Не звертайте уваги. Візьміть каву для свого печива. ☕">API її відхилила</abbr>. 🍪
|
||||
|
||||
Наприклад, якщо клієнт спробує надіслати cookie `santa_tracker` зі значенням `good-list-please`, він отримає відповідь з помилкою, яка повідомить, що <abbr title="Санта не схвалює відсутність cookie. 🎅 Гаразд, більше жартів не буде.">cookie `santa_tracker` не дозволено</abbr>:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "extra_forbidden",
|
||||
"loc": ["cookie", "santa_tracker"],
|
||||
"msg": "Extra inputs are not permitted",
|
||||
"input": "good-list-please",
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Підсумок
|
||||
|
||||
Ви можете використовувати **Pydantic-моделі** для оголошення <abbr title="Отримайте останнє печиво перед тим, як піти. 🍪">cookie</abbr> у FastAPI. 😎
|
||||
@@ -0,0 +1,34 @@
|
||||
# Параметри Cookie
|
||||
|
||||
Ви можете визначити параметри Cookie таким же чином, як визначаються параметри `Query` і `Path`.
|
||||
|
||||
## Імпорт `Cookie`
|
||||
|
||||
Спочатку імпортуйте `Cookie`:
|
||||
|
||||
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## Визначення параметрів `Cookie`
|
||||
|
||||
Потім визначте параметри cookie, використовуючи таку ж конструкцію як для `Path` і `Query`.
|
||||
|
||||
Перше значення це значення за замовчуванням, ви можете також передати всі додаткові параметри валідації чи анотації:
|
||||
|
||||
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[9] *}
|
||||
|
||||
/// note | Технічні Деталі
|
||||
|
||||
`Cookie` це "сестра" класів `Path` і `Query`. Вони наслідуються від одного батьківського класу `Param`.
|
||||
Але пам'ятайте, що коли ви імпортуєте `Query`, `Path`, `Cookie` та інше з `fastapi`, це фактично функції, що повертають спеціальні класи.
|
||||
|
||||
///
|
||||
|
||||
/// info
|
||||
|
||||
Для визначення cookies ви маєте використовувати `Cookie`, тому що в іншому випадку параметри будуть інтерпритовані, як параметри запиту.
|
||||
|
||||
///
|
||||
|
||||
## Підсумки
|
||||
|
||||
Визначайте cookies за допомогою `Cookie`, використовуючи той же спільний шаблон, що і `Query` та `Path`.
|
||||
@@ -0,0 +1,89 @@
|
||||
# CORS (Обмін ресурсами між різними джерелами)
|
||||
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">CORS або "Обмін ресурсами між різними джерелами"</a> є ситуація, коли фронтенд, що працює в браузері, містить JavaScript-код, який взаємодіє з бекендом, розташованим в іншому "джерелі" (origin).
|
||||
|
||||
## Джерело (Origin)
|
||||
|
||||
Джерело визначається комбінацією протоколу (`http`, `https`), домену (`myapp.com`, `localhost`, `localhost.tiangolo.com`), порту (`80`, `443`, `8080`).
|
||||
|
||||
|
||||
Наприклад, такі адреси вважаються різними джерелами:
|
||||
|
||||
* `http://localhost`
|
||||
* `https://localhost`
|
||||
* `http://localhost:8080`
|
||||
|
||||
Навіть якщо вони всі містять `localhost`, вони мають різні протоколи або порти, що робить їх окремими "джерелами".
|
||||
|
||||
## Кроки
|
||||
|
||||
Припустимо, що Ваш фронтенд працює в браузері на `http://localhost:8080`, а його JavaScript намагається відправити запит до бекенду, який працює на `http://localhost` (Оскільки ми не вказуємо порт, браузер за замовчуванням припускає порт `80`).
|
||||
|
||||
Потім браузер надішле HTTP-запит `OPTIONS` до бекенду на порту `:80`, і якщо бекенд надішле відповідні заголовки, що дозволяють комунікацію з цього іншого джерела (`http://localhost:8080`), тоді браузер на порту `:8080` дозволить JavaScript у фронтенді надіслати свій запит до бекенду на порту `:80`.
|
||||
|
||||
Щоб досягти цього, бекенд на порту `:80` повинен мати список "дозволених джерел".
|
||||
|
||||
У цьому випадку список має містити `http://localhost:8080`, щоб фронтенд на порту `:8080` працював коректно.
|
||||
|
||||
## Символьне підставляння
|
||||
|
||||
Можна також оголосити список як `"*"` ("символьне підставляння"), що означає дозвіл для всіх джерел.
|
||||
|
||||
Однак це дозволить лише певні типи комунікації, виключаючи все, що пов'язане з обліковими даними: Cookies, заголовки авторизації, такі як ті, що використовуються з Bearer токенами тощо.
|
||||
|
||||
Тому для коректної роботи краще явно вказувати дозволені джерела.
|
||||
|
||||
## Використання `CORSMiddleware`
|
||||
|
||||
Ви можете налаштувати це у Вашому додатку **FastAPI** за допомогою `CORSMiddleware`.
|
||||
|
||||
* Імпортуйте `CORSMiddleware`.
|
||||
* Створіть список дозволених джерел (у вигляді рядків).
|
||||
* Додайте його як "middleware" у Ваш додаток **FastAPI**.
|
||||
|
||||
|
||||
Також можна вказати, чи дозволяє Ваш бекенд:
|
||||
|
||||
* Облікові дані (заголовки авторизації, сookies, тощо).
|
||||
* Конкретні HTTP-методи (`POST`, `PUT`) або всі за допомогою `"*"`
|
||||
* Конкретні HTTP-заголовки або всі за допомогою `"*"`.
|
||||
|
||||
|
||||
{* ../../docs_src/cors/tutorial001.py hl[2,6:11,13:19] *}
|
||||
|
||||
Параметри за замовчуванням у `CORSMiddleware` є досить обмеженими, тому Вам потрібно явно вказати конкретні джерела, методи або заголовки, щоб браузери могли використовувати їх у контексті запитів між різними доменами.
|
||||
|
||||
|
||||
Підтримуються такі аргументи:
|
||||
|
||||
* `allow_origins` - Список джерел, яким дозволено здійснювати міждоменні запити. Наприклад `['https://example.org', 'https://www.example.org']`. Ви можете використовувати ['*'], щоб дозволити всі джерела.
|
||||
* `allow_origin_regex` - Рядок регулярного виразу для відповідності джерелам, яким дозволено здійснювати міждоменні запити. Наприклад, `'https://.*\.example\.org'`.
|
||||
* `allow_methods` - Список HTTP-методів, дозволених для міждоменних запитів. За замовчуванням `['GET']`. Ви можете використовувати `['*']`, щоб дозволити всі стандартні методи.
|
||||
* `allow_headers` - Список HTTP-заголовків, які підтримуються для міждоменних запитів. За замовчуванням `[]`. Ви можете використовувати `['*']`, щоб дозволити всі заголовки. Заголовки `Accept`, `Accept-Language`, `Content-Language` і `Content-Type` завжди дозволені для <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests" class="external-link" rel="noopener" target="_blank">простих CORS-запитів</a>.
|
||||
* `allow_credentials` - Визначає, чи підтримуються файли cookie для міждоменних запитів. За замовчуванням `False`. Також, якщо потрібно дозволити обмін обліковими даними (`allow_credentials = True`), параметр `allow_origins` не може бути встановлений як `['*']`, необхідно вказати конкретні джерела.
|
||||
* `expose_headers` - Вказує, які заголовки відповіді повинні бути доступні для браузера. За замовчуванням `[]`.
|
||||
* `max_age` - Встановлює максимальний час (у секундах) для кешування CORS-відповідей у браузерах. За замовчуванням `600`.
|
||||
|
||||
Цей middleware обробляє два типи HTTP-запитів...
|
||||
|
||||
### Попередні CORS-запити (preflight requests)
|
||||
|
||||
Це будь-які `OPTIONS` - запити, що містять заголовки `Origin` та `Access-Control-Request-Method`.
|
||||
|
||||
У такому випадку middleware перехопить вхідний запит і відповість відповідними CORS-заголовками, повертаючи або `200`, або `400` для інформаційних цілей.
|
||||
|
||||
### Прості запити
|
||||
|
||||
Будь-які запити із заголовком `Origin`. У цьому випадку middleware пропустить запит як звичайний, але додасть відповідні CORS-заголовки у відповідь.
|
||||
|
||||
## Додаткова інформація
|
||||
|
||||
Більше про <abbr title="Cross-Origin Resource Sharing">CORS</abbr> можна дізнатися в <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">документації Mozilla</a>.
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Також можна використовувати `from starlette.middleware.cors import CORSMiddleware`.
|
||||
|
||||
**FastAPI** надає кілька middleware у `fastapi.middleware` для зручності розробників. Але більшість доступних middleware походять безпосередньо зі Starlette.
|
||||
|
||||
///
|
||||
@@ -0,0 +1,112 @@
|
||||
# Налагодження (Debugging)
|
||||
|
||||
Ви можете під'єднати дебагер у Вашому редакторі коду, наприклад, у Visual Studio Code або PyCharm.
|
||||
|
||||
## Виклик `uvicorn`
|
||||
|
||||
У Вашому FastAPI-додатку імпортуйте та запустіть `uvicorn` безпосередньо:
|
||||
|
||||
{* ../../docs_src/debugging/tutorial001.py hl[1,15] *}
|
||||
|
||||
### Про `__name__ == "__main__"`
|
||||
|
||||
Головна мета використання `__name__ == "__main__"` — це забезпечення виконання певного коду тільки тоді, коли файл запускається безпосередньо:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python myapp.py
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
але не виконується при його імпорті в інший файл, наприклад:
|
||||
|
||||
```Python
|
||||
from myapp import app
|
||||
```
|
||||
|
||||
#### Детальніше
|
||||
|
||||
Припустимо, Ваш файл називається `myapp.py`.
|
||||
|
||||
Якщо Ви запустите його так:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python myapp.py
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
тоді внутрішня змінна `__name__`, яка створюється автоматично Python, матиме значення `"__main__"`.
|
||||
|
||||
Отже, цей блок коду:
|
||||
|
||||
```Python
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
```
|
||||
|
||||
буде виконаний.
|
||||
|
||||
---
|
||||
|
||||
Це не станеться, якщо Ви імпортуєте цей модуль (файл).
|
||||
|
||||
Якщо у Вас є інший файл, наприклад `importer.py`, з наступним кодом:
|
||||
|
||||
```Python
|
||||
from myapp import app
|
||||
|
||||
# Додатковий код
|
||||
```
|
||||
|
||||
У цьому випадку автоматично створена змінна у файлі `myapp.py` не матиме значення змінної `__name__` як `"__main__"`.
|
||||
|
||||
Отже, рядок:
|
||||
|
||||
```Python
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
```
|
||||
|
||||
не буде виконано.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Більш детальну інформацію можна знайти в <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">офіційній документації Python</a>.
|
||||
|
||||
///
|
||||
|
||||
## Запуск коду з вашим дебагером
|
||||
|
||||
Оскільки Ви запускаєте сервер Uvicorn безпосередньо з Вашого коду, Ви можете запустити вашу Python програму (ваш FastAPI додаток) безпосередньо з дебагера.
|
||||
|
||||
---
|
||||
|
||||
Наприклад, у Visual Studio Code Ви можете:
|
||||
|
||||
* Перейдіть на вкладку "Debug".
|
||||
* Натисніть "Add configuration...".
|
||||
* Виберіть "Python"
|
||||
* Запустіть дебагер з опцією "`Python: Current File (Integrated Terminal)`".
|
||||
|
||||
Це запустить сервер з Вашим **FastAPI** кодом, зупиниться на точках зупину тощо.
|
||||
|
||||
Ось як це може виглядати:
|
||||
|
||||
<img src="/img/tutorial/debugging/image01.png">
|
||||
|
||||
---
|
||||
Якщо Ви використовуєте PyCharm, ви можете:
|
||||
|
||||
* Відкрити меню "Run".
|
||||
* Вибрати опцію "Debug...".
|
||||
* Потім з'явиться контекстне меню.
|
||||
* Вибрати файл для налагодження (у цьому випадку, `main.py`).
|
||||
|
||||
Це запустить сервер з Вашим **FastAPI** кодом, зупиниться на точках зупину тощо.
|
||||
|
||||
Ось як це може виглядати:
|
||||
|
||||
<img src="/img/tutorial/debugging/image02.png">
|
||||
@@ -0,0 +1,35 @@
|
||||
# JSON Compatible Encoder
|
||||
|
||||
Існують випадки, коли вам може знадобитися перетворити тип даних (наприклад, модель Pydantic) в щось сумісне з JSON (наприклад, `dict`, `list`, і т. д.).
|
||||
|
||||
Наприклад, якщо вам потрібно зберегти це в базі даних.
|
||||
|
||||
Для цього, **FastAPI** надає `jsonable_encoder()` функцію.
|
||||
|
||||
## Використання `jsonable_encoder`
|
||||
|
||||
Давайте уявимо, що у вас є база даних `fake_db`, яка приймає лише дані, сумісні з JSON.
|
||||
|
||||
Наприклад, вона не приймає об'єкти типу `datetime`, оскільки вони не сумісні з JSON.
|
||||
|
||||
Отже, об'єкт типу `datetime` потрібно перетворити в рядок `str`, який містить дані в <a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">ISO форматі</a>.
|
||||
|
||||
Тим самим способом ця база даних не прийматиме об'єкт типу Pydantic model (об'єкт з атрибутами), а лише `dict`.
|
||||
|
||||
Ви можете використовувати `jsonable_encoder` для цього.
|
||||
|
||||
Вона приймає об'єкт, такий як Pydantic model, і повертає його версію, сумісну з JSON:
|
||||
|
||||
{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *}
|
||||
|
||||
У цьому прикладі вона конвертує Pydantic model у `dict`, а `datetime` у `str`.
|
||||
|
||||
Результат виклику цієї функції - це щось, що можна кодувати з використанням стандарту Python <a href="https://docs.python.org/3/library/json.html#json.dumps" class="external-link" target="_blank">`json.dumps()`</a>.
|
||||
|
||||
Вона не повертає велику строку `str`, яка містить дані у форматі JSON (як строка). Вона повертає стандартну структуру даних Python (наприклад `dict`) із значеннями та підзначеннями, які є сумісними з JSON.
|
||||
|
||||
/// note | Примітка
|
||||
|
||||
`jsonable_encoder` фактично використовується **FastAPI** внутрішньо для перетворення даних. Проте вона корисна в багатьох інших сценаріях.
|
||||
|
||||
///
|
||||
@@ -0,0 +1,62 @@
|
||||
# Додаткові типи даних
|
||||
|
||||
До цього часу, ви використовували загальнопоширені типи даних, такі як:
|
||||
|
||||
* `int`
|
||||
* `float`
|
||||
* `str`
|
||||
* `bool`
|
||||
|
||||
Але можна також використовувати більш складні типи даних.
|
||||
|
||||
І ви все ще матимете ті ж можливості, які були показані до цього:
|
||||
|
||||
* Чудова підтримка редактора.
|
||||
* Конвертація даних з вхідних запитів.
|
||||
* Конвертація даних для відповіді.
|
||||
* Валідація даних.
|
||||
* Автоматична анотація та документація.
|
||||
|
||||
## Інші типи даних
|
||||
|
||||
Ось додаткові типи даних для використання:
|
||||
|
||||
* `UUID`:
|
||||
* Стандартний "Універсальний Унікальний Ідентифікатор", який часто використовується як ідентифікатор у багатьох базах даних та системах.
|
||||
* У запитах та відповідях буде представлений як `str`.
|
||||
* `datetime.datetime`:
|
||||
* Пайтонівський `datetime.datetime`.
|
||||
* У запитах та відповідях буде представлений як `str` в форматі ISO 8601, як: `2008-09-15T15:53:00+05:00`.
|
||||
* `datetime.date`:
|
||||
* Пайтонівський `datetime.date`.
|
||||
* У запитах та відповідях буде представлений як `str` в форматі ISO 8601, як: `2008-09-15`.
|
||||
* `datetime.time`:
|
||||
* Пайтонівський `datetime.time`.
|
||||
* У запитах та відповідях буде представлений як `str` в форматі ISO 8601, як: `14:23:55.003`.
|
||||
* `datetime.timedelta`:
|
||||
* Пайтонівський `datetime.timedelta`.
|
||||
* У запитах та відповідях буде представлений як `float` загальної кількості секунд.
|
||||
* Pydantic також дозволяє представляти це як "ISO 8601 time diff encoding", <a href="https://docs.pydantic.dev/latest/concepts/serialization/#json_encoders" class="external-link" target="_blank">більше інформації дивись у документації</a>.
|
||||
* `frozenset`:
|
||||
* У запитах і відповідях це буде оброблено так само, як і `set`:
|
||||
* У запитах список буде зчитано, дублікати будуть видалені та він буде перетворений на `set`.
|
||||
* У відповідях, `set` буде перетворений на `list`.
|
||||
* Згенерована схема буде вказувати, що значення `set` є унікальними (з використанням JSON Schema's `uniqueItems`).
|
||||
* `bytes`:
|
||||
* Стандартний Пайтонівський `bytes`.
|
||||
* У запитах і відповідях це буде оброблено як `str`.
|
||||
* Згенерована схема буде вказувати, що це `str` з "форматом" `binary`.
|
||||
* `Decimal`:
|
||||
* Стандартний Пайтонівський `Decimal`.
|
||||
* У запитах і відповідях це буде оброблено так само, як і `float`.
|
||||
* Ви можете перевірити всі дійсні типи даних Pydantic тут: <a href="https://docs.pydantic.dev/latest/concepts/types/" class="external-link" target="_blank">типи даних Pydantic</a>.
|
||||
|
||||
## Приклад
|
||||
|
||||
Ось приклад *path operation* з параметрами, використовуючи деякі з вищезазначених типів.
|
||||
|
||||
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *}
|
||||
|
||||
Зверніть увагу, що параметри всередині функції мають свій звичайний тип даних, і ви можете, наприклад, виконувати звичайні маніпуляції з датами, такі як:
|
||||
|
||||
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[18:19] *}
|
||||
@@ -0,0 +1,328 @@
|
||||
# Перші кроки
|
||||
|
||||
Найпростіший файл FastAPI може виглядати так:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py *}
|
||||
|
||||
Скопіюйте це до файлу `main.py`.
|
||||
|
||||
Запустіть сервер:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:single">main.py</u>
|
||||
<font color="#3465A4">INFO </font> Using path <font color="#3465A4">main.py</font>
|
||||
<font color="#3465A4">INFO </font> Resolved absolute path <font color="#75507B">/home/user/code/awesomeapp/</font><font color="#AD7FA8">main.py</font>
|
||||
<font color="#3465A4">INFO </font> Searching for package file structure from directories with <font color="#3465A4">__init__.py</font> files
|
||||
<font color="#3465A4">INFO </font> Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
|
||||
|
||||
╭─ <font color="#8AE234"><b>Python module file</b></font> ─╮
|
||||
│ │
|
||||
│ 🐍 main.py │
|
||||
│ │
|
||||
╰──────────────────────╯
|
||||
|
||||
<font color="#3465A4">INFO </font> Importing module <font color="#4E9A06">main</font>
|
||||
<font color="#3465A4">INFO </font> Found importable FastAPI app
|
||||
|
||||
╭─ <font color="#8AE234"><b>Importable FastAPI app</b></font> ─╮
|
||||
│ │
|
||||
│ <span style="background-color:#272822"><font color="#FF4689">from</font></span><span style="background-color:#272822"><font color="#F8F8F2"> main </font></span><span style="background-color:#272822"><font color="#FF4689">import</font></span><span style="background-color:#272822"><font color="#F8F8F2"> app</font></span><span style="background-color:#272822"> </span> │
|
||||
│ │
|
||||
╰──────────────────────────╯
|
||||
|
||||
<font color="#3465A4">INFO </font> Using import string <font color="#8AE234"><b>main:app</b></font>
|
||||
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">╭────────── FastAPI CLI - Development mode ───────────╮</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ Serving at: http://127.0.0.1:8000 │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ API docs: http://127.0.0.1:8000/docs │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ Running in development mode, for production use: │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ </font></span><span style="background-color:#C4A000"><font color="#555753"><b>fastapi run</b></font></span><span style="background-color:#C4A000"><font color="#2E3436"> │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">╰─────────────────────────────────────────────────────╯</font></span>
|
||||
|
||||
<font color="#4E9A06">INFO</font>: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
|
||||
<font color="#4E9A06">INFO</font>: Uvicorn running on <b>http://127.0.0.1:8000</b> (Press CTRL+C to quit)
|
||||
<font color="#4E9A06">INFO</font>: Started reloader process [<font color="#34E2E2"><b>2265862</b></font>] using <font color="#34E2E2"><b>WatchFiles</b></font>
|
||||
<font color="#4E9A06">INFO</font>: Started server process [<font color="#06989A">2265873</font>]
|
||||
<font color="#4E9A06">INFO</font>: Waiting for application startup.
|
||||
<font color="#4E9A06">INFO</font>: Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
У консолі буде рядок приблизно такого змісту:
|
||||
|
||||
```hl_lines="4"
|
||||
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
Цей рядок показує URL, за яким додаток запускається на вашій локальній машині.
|
||||
|
||||
### Перевірте
|
||||
|
||||
Відкрийте браузер та введіть адресу <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
|
||||
|
||||
Ви побачите у відповідь таке повідомлення у форматі JSON:
|
||||
|
||||
```JSON
|
||||
{"message": "Hello World"}
|
||||
```
|
||||
|
||||
### Інтерактивна API документація
|
||||
|
||||
Перейдемо сюди <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
Ви побачите автоматичну інтерактивну API документацію (створену завдяки <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
|
||||
|
||||

|
||||
|
||||
### Альтернативна API документація
|
||||
|
||||
Тепер перейдемо сюди <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
|
||||
Ви побачите альтернативну автоматичну документацію (створену завдяки <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
|
||||
|
||||

|
||||
|
||||
### OpenAPI
|
||||
|
||||
**FastAPI** генерує "схему" з усім вашим API, використовуючи стандарт **OpenAPI** для визначення API.
|
||||
|
||||
#### "Схема"
|
||||
|
||||
"Схема" - це визначення або опис чогось. Це не код, який його реалізує, а просто абстрактний опис.
|
||||
|
||||
#### API "схема"
|
||||
|
||||
У цьому випадку, <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> є специфікацією, яка визначає, як описати схему вашого API.
|
||||
|
||||
Це визначення схеми включає шляхи (paths) вашого API, можливі параметри, які вони приймають тощо.
|
||||
|
||||
#### "Схема" даних
|
||||
|
||||
Термін "схема" також може відноситися до структури даних, наприклад, JSON.
|
||||
|
||||
У цьому випадку це означає - атрибути JSON і типи даних, які вони мають тощо.
|
||||
|
||||
#### OpenAPI і JSON Schema
|
||||
|
||||
OpenAPI описує схему для вашого API. І ця схема включає визначення (або "схеми") даних, що надсилаються та отримуються вашим API за допомогою **JSON Schema**, стандарту для схем даних JSON.
|
||||
|
||||
#### Розглянемо `openapi.json`
|
||||
|
||||
Якщо вас цікавить, як виглядає вихідна схема OpenAPI, то FastAPI автоматично генерує JSON-схему з усіма описами API.
|
||||
|
||||
Ознайомитися можна за посиланням: <a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a>.
|
||||
|
||||
Ви побачите приблизно такий JSON:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "FastAPI",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
|
||||
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
#### Для чого потрібний OpenAPI
|
||||
|
||||
Схема OpenAPI є основою для обох систем інтерактивної документації.
|
||||
|
||||
Існують десятки альтернативних інструментів, заснованих на OpenAPI. Ви можете легко додати будь-який з них до **FastAPI** додатку.
|
||||
|
||||
Ви також можете використовувати OpenAPI для автоматичної генерації коду для клієнтів, які взаємодіють з API. Наприклад, для фронтенд-, мобільних або IoT-додатків
|
||||
|
||||
## А тепер крок за кроком
|
||||
|
||||
### Крок 1: імпортуємо `FastAPI`
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[1] *}
|
||||
|
||||
`FastAPI` це клас у Python, який надає всю функціональність для API.
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
`FastAPI` це клас, який успадковується безпосередньо від `Starlette`.
|
||||
|
||||
Ви також можете використовувати всю функціональність <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> у `FastAPI`.
|
||||
|
||||
///
|
||||
|
||||
### Крок 2: створюємо екземпляр `FastAPI`
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[3] *}
|
||||
Змінна `app` є екземпляром класу `FastAPI`.
|
||||
|
||||
Це буде головна точка для створення і взаємодії з API.
|
||||
|
||||
### Крок 3: визначте операцію шляху (path operation)
|
||||
|
||||
#### Шлях (path)
|
||||
|
||||
"Шлях" це частина URL, яка йде одразу після символу `/`.
|
||||
|
||||
Отже, у такому URL, як:
|
||||
|
||||
```
|
||||
https://example.com/items/foo
|
||||
```
|
||||
|
||||
...шлях буде:
|
||||
|
||||
```
|
||||
/items/foo
|
||||
```
|
||||
|
||||
/// info | Додаткова інформація
|
||||
|
||||
"Шлях" (path) також зазвичай називають "ендпоінтом" (endpoint) або "маршрутом" (route).
|
||||
|
||||
///
|
||||
|
||||
При створенні API, "шлях" є основним способом розділення "завдань" і "ресурсів".
|
||||
#### Operation
|
||||
|
||||
"Операція" (operation) тут означає один з "методів" HTTP.
|
||||
|
||||
Один з:
|
||||
|
||||
* `POST`
|
||||
* `GET`
|
||||
* `PUT`
|
||||
* `DELETE`
|
||||
|
||||
...та більш екзотичних:
|
||||
|
||||
* `OPTIONS`
|
||||
* `HEAD`
|
||||
* `PATCH`
|
||||
* `TRACE`
|
||||
|
||||
У HTTP-протоколі можна спілкуватися з кожним шляхом, використовуючи один (або кілька) з цих "методів".
|
||||
|
||||
---
|
||||
|
||||
При створенні API зазвичай використовуються конкретні методи HTTP для виконання певних дій.
|
||||
|
||||
Як правило, використовують:
|
||||
|
||||
* `POST`: для створення даних.
|
||||
* `GET`: для читання даних.
|
||||
* `PUT`: для оновлення даних.
|
||||
* `DELETE`: для видалення даних.
|
||||
|
||||
В OpenAPI кожен HTTP метод називається "операція".
|
||||
|
||||
Ми також будемо дотримуватися цього терміна.
|
||||
|
||||
#### Визначте декоратор операції шляху (path operation decorator)
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[6] *}
|
||||
Декоратор `@app.get("/")` вказує **FastAPI**, що функція нижче, відповідає за обробку запитів, які надходять до неї:
|
||||
|
||||
* шлях `/`
|
||||
* використовуючи <abbr title="an HTTP GET method"><code>get</code> операцію</abbr>
|
||||
|
||||
/// info | `@decorator` Додаткова інформація
|
||||
|
||||
Синтаксис `@something` у Python називається "декоратором".
|
||||
|
||||
Ви розташовуєте його над функцією. Як гарний декоративний капелюх (мабуть, звідти походить термін).
|
||||
|
||||
"Декоратор" приймає функцію нижче і виконує з нею якусь дію.
|
||||
|
||||
У нашому випадку, цей декоратор повідомляє **FastAPI**, що функція нижче відповідає **шляху** `/` і **операції** `get`.
|
||||
|
||||
Це і є "декоратор операції шляху (path operation decorator)".
|
||||
|
||||
///
|
||||
|
||||
Можна також використовувати операції:
|
||||
|
||||
* `@app.post()`
|
||||
* `@app.put()`
|
||||
* `@app.delete()`
|
||||
|
||||
І більш екзотичні:
|
||||
|
||||
* `@app.options()`
|
||||
* `@app.head()`
|
||||
* `@app.patch()`
|
||||
* `@app.trace()`
|
||||
|
||||
/// tip | Порада
|
||||
|
||||
Ви можете використовувати кожну операцію (HTTP-метод) на свій розсуд.
|
||||
|
||||
**FastAPI** не нав'язує жодного певного значення для кожного методу.
|
||||
|
||||
Наведена тут інформація є рекомендацією, а не обов'язковою вимогою.
|
||||
|
||||
Наприклад, під час використання GraphQL зазвичай усі дії виконуються тільки за допомогою `POST` операцій.
|
||||
|
||||
///
|
||||
|
||||
### Крок 4: визначте **функцію операції шляху (path operation function)**
|
||||
|
||||
Ось "**функція операції шляху**":
|
||||
|
||||
* **шлях**: це `/`.
|
||||
* **операція**: це `get`.
|
||||
* **функція**: це функція, яка знаходиться нижче "декоратора" (нижче `@app.get("/")`).
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
|
||||
|
||||
Це звичайна функція Python.
|
||||
|
||||
FastAPI викликатиме її щоразу, коли отримає запит до URL із шляхом "/", використовуючи операцію `GET`.
|
||||
|
||||
У даному випадку це асинхронна функція.
|
||||
|
||||
---
|
||||
|
||||
Ви також можете визначити її як звичайну функцію замість `async def`:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
|
||||
|
||||
/// note | Примітка
|
||||
|
||||
Якщо не знаєте в чому різниця, подивіться [Конкурентність: *"Поспішаєш?"*](../async.md#in-a-hurry){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
### Крок 5: поверніть результат
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
|
||||
|
||||
Ви можете повернути `dict`, `list`, а також окремі значення `str`, `int`, ітд.
|
||||
|
||||
Також можна повернути моделі Pydantic (про це ви дізнаєтесь пізніше).
|
||||
|
||||
Існує багато інших об'єктів і моделей, які будуть автоматично конвертовані в JSON (зокрема ORM тощо). Спробуйте використати свої улюблені, велика ймовірність, що вони вже підтримуються.
|
||||
|
||||
## Підіб'ємо підсумки
|
||||
|
||||
* Імпортуємо `FastAPI`.
|
||||
* Створюємо екземпляр `app`.
|
||||
* Пишемо **декоратор операції шляху** як `@app.get("/")`.
|
||||
* Пишемо **функцію операції шляху**; наприклад, `def root(): ...`.
|
||||
* Запускаємо сервер у режимі розробки `fastapi dev`.
|
||||
@@ -0,0 +1,255 @@
|
||||
# Обробка Помилок
|
||||
|
||||
Є багато ситуацій, коли потрібно повідомити клієнта, який використовує Ваш API, про помилку.
|
||||
|
||||
Цим клієнтом може бути браузер із фронтендом, код іншого розробника, IoT-пристрій тощо.
|
||||
|
||||
Можливо, Вам потрібно повідомити клієнта, що:
|
||||
|
||||
* У нього недостатньо прав для виконання цієї операції.
|
||||
* Він не має доступу до цього ресурсу.
|
||||
* Елемент, до якого він намагається отримати доступ, не існує.
|
||||
* тощо.
|
||||
|
||||
У таких випадках зазвичай повертається **HTTP статус-код** в діапазоні **400** (від 400 до 499).
|
||||
|
||||
Це схоже на HTTP статус-коди 200 (від 200 до 299). Ці "200" статус-коди означають, що запит пройшов успішно.
|
||||
|
||||
Статус-коди в діапазоні 400 означають, що сталася помилка з боку клієнта.
|
||||
|
||||
Пам'ятаєте всі ці помилки **404 Not Found** (і жарти про них)?
|
||||
|
||||
## Використання `HTTPException`
|
||||
|
||||
Щоб повернути HTTP-відповіді з помилками клієнту, використовуйте `HTTPException`.
|
||||
|
||||
### Імпорт `HTTPException`
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial001.py hl[1] *}
|
||||
|
||||
### Використання `HTTPException` у коді
|
||||
|
||||
`HTTPException` — це звичайна помилка Python із додатковими даними, які стосуються API.
|
||||
|
||||
Оскільки це помилка Python, Ви не `повертаєте` його, а `генеруєте` (генеруєте помилку).
|
||||
|
||||
Це також означає, що якщо Ви перебуваєте всередині допоміжної функції, яку викликаєте всередині своєї *функції операції шляху*, і там генеруєте `HTTPException`, всередині цієї допоміжної функції, то решта коду в *функції операції шляху* не буде виконана. Запит одразу завершиться, і HTTP-помилка з `HTTPException` буде надіслана клієнту.
|
||||
|
||||
Перевага використання `генерації` (raise) помилки замість `повернення` значення (return) стане більш очевидним в розділі про Залежності та Безпеку.
|
||||
|
||||
У цьому прикладі, якщо клієнт запитує елемент за ID, якого не існує, буде згенеровано помилку зі статус-кодом `404`:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial001.py hl[11] *}
|
||||
|
||||
### Отримана відповідь
|
||||
|
||||
Якщо клієнт робить запит за шляхом `http://example.com/items/foo` (де `item_id` `"foo"`), він отримає статус-код 200 і JSON відповідь:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"item": "The Foo Wrestlers"
|
||||
}
|
||||
```
|
||||
|
||||
Але якщо клієнт робить запит на `http://example.com/items/bar` (де `item_id` має не існуюче значення `"bar"`), то отримає статус-код 404 (помилка "не знайдено") та відповідь:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": "Item not found"
|
||||
}
|
||||
```
|
||||
|
||||
/// tip | Порада
|
||||
|
||||
Під час виклику `HTTPException` Ви можете передати будь-яке значення, яке може бути перетворене в JSON, як параметр `detail`, а не лише рядок (`str`).
|
||||
|
||||
Ви можете передати `dict`, `list` тощо.
|
||||
|
||||
Вони обробляються автоматично за допомогою **FastAPI** та перетворюються в JSON.
|
||||
|
||||
///
|
||||
|
||||
## Додавання власних заголовків
|
||||
|
||||
Іноді потрібно додати власні заголовки до HTTP-помилки, наприклад, для певних типів безпеки.
|
||||
|
||||
Ймовірно, Вам не доведеться використовувати це безпосередньо у своєму коді.
|
||||
|
||||
Але якщо Вам знадобиться це для складного сценарію, Ви можете додати власні заголовки:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial002.py hl[14] *}
|
||||
|
||||
## Встановлення власних обробників помилок
|
||||
|
||||
Ви можете додати власні обробники помилок за допомогою <a href="https://www.starlette.dev/exceptions/" class="external-link" target="_blank">тих самих утиліт обробки помилок зі Starlette</a>.
|
||||
|
||||
Припустимо, у Вас є власний обʼєкт помилки `UnicornException`, яке Ви (або бібліотека, яку Ви використовуєте) може `згенерувати` (`raise`).
|
||||
|
||||
І Ви хочете обробляти це виключення глобально за допомогою FastAPI.
|
||||
|
||||
Ви можете додати власний обробник виключень за допомогою `@app.exception_handler()`:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial003.py hl[5:7,13:18,24] *}
|
||||
|
||||
Тут, якщо Ви звернетеся до `/unicorns/yolo`, то згенерується помилка `UnicornException`.
|
||||
|
||||
Але вона буде оброблена функцією-обробником `unicorn_exception_handler`.
|
||||
|
||||
Отже, Ви отримаєте зрозумілу помилку зі HTTP-статусом `418` і JSON-відповіддю:
|
||||
|
||||
```JSON
|
||||
{"message": "Oops! yolo did something. There goes a rainbow..."}
|
||||
```
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Ви також можете використовувати `from starlette.requests import Request` і `from starlette.responses import JSONResponse`.
|
||||
|
||||
**FastAPI** надає ті самі `starlette.responses`, що й `fastapi.responses`, просто для зручності розробника. Але більшість доступних відповідей надходять безпосередньо зі Starlette. Те ж саме стосується і `Request`.
|
||||
|
||||
///
|
||||
|
||||
## Перевизначення обробників помилок за замовчуванням
|
||||
|
||||
**FastAPI** має кілька обробників помилок за замовчуванням.
|
||||
|
||||
Ці обробники відповідають за повернення стандартних JSON-відповідей, коли Ви `генеруєте` (`raise`) `HTTPException`, а також коли запит містить некоректні дані.
|
||||
|
||||
Ви можете перевизначити ці обробники, створивши власні.
|
||||
|
||||
### Перевизначення помилок валідації запиту
|
||||
|
||||
Коли запит містить некоректні дані, **FastAPI** генерує `RequestValidationError`.
|
||||
|
||||
І також включає обробник помилок за замовчуванням для нього.
|
||||
|
||||
Щоб перевизначити його, імпортуйте `RequestValidationError` і використовуйте його з `@app.exception_handler(RequestValidationError)` для декорування обробника помилок.
|
||||
|
||||
Обробник помилок отримує `Request` і саму помилку.
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial004.py hl[2,14:16] *}
|
||||
|
||||
Тепер, якщо Ви перейдете за посиланням `/items/foo`, замість того, щоб отримати стандартну JSON-помилку:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"loc": [
|
||||
"path",
|
||||
"item_id"
|
||||
],
|
||||
"msg": "value is not a valid integer",
|
||||
"type": "type_error.integer"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Ви отримаєте текстову версію:
|
||||
|
||||
```
|
||||
1 validation error
|
||||
path -> item_id
|
||||
value is not a valid integer (type=type_error.integer)
|
||||
```
|
||||
|
||||
#### `RequestValidationError` проти `ValidationError`
|
||||
|
||||
/// warning | Увага
|
||||
|
||||
Це технічні деталі, які Ви можете пропустити, якщо вони зараз не важливі для Вас.
|
||||
|
||||
///
|
||||
|
||||
`RequestValidationError` є підкласом Pydantic <a href="https://docs.pydantic.dev/latest/concepts/models/#error-handling" class="external-link" target="_blank">`ValidationError`</a>.
|
||||
|
||||
**FastAPI** використовує його для того, якщо Ви використовуєте модель Pydantic у `response_model` і у ваших даних є помилка, Ви побачили помилку у своєму журналі.
|
||||
|
||||
Але клієнт/користувач не побачить її. Натомість клієнт отримає "Internal Server Error" зі статусом HTTP `500`.
|
||||
|
||||
Так має бути, якщо у Вас виникла `ValidationError` Pydantic у *відповіді* або деінде у вашому коді (не у *запиті* клієнта), це насправді є помилкою у Вашому коді.
|
||||
|
||||
І поки Ви її виправляєте, клієнти/користувачі не повинні мати доступу до внутрішньої інформації про помилку, оскільки це може призвести до вразливості безпеки.
|
||||
|
||||
### Перевизначення обробника помилок `HTTPException`
|
||||
|
||||
Аналогічно, Ви можете перевизначити обробник `HTTPException`.
|
||||
|
||||
Наприклад, Ви можете захотіти повернути текстову відповідь замість JSON для цих помилок:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial004.py hl[3:4,9:11,22] *}
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Ви також можете використовувати `from starlette.responses import PlainTextResponse`.
|
||||
|
||||
**FastAPI** надає ті самі `starlette.responses`, що й `fastapi.responses`, просто для зручності розробника. Але більшість доступних відповідей надходять безпосередньо зі Starlette.
|
||||
|
||||
///
|
||||
|
||||
### Використання тіла `RequestValidationError`
|
||||
|
||||
`RequestValidationError` містить `body`, який він отримав із некоректними даними.
|
||||
|
||||
Ви можете використовувати це під час розробки свого додатка, щоб логувати тіло запиту та налагоджувати його, повертати користувачеві тощо.
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial005.py hl[14] *}
|
||||
|
||||
Тепер спробуйте надіслати некоректний елемент, наприклад:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"title": "towel",
|
||||
"size": "XL"
|
||||
}
|
||||
```
|
||||
Ви отримаєте відповідь, яка повідомить Вам, які саме дані є некоректні у вашому тілі запиту:
|
||||
|
||||
|
||||
```JSON hl_lines="12-15"
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"loc": [
|
||||
"body",
|
||||
"size"
|
||||
],
|
||||
"msg": "value is not a valid integer",
|
||||
"type": "type_error.integer"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"title": "towel",
|
||||
"size": "XL"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `HTTPException` FastAPI проти `HTTPException` Starlette
|
||||
|
||||
**FastAPI** має власний `HTTPException`.
|
||||
|
||||
І клас помилки `HTTPException` в **FastAPI** успадковується від класу помилки `HTTPException` в Starlette.
|
||||
|
||||
Єдина різниця полягає в тому, що `HTTPException` в **FastAPI** приймає будь-які дані, які можна перетворити на JSON, для поля `detail`, тоді як `HTTPException` у Starlette приймає тільки рядки.
|
||||
|
||||
Отже, Ви можете продовжувати використовувати `HTTPException` в **FastAPI** як зазвичай у своєму коді.
|
||||
|
||||
Але коли Ви реєструєте обробник виключень, слід реєструвати його для `HTTPException` зі Starlette.
|
||||
|
||||
Таким чином, якщо будь-яка частина внутрішнього коду Starlette або розширення чи плагін Starlette згенерує (raise) `HTTPException`, Ваш обробник зможе перехопити та обробити її.
|
||||
|
||||
У цьому прикладі, щоб мати можливість використовувати обидва `HTTPException` в одному коді, помилка Starlette перейменовується на `StarletteHTTPException`:
|
||||
|
||||
```Python
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
```
|
||||
|
||||
### Повторне використання обробників помилок **FastAPI**
|
||||
|
||||
Якщо Ви хочете використовувати помилки разом із такими ж обробниками помилок за замовчуванням, як у **FastAPI**, Ви можете імпортувати та повторно використовувати їх із `fastapi.exception_handlers`:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial006.py hl[2:5,15,21] *}
|
||||
|
||||
У цьому прикладі Ви просто використовуєте `print` для виведення дуже інформативного повідомлення, але Ви зрозуміли основну ідею. Ви можете обробити помилку та повторно використовувати обробники помилок за замовчуванням.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Моделі Параметрів Заголовків
|
||||
|
||||
Якщо у Вас є група пов’язаних параметрів заголовків, Ви можете створити **Pydantic модель** для їх оголошення.
|
||||
|
||||
Це дозволить Вам повторно **використовувати модель** в **різних місцях**, а також оголосити валідації та метадані для всіх параметрів одночасно. 😎
|
||||
|
||||
/// note | Нотатки
|
||||
|
||||
Ця можливість підтримується починаючи з версії FastAPI `0.115.0`. 🤓
|
||||
|
||||
///
|
||||
|
||||
## Параметри Заголовків з Використанням Pydantic Model
|
||||
|
||||
Оголосіть потрібні **параметри заголовків** у **Pydantic моделі**, а потім оголосіть параметр як `Header`:
|
||||
|
||||
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *}
|
||||
|
||||
FastAPI буде витягувати дані для кожного поля з заголовків у запиті та передавати їх у створену Вами Pydantic модель.
|
||||
|
||||
**FastAPI** буде **витягувати** дані для **кожного поля** з **заголовків** у запиті та передавати їх у створену Вами Pydantic модель.
|
||||
|
||||
## Перевірка в Документації
|
||||
|
||||
Ви можете побачити необхідні заголовки в інтерфейсі документації за адресою `/docs`:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/header-param-models/image01.png">
|
||||
</div>
|
||||
|
||||
## Заборона Додаткових Заголовків
|
||||
|
||||
У деяких особливих випадках (ймовірно, не дуже поширених) Ви можете захотіти **обмежити** заголовки, які хочете отримати.
|
||||
|
||||
Ви можете використати конфігурацію моделі Pydantic, щоб `заборонити` будь-які `додаткові` поля:
|
||||
|
||||
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *}
|
||||
|
||||
Якщо клієнт спробує надіслати **додаткові заголовки**, він отримає **помилку** у відповіді.
|
||||
|
||||
Наприклад, якщо клієнт спробує надіслати заголовок `tool` зі значенням `plumbus`, він отримає **помилку** з повідомленням про те, що параметр заголовка `tool` не дозволений:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "extra_forbidden",
|
||||
"loc": ["header", "tool"],
|
||||
"msg": "Extra inputs are not permitted",
|
||||
"input": "plumbus",
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Підсумок
|
||||
|
||||
Ви можете використовувати **Pydantic моделі** для оголошення **заголовків** у **FastAPI**. 😎
|
||||
@@ -0,0 +1,91 @@
|
||||
# Header-параметри
|
||||
|
||||
Ви можете визначати параметри заголовків, так само як визначаєте `Query`, `Path` і `Cookie` параметри.
|
||||
|
||||
## Імпорт `Header`
|
||||
|
||||
Спочатку імпортуйте `Header`:
|
||||
|
||||
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## Оголошення параметрів `Header`
|
||||
|
||||
Потім оголосіть параметри заголовків, використовуючи ту ж структуру, що й для `Path`, `Query` та `Cookie`.
|
||||
|
||||
Ви можете визначити значення за замовчуванням, а також усі додаткові параметри валідації або анотації:
|
||||
|
||||
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[9] *}
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
`Header`є "сестринським" класом для `Path`, `Query` і `Cookie`. Він також успадковується від загального класу `Param`.
|
||||
|
||||
Але пам’ятайте, що при імпорті `Query`, `Path`, `Header` та інших із `fastapi`, то насправді вони є функціями, які повертають спеціальні класи.
|
||||
|
||||
///
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Щоб оголосити заголовки, потрібно використовувати `Header`, інакше параметри будуть інтерпретуватися як параметри запиту.
|
||||
|
||||
///
|
||||
|
||||
## Автоматичне перетворення
|
||||
|
||||
`Header` має додатковий функціонал порівняно з `Path`, `Query` та `Cookie`.
|
||||
|
||||
Більшість стандартних заголовків розділяються символом «дефіс», також відомим як «мінус» (`-`).
|
||||
|
||||
Але змінна, така як `user-agent`, є недійсною в Python.
|
||||
|
||||
Тому, за замовчуванням, `Header` автоматично перетворює символи підкреслення (`_`) на дефіси (`-`) для отримання та документування заголовків.
|
||||
|
||||
Оскільки заголовки HTTP не чутливі до регістру, Ви можете використовувати стандартний стиль Python ("snake_case").
|
||||
|
||||
Тому Ви можете використовувати `user_agent`, як зазвичай у коді Python, замість того щоб писати з великої літери, як `User_Agent` або щось подібне.
|
||||
|
||||
Якщо Вам потрібно вимкнути автоматичне перетворення підкреслень у дефіси, встановіть `convert_underscores` в `Header` значення `False`:
|
||||
|
||||
{* ../../docs_src/header_params/tutorial002_an_py310.py hl[10] *}
|
||||
|
||||
/// warning | Увага
|
||||
|
||||
Перед тим як встановити значення `False` для `convert_underscores` пам’ятайте, що деякі HTTP-проксі та сервери не підтримують заголовки з підкресленнями.
|
||||
|
||||
///
|
||||
|
||||
## Дубльовані заголовки
|
||||
|
||||
Можливо отримати дубльовані заголовки, тобто той самий заголовок із кількома значеннями.
|
||||
|
||||
Це можна визначити, використовуючи список у типізації параметра.
|
||||
|
||||
Ви отримаєте всі значення дубльованого заголовка у вигляді `list` у Python.
|
||||
|
||||
Наприклад, щоб оголосити заголовок `X-Token`, який може з’являтися більше ніж один раз:
|
||||
|
||||
{* ../../docs_src/header_params/tutorial003_an_py310.py hl[9] *}
|
||||
|
||||
Якщо Ви взаємодієте з цією операцією шляху, надсилаючи два HTTP-заголовки, наприклад:
|
||||
|
||||
```
|
||||
X-Token: foo
|
||||
X-Token: bar
|
||||
```
|
||||
|
||||
Відповідь буде така:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"X-Token values": [
|
||||
"bar",
|
||||
"foo"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Підсумок
|
||||
|
||||
Оголошуйте заголовки за допомогою `Header`, використовуючи той самий підхід, що й для `Query`, `Path` та `Cookie`.
|
||||
|
||||
Не хвилюйтеся про підкреслення у змінних — **FastAPI** автоматично конвертує їх.
|
||||
@@ -0,0 +1,83 @@
|
||||
# Туторіал - Посібник користувача
|
||||
|
||||
У цьому посібнику показано, як користуватися **FastAPI** з більшістю його функцій, крок за кроком.
|
||||
|
||||
Кожен розділ поступово надбудовується на попередні, але він структурований на окремі теми, щоб ви могли перейти безпосередньо до будь-якої конкретної, щоб вирішити ваші конкретні потреби API.
|
||||
|
||||
Він також створений як довідник для роботи у майбутньому.
|
||||
|
||||
Тож ви можете повернутися і побачити саме те, що вам потрібно.
|
||||
|
||||
## Запустіть код
|
||||
|
||||
Усі блоки коду можна скопіювати та використовувати безпосередньо (це фактично перевірені файли Python).
|
||||
|
||||
Щоб запустити будь-який із прикладів, скопіюйте код у файл `main.py` і запустіть `uvicorn` за допомогою:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
<span style="color: green;">INFO</span>: Started reloader process [28720]
|
||||
<span style="color: green;">INFO</span>: Started server process [28722]
|
||||
<span style="color: green;">INFO</span>: Waiting for application startup.
|
||||
<span style="color: green;">INFO</span>: Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
**ДУЖЕ радимо** написати або скопіювати код, відредагувати його та запустити локально.
|
||||
|
||||
Використання його у своєму редакторі – це те, що дійсно показує вам переваги FastAPI, бачите, як мало коду вам потрібно написати, всі перевірки типів, автозаповнення тощо.
|
||||
|
||||
---
|
||||
|
||||
## Встановлення FastAPI
|
||||
|
||||
Першим кроком є встановлення FastAPI.
|
||||
|
||||
Для туторіалу ви можете встановити його з усіма необов’язковими залежностями та функціями:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "fastapi[all]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
...який також включає `uvicorn`, який ви можете використовувати як сервер, який запускає ваш код.
|
||||
|
||||
/// note
|
||||
|
||||
Ви також можете встановити його частина за частиною.
|
||||
|
||||
Це те, що ви, ймовірно, зробили б, коли захочете розгорнути свою програму у виробничому середовищі:
|
||||
|
||||
```
|
||||
pip install fastapi
|
||||
```
|
||||
|
||||
Також встановіть `uvicorn`, щоб він працював як сервер:
|
||||
|
||||
```
|
||||
pip install "uvicorn[standard]"
|
||||
```
|
||||
|
||||
І те саме для кожної з опціональних залежностей, які ви хочете використовувати.
|
||||
|
||||
///
|
||||
|
||||
## Розширений посібник користувача
|
||||
|
||||
Існує також **Розширений посібник користувача**, який ви зможете прочитати пізніше після цього **Туторіал - Посібник користувача**.
|
||||
|
||||
**Розширений посібник користувача** засновано на цьому, використовує ті самі концепції та навчає вас деяким додатковим функціям.
|
||||
|
||||
Але вам слід спочатку прочитати **Туторіал - Посібник користувача** (те, що ви зараз читаєте).
|
||||
|
||||
Він розроблений таким чином, що ви можете створити повну програму лише за допомогою **Туторіал - Посібник користувача**, а потім розширити її різними способами, залежно від ваших потреб, використовуючи деякі з додаткових ідей з **Розширеного посібника користувача** .
|
||||
@@ -0,0 +1,120 @@
|
||||
# Метадані та URL-адреси документації
|
||||
|
||||
Ви можете налаштувати кілька конфігурацій метаданих у Вашому додатку **FastAPI**.
|
||||
|
||||
## Метадані для API
|
||||
|
||||
Ви можете встановити такі поля, які використовуються в специфікації OpenAPI та в автоматично згенерованих інтерфейсах документації API:
|
||||
|
||||
| Параметр | Тип | Опис |
|
||||
|------------|------|-------------|
|
||||
| `title` | `str` | Назва API. |
|
||||
| `summary` | `str` | Короткий опис API. <small>Доступно з OpenAPI 3.1.0, FastAPI 0.99.0.</small> |
|
||||
| `description` | `str` | Більш детальний опис API. Може використовувати Markdown. |
|
||||
| `version` | `string` | Версія API. Це версія Вашого додатка, а не OpenAPI. Наприклад, `2.5.0`. |
|
||||
| `terms_of_service` | `str` | URL до умов використання API. Якщо вказано, має бути у форматі URL. |
|
||||
| `contact` | `dict` | Інформація для контакту з API. Може містити кілька полів. <details><summary><code>contact</code> поля</summary><table><thead><tr><th>Параметр</th><th>Тип</th><th>Опис</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td>Ім'я контактної особи або організації.</td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>URL з інформацією для контакту. Повинен бути у форматі URL.</td></tr><tr><td><code>email</code></td><td><code>str</code></td><td>Email контактної особи або організації. Повинен бути у форматі електронної пошти.</td></tr></tbody></table></details> |
|
||||
| `license_info` | `dict` | Інформація про ліцензію для API. Може містити кілька полів. <details><summary><code>license_info</code> поля</summary><table><thead><tr><th>Параметр</th><th>Тип</th><th>Опис</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td><strong>ОБОВ'ЯЗКОВО</strong> (якщо встановлено <code>license_info</code>). Назва ліцензії для API.</td></tr><tr><td><code>identifier</code></td><td><code>str</code></td><td>Ліцензійний вираз за <a href="https://spdx.org/licenses/" class="external-link" target="_blank">SPDX</a> для API. Поле <code>identifier</code> взаємовиключне з полем <code>url</code>. <small>Доступно з OpenAPI 3.1.0, FastAPI 0.99.0.</small></td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>URL до ліцензії, яка використовується для API. Повинен бути у форматі URL.</td></tr></tbody></table></details> |
|
||||
|
||||
Ви можете налаштувати їх наступним чином:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial001.py hl[3:16, 19:32] *}
|
||||
|
||||
/// tip | Підказка
|
||||
|
||||
У полі `description` можна використовувати Markdown, і він буде відображатися у результаті.
|
||||
|
||||
///
|
||||
|
||||
З цією конфігурацією автоматична документація API виглядатиме так:
|
||||
|
||||
<img src="/img/tutorial/metadata/image01.png">
|
||||
|
||||
## Ідентифікатор ліцензії
|
||||
|
||||
З початку використання OpenAPI 3.1.0 та FastAPI 0.99.0 Ви також можете налаштувати `license_info` за допомогою `identifier` замість `url`.
|
||||
|
||||
Наприклад:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial001_1.py hl[31] *}
|
||||
|
||||
## Метадані для тегів
|
||||
|
||||
Ви також можете додати додаткові метадані для різних тегів, які використовуються для групування операцій шляхів, за допомогою параметра `openapi_tags`.
|
||||
|
||||
Він приймає список, який містить один словник для кожного тега.
|
||||
|
||||
Кожен словник може містити:
|
||||
|
||||
* `name` (**обов'язково**): `str` з тією ж назвою тегу, яку Ви використовуєте у параметрі `tags` у Ваших *операціях шляху* та `APIRouter`s.
|
||||
* `description`: `str` з коротким описом тегу. Може містити Markdown і буде відображено в інтерфейсі документації.
|
||||
* `externalDocs`: `dict` який описує зовнішню документацію з такими полями:
|
||||
* `description`: `str` з коротким описом зовнішньої документації.
|
||||
* `url` (**обов'язково**): `str`з URL-адресою зовнішньої документації.
|
||||
|
||||
### Створення метаданих для тегів
|
||||
|
||||
Спробуймо це на прикладі з тегами для `users` та `items`.
|
||||
|
||||
Створіть метадані для своїх тегів і передайте їх у параметр `openapi_tags`:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial004.py hl[3:16,18] *}
|
||||
|
||||
Зверніть увагу, що в описах можна використовувати Markdown, наприклад, "login" буде показано жирним шрифтом (**login**), а "fancy" буде показано курсивом (_fancy_).
|
||||
|
||||
/// tip | Порада
|
||||
|
||||
Не обов'язково додавати метадані для всіх тегів, які Ви використовуєте.
|
||||
|
||||
///
|
||||
|
||||
### Використання тегів
|
||||
|
||||
Використовуйте параметр `tags` зі своїми *операціями шляху* (і `APIRouter`) для призначення їх до різних тегів:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial004.py hl[21,26] *}
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Детальніше про теги читайте в розділі [Конфігурація шляхів операцій](path-operation-configuration.md#tags){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
### Перевірка документації
|
||||
|
||||
Якщо Ви зараз перевірите документацію, вона покаже всі додаткові метадані:
|
||||
|
||||
<img src="/img/tutorial/metadata/image02.png">
|
||||
|
||||
### Порядок тегів
|
||||
|
||||
Порядок кожного словника метаданих тегу також визначає порядок відображення в інтерфейсі документації.
|
||||
|
||||
Наприклад, хоча `users` мав би йти після `items` в алфавітному порядку, він відображається перед ними, оскільки ми додали його метадані як перший словник у списку.
|
||||
|
||||
## URL для OpenAPI
|
||||
|
||||
За замовчуванням схема OpenAPI надається за адресою `/openapi.json`.
|
||||
|
||||
Але Ви можете налаштувати це за допомогою параметра `openapi_url`.
|
||||
|
||||
Наприклад, щоб налаштувати його на `/api/v1/openapi.json`:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial002.py hl[3] *}
|
||||
|
||||
Якщо Ви хочете повністю вимкнути схему OpenAPI, Ви можете встановити `openapi_url=None`, це також вимкне інтерфейси документації, які її використовують.
|
||||
|
||||
## URL-адреси документації
|
||||
|
||||
Ви можете налаштувати два інтерфейси користувача для документації, які включені:
|
||||
|
||||
* **Swagger UI**: доступний за адресою `/docs`.
|
||||
* Ви можете змінити його URL за допомогою параметра `docs_url`.
|
||||
* Ви можете вимкнути його, встановивши `docs_url=None`.
|
||||
* **ReDoc**: доступний за адресою `/redoc`.
|
||||
* Ви можете змінити його URL за допомогою параметра `redoc_url`.
|
||||
* Ви можете вимкнути його, встановивши `redoc_url=None`.
|
||||
|
||||
Наприклад, щоб налаштувати Swagger UI на `/documentation` і вимкнути ReDoc:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial003.py hl[3] *}
|
||||
@@ -0,0 +1,75 @@
|
||||
# Middleware (Проміжний шар)
|
||||
|
||||
У **FastAPI** можна додавати middleware (проміжний шар).
|
||||
|
||||
"Middleware" — це функція, яка працює з кожним **запитом** перед його обробкою будь-якою конкретною *операцією шляху* (*path operation*), а також з кожною **відповіддю** перед її поверненням.
|
||||
|
||||
* Middleware отримує кожен **запит**, що надходить до Вашого застосунку.
|
||||
* Може виконати певні дії із цим **запитом** або запустити необхідний код.
|
||||
* Далі передає **запит** для обробки основним застосунком (*операцією шляху*).
|
||||
* Отримує **відповідь**, сформовану застосунком (*операцією шляху*).
|
||||
* Може змінити цю **відповідь** або виконати додатковий код.
|
||||
* Повертає **відповідь** клієнту.
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Якщо у Вас є залежності з `yield`, код виходу виконається *після* middleware.
|
||||
|
||||
Якщо були заплановані фонові задачі (background tasks - розглянуто далі), вони виконаються *після* всіх middleware.
|
||||
|
||||
///
|
||||
|
||||
## Створення middleware
|
||||
|
||||
Щоб створити middleware, Ви використовуєте декоратор `@app.middleware("http")` на функції.
|
||||
|
||||
Функція middleware отримує:
|
||||
|
||||
* `Запит`.
|
||||
* Функцію `call_next`, яка приймає `запит` як параметр.
|
||||
* Ця функція передає `запит` відповідній *операції шляху*.
|
||||
* Потім вона повертає `відповідь`, згенеровану цією *операцією шляху*.
|
||||
|
||||
* Ви можете ще змінити `відповідь` перед тим, як повернути її.
|
||||
|
||||
|
||||
{* ../../docs_src/middleware/tutorial001.py hl[8:9,11,14] *}
|
||||
|
||||
/// tip | Порада
|
||||
|
||||
Не забувайте, що власні заголовки можна додавати, використовуючи <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">префікс 'X-'</a>.
|
||||
|
||||
Але якщо у Вас є власні заголовки, які Ви хочете, щоб браузерний клієнт міг побачити, потрібно додати їх до Вашої конфігурації CORS (див. [CORS (Обмін ресурсами між різними джерелами)](cors.md){.internal-link target=_blank} за допомогою параметра `expose_headers`, описаного в <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">документації Starlette по CORS</a>.
|
||||
|
||||
///
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Ви також можете використати `from starlette.requests import Request`.
|
||||
|
||||
**FastAPI** надає це для Вашої зручності як розробника. Але він походить безпосередньо зі Starlette.
|
||||
|
||||
///
|
||||
|
||||
### До і після `response`(`відповіді`)
|
||||
|
||||
Ви можете додати код, який буде виконуватися з `запитом` (`request`), до того, як його обробить будь-яка *операція шляху* (*path operation*).
|
||||
|
||||
Також Ви можете додати код, який буде виконуватися після того, як `відповідь` (`response`) буде згенеровано, перед тим як його повернути.
|
||||
|
||||
Наприклад, Ви можете додати власний заголовок `X-Process-Time`, який міститиме час у секундах, який витратився на обробку запиту та генерацію відповіді:
|
||||
|
||||
{* ../../docs_src/middleware/tutorial001.py hl[10,12:13] *}
|
||||
|
||||
|
||||
/// tip | Підказка
|
||||
|
||||
Тут ми використовуємо <a href="https://docs.python.org/3/library/time.html#time.perf_counter" class="external-link" target="_blank">`time.perf_counter()`</a> замість `time.time()` оскільки він може бути більш точним для таких випадків. 🤓
|
||||
|
||||
///
|
||||
|
||||
## Інші middlewares
|
||||
|
||||
Ви можете пізніше прочитати більше про інші middlewares в [Advanced User Guide: Advanced Middleware](../advanced/middleware.md){.internal-link target=_blank}.
|
||||
|
||||
Ви дізнаєтесь, як обробляти <abbr title="Cross-Origin Resource Sharing">CORS</abbr> за допомогою middleware в наступному розділі.
|
||||
@@ -0,0 +1,155 @@
|
||||
# Path Параметри та валідація числових даних
|
||||
|
||||
Так само як Ви можете оголошувати додаткові перевірки та метадані для query параметрів за допомогою `Query`, Ви можете оголошувати той самий тип перевірок і метаданих для параметрів шляху за допомогою `Path`.
|
||||
|
||||
## Імпорт Path
|
||||
|
||||
Спочатку імпортуйте `Path` з `fastapi` і імпортуйте `Annotated`:
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *}
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
FastAPI додав підтримку `Annotated` (і почав рекомендувати його використання) у версії 0.95.0.
|
||||
|
||||
Якщо у Вас стара версія, при спробі використати `Annotated` можуть виникати помилки.
|
||||
|
||||
Переконайтеся, що Ви [оновили версію FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} принаймні до версії 0.95.1 перед використанням `Annotated`.
|
||||
|
||||
///
|
||||
|
||||
## Оголошення метаданих
|
||||
|
||||
Ви можете оголошувати всі ті ж параметри, що і для `Query`.
|
||||
|
||||
Наприклад, щоб оголосити значення метаданих `title` для параметра шляху `item_id`, Ви можете написати:
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *}
|
||||
|
||||
/// note | Примітка
|
||||
|
||||
Параметр шляху завжди є обов’язковим, оскільки він має бути частиною шляху. Навіть якщо Ви оголосите його зі значенням `None` або встановите значення за замовчуванням — він все одно залишатиметься обов’язковим.
|
||||
|
||||
///
|
||||
|
||||
## Упорядковуйте параметри, як Вам потрібно
|
||||
|
||||
/// tip | Підказка
|
||||
|
||||
Це, мабуть, не настільки важливо або необхідно, якщо Ви використовуєте `Annotated`.
|
||||
|
||||
///
|
||||
|
||||
Припустимо, Ви хочете оголосити параметр запиту `q` як обов’язковий `str`.
|
||||
|
||||
І Вам не потрібно оголошувати нічого іншого для цього параметра, тому немає потреби використовувати `Query`.
|
||||
|
||||
Але Вам все одно потрібно використовувати `Path` для параметра шляху `item_id`. І з певних причин Ви не хочете використовувати `Annotated`.
|
||||
|
||||
Python видасть помилку, якщо розмістити значення з "default" перед значенням, яке не має "default".
|
||||
|
||||
Але Ви можете змінити порядок і розмістити значення без значення за замовчуванням (параметр запиту `q`) першим.
|
||||
|
||||
|
||||
Для **FastAPI** порядок не має значення. Він визначає параметри за їх іменами, типами та значеннями за замовчуванням (`Query`, `Path` тощо) і не звертає уваги на порядок.
|
||||
|
||||
Тому Ви можете оголосити Вашу функцію так:
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[7] *}
|
||||
|
||||
Але майте на увазі, що якщо Ви використовуєте `Annotated`, ця проблема не виникне, оскільки Ви не використовуєте значення за замовчуванням для параметрів `Query()` або `Path()`.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *}
|
||||
|
||||
## Упорядковуйте параметри за потребою, хитрощі
|
||||
|
||||
/// tip | Підказка
|
||||
|
||||
Це, мабуть, не настільки важливо або необхідно, якщо Ви використовуєте `Annotated`.
|
||||
|
||||
///
|
||||
|
||||
Ось **невелика хитрість**, яка може стати в пригоді, хоча вона рідко знадобиться.
|
||||
|
||||
Якщо Ви хочете:
|
||||
|
||||
* оголосити параметр запиту `q` без використання `Query` або значення за замовчуванням
|
||||
* оголосити параметр шляху `item_id`, використовуючи `Path`
|
||||
* розмістити їх у різному порядку
|
||||
* не використовувати `Annotated`
|
||||
|
||||
...у Python є спеціальний синтаксис для цього.
|
||||
|
||||
Передайте `*` як перший параметр функції.
|
||||
|
||||
Python нічого не зробить із цією `*`, але розпізнає, що всі наступні параметри слід викликати як аргументи за ключовим словом (пари ключ-значення), також відомі як <abbr title="From: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Навіть якщо вони не мають значення за замовчуванням.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[7] *}
|
||||
|
||||
### Краще з `Annotated`
|
||||
|
||||
Майте на увазі, якщо Ви використовуєте `Annotated`, оскільки Ви не використовуєте значення за замовчуванням для параметрів функції, цієї проблеми не виникне, і, швидше за все, Вам не потрібно буде використовувати `*`.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py hl[10] *}
|
||||
|
||||
## Валідація числових даних: більше або дорівнює
|
||||
|
||||
За допомогою `Query` і `Path` (та інших, які Ви побачите пізніше) можна оголошувати числові обмеження.
|
||||
|
||||
Тут, завдяки `ge=1`, `item_id` має бути цілим числом, яке "`g`reater than or `e`qual" (більше або дорівнює) `1`.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *}
|
||||
|
||||
## Валідація числових даних: більше ніж і менше або дорівнює
|
||||
|
||||
Те саме застосовується до:
|
||||
|
||||
* `gt`: `g`reater `t`han (більше ніж)
|
||||
* `le`: `l`ess than or `e`qual (менше або дорівнює)
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py hl[10] *}
|
||||
|
||||
## Валідація числових даних: float, більше ніж і менше ніж
|
||||
|
||||
Валідація чисел також працює для значень типу `float`.
|
||||
|
||||
Ось де стає важливо мати можливість оголошувати <abbr title="greater than (більше ніж)"><code>gt</code></abbr>, а не тільки <abbr title="greater than or equal (більше або дорівнює)"><code>ge</code></abbr>. Це дозволяє, наприклад, вимагати, щоб значення було більше `0`, навіть якщо воно менше `1`.
|
||||
|
||||
Таким чином, значення `0.5` буде допустимим. Але `0.0` або `0` — ні.
|
||||
|
||||
Те саме стосується <abbr title="less than (менше ніж)"><code>lt</code></abbr>.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py hl[13] *}
|
||||
|
||||
## Підсумок
|
||||
|
||||
За допомогою `Query`, `Path` (і інших параметрів, які Ви ще не бачили) можна оголошувати метадані та перевірки рядків, так само як у [Query параметри та валідація рядків](query-params-str-validations.md){.internal-link target=_blank}.
|
||||
|
||||
Також можна оголошувати числові перевірки:
|
||||
|
||||
* `gt`: `g`reater `t`han (більше ніж)
|
||||
* `ge`: `g`reater than or `e`qual (більше або дорівнює)
|
||||
* `lt`: `l`ess `t`han (менше ніж)
|
||||
* `le`: `l`ess than or `e`qual (менше або дорівнює)
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
`Query`, `Path` та інші класи, які Ви побачите пізніше, є підкласами спільного класу `Param`.
|
||||
|
||||
Всі вони мають однакові параметри для додаткових перевірок і метаданих, які Ви вже бачили.
|
||||
|
||||
///
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Коли Ви імпортуєте `Query`, `Path` та інші з `fastapi`, насправді це функції.
|
||||
|
||||
При виклику вони повертають екземпляри класів з такими ж іменами.
|
||||
|
||||
Тобто Ви імпортуєте `Query`, яка є функцією. А коли Ви її викликаєте, вона повертає екземпляр класу, який теж називається `Query`.
|
||||
|
||||
Ці функції створені таким чином (замість використання класів напряму), щоб Ваш редактор не відзначав їхні типи як помилки.
|
||||
|
||||
Таким чином, Ви можете користуватися своїм звичайним редактором і інструментами для програмування без додаткових налаштувань для ігнорування таких помилок.
|
||||
|
||||
///
|
||||
@@ -0,0 +1,260 @@
|
||||
# Path Параметри
|
||||
|
||||
Ви можете визначити "параметри" або "змінні" шляху, використовуючи синтаксис форматованих рядків:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
|
||||
|
||||
Значення параметра шляху `item_id` передається у функцію як аргумент `item_id`.
|
||||
|
||||
Якщо запустити цей приклад та перейти за посиланням <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, то отримаємо таку відповідь:
|
||||
|
||||
```JSON
|
||||
{"item_id":"foo"}
|
||||
```
|
||||
|
||||
## Path параметри з типами
|
||||
|
||||
Ви можете визначити тип параметра шляху у функції, використовуючи стандартні анотації типів Python:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial002.py hl[7] *}
|
||||
|
||||
У такому випадку `item_id` визначається як `int`.
|
||||
|
||||
/// check | Примітка
|
||||
|
||||
Це дасть можливість підтримки редактора всередині функції з перевірками помилок, автодоповнення тощо.
|
||||
|
||||
///
|
||||
|
||||
## <abbr title="або: серіалізація, парсинг, маршалізація">Перетворення</abbr> даних
|
||||
|
||||
Якщо запустити цей приклад і перейти за посиланням <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a>, то отримаєте таку відповідь:
|
||||
|
||||
```JSON
|
||||
{"item_id":3}
|
||||
```
|
||||
|
||||
/// check | Примітка
|
||||
|
||||
Зверніть увагу, що значення, яке отримала (і повернула) ваша функція, — це `3`. Це Python `int`, а не рядок `"3"`.
|
||||
|
||||
Отже, з таким оголошенням типу **FastAPI** автоматично виконує <abbr title="перетворення рядка, що надходить із HTTP-запиту, у типи даних Python">"парсинг"</abbr> запитів.
|
||||
|
||||
///
|
||||
|
||||
## <abbr title="Або валідація">Перевірка</abbr> даних
|
||||
|
||||
Якщо ж відкрити у браузері посилання <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, то побачимо цікаву HTTP-помилку:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "int_parsing",
|
||||
"loc": [
|
||||
"path",
|
||||
"item_id"
|
||||
],
|
||||
"msg": "Input should be a valid integer, unable to parse string as an integer",
|
||||
"input": "foo"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
тому що параметр шляху має значення `"foo"`, яке не є типом `int`.
|
||||
|
||||
Таку саму помилку отримаємо, якщо передати `float` замість `int`, як бачимо, у цьому прикладі: <a href="http://127.0.0.1:8000/items/4.2" class="external-link" target="_blank">http://127.0.0.1:8000/items/4.2</a>
|
||||
|
||||
/// check | Примітка
|
||||
|
||||
Отже, **FastAPI** надає перевірку типів з таким самим оголошенням типу в Python.
|
||||
|
||||
Зверніть увагу, що помилка також чітко вказує саме на те місце, де валідація не пройшла.
|
||||
|
||||
Це неймовірно корисно під час розробки та дебагінгу коду, що взаємодіє з вашим API.
|
||||
|
||||
///
|
||||
|
||||
## Документація
|
||||
|
||||
Тепер коли відкриєте свій браузер за посиланням <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, то побачите автоматично згенеровану, інтерактивну API-документацію:
|
||||
|
||||
<img src="/img/tutorial/path-params/image01.png">
|
||||
|
||||
/// check | Примітка
|
||||
|
||||
Знову ж таки, лише з цим самим оголошенням типу в Python, FastAPI надає вам автоматичну, інтерактивну документацію (з інтеграцією Swagger UI).
|
||||
|
||||
Зверніть увагу, що параметр шляху оголошений як ціле число.
|
||||
|
||||
|
||||
///
|
||||
|
||||
## Переваги стандартизації, альтернативна документація
|
||||
|
||||
І оскільки згенерована схема відповідає стандарту <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md" class="external-link" target="_blank">OpenAPI</a>, існує багато сумісних інструментів.
|
||||
|
||||
З цієї причини FastAPI також надає альтернативну документацію API (використовуючи ReDoc), до якої можна отримати доступ за посиланням <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>:
|
||||
|
||||
<img src="/img/tutorial/path-params/image02.png">
|
||||
|
||||
Таким чином, існує багато сумісних інструментів, включаючи інструменти для генерації коду для багатьох мов.
|
||||
|
||||
|
||||
## Pydantic
|
||||
|
||||
Вся валідація даних виконується за лаштунками за допомогою <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>, тому Ви отримуєте всі переваги від його використання. І можете бути впевнені, що все в надійних руках.
|
||||
|
||||
Ви можете використовувати ті самі оголошення типів з `str`, `float`, `bool` та багатьма іншими складними типами даних.
|
||||
|
||||
Декілька з них будуть розглянуті в наступних розділах посібника.
|
||||
|
||||
## Порядок має значення
|
||||
|
||||
При створенні *операцій шляху* можуть виникати ситуації, коли шлях фіксований.
|
||||
|
||||
Наприклад, `/users/me`. Припустимо, що це шлях для отримання даних про поточного користувача.
|
||||
|
||||
А також у вас може бути шлях `/users/{user_id}`, щоб отримати дані про конкретного користувача за його ID.
|
||||
|
||||
Оскільки *операції шляху* оцінюються по черзі, Ви повинні переконатися, що шлях для `/users/me` оголошений перед шляхом для `/users/{user_id}`:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
|
||||
|
||||
Інакше шлях для `/users/{user_id}` також буде відповідати для `/users/me`, "вважаючи", що він отримує параметр `user_id` зі значенням `"me"`.
|
||||
|
||||
Аналогічно, Ви не можете оголосити операцію шляху:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial003b.py hl[6,11] *}
|
||||
|
||||
Перша операція буде завжди використовуватися, оскільки шлях збігається першим.
|
||||
## Попередньо визначені значення
|
||||
|
||||
Якщо у вас є *операція шляху*, яка приймає *параметр шляху*, але Ви хочете, щоб можливі допустимі значення *параметра шляху* були попередньо визначені, Ви можете використати стандартний Python <abbr title="перелічення">Enum</abbr>.
|
||||
|
||||
### Створення класу `Enum`
|
||||
|
||||
Імпортуйте `Enum` і створіть підклас, що наслідується від `str` та `Enum`.
|
||||
|
||||
Наслідуючи від `str`, документація API зможе визначити, що значення повинні бути типу `string`, і правильно їх відобразить.
|
||||
|
||||
Після цього створіть атрибути класу з фіксованими значеннями, які будуть доступними допустимими значеннями:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
|
||||
|
||||
/// info | Додаткова інформація
|
||||
|
||||
<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">Перелічення (або enums) доступні в Python</a> починаючи з версії 3.4.
|
||||
|
||||
///
|
||||
|
||||
/// tip | Порада
|
||||
|
||||
Якщо вам цікаво, "AlexNet", "ResNet" та "LeNet" — це просто назви ML моделей <abbr title="Технічно, архітектури Deep Learning моделей">Machine Learning</abbr>.
|
||||
|
||||
///
|
||||
|
||||
|
||||
### Оголосіть *параметр шляху*
|
||||
|
||||
Потім створіть *параметр шляху* з анотацією типу, використовуючи створений вами клас enum (`ModelName`):
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[16] *}
|
||||
|
||||
### Перевірка документації
|
||||
|
||||
Оскільки доступні значення для *параметра шляху* визначені заздалегідь, інтерактивна документація зможе красиво їх відобразити:
|
||||
|
||||
<img src="/img/tutorial/path-params/image03.png">
|
||||
|
||||
### Робота з *перелічуваннями* у Python
|
||||
|
||||
Значення *параметра шляху* буде елементом *перелічування*.
|
||||
|
||||
#### Порівняння *елементів перелічування*
|
||||
|
||||
Ви можете порівнювати його з *елементами перелічування* у створеному вами enum `ModelName`:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[17] *}
|
||||
|
||||
#### Отримання *значення перелічування*
|
||||
|
||||
Ви можете отримати фактичне значення (у цьому випадку це `str`), використовуючи `model_name.value`, або загалом `your_enum_member.value`:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[20] *}
|
||||
|
||||
/// tip | Порада
|
||||
|
||||
Ви також можете отримати доступ до значення `"lenet"`, використовуючи `ModelName.lenet.value`.
|
||||
|
||||
///
|
||||
|
||||
|
||||
#### Повернення *елементів перелічування*
|
||||
|
||||
Ви можете повертати *елементи перелічування* з вашої *операції шляху*, навіть вкладені у JSON-тіло (наприклад, `dict`).
|
||||
|
||||
Вони будуть перетворені на відповідні значення (у цьому випадку рядки) перед поверненням клієнту:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
|
||||
|
||||
На стороні клієнта Ви отримаєте відповідь у форматі JSON, наприклад:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"model_name": "alexnet",
|
||||
"message": "Deep Learning FTW!"
|
||||
}
|
||||
```
|
||||
|
||||
## Path-параметри, що містять шляхи
|
||||
|
||||
Припустимо, у вас є *операція шляху* з маршрутом `/files/{file_path}`.
|
||||
|
||||
Але вам потрібно, щоб `file_path` містив *шлях*, наприклад `home/johndoe/myfile.txt`.
|
||||
|
||||
Отже, URL для цього файлу виглядатиме так: `/files/home/johndoe/myfile.txt`.
|
||||
|
||||
|
||||
|
||||
### Підтримка OpenAPI
|
||||
|
||||
OpenAPI не підтримує спосіб оголошення *параметра шляху*, що містить *шлях* всередині, оскільки це може призвести до сценаріїв, які складно тестувати та визначати.
|
||||
|
||||
Однак (одначе), Ви все одно можете зробити це в **FastAPI**, використовуючи один із внутрішніх інструментів Starlette.
|
||||
|
||||
Документація все ще працюватиме, хоча й не додаватиме опису про те, що параметр повинен містити шлях.
|
||||
|
||||
### Конвертер шляху
|
||||
|
||||
Використовуючи опцію безпосередньо зі Starlette, Ви можете оголосити *параметр шляху*, що містить *шлях*, використовуючи URL на кшталт:
|
||||
|
||||
```
|
||||
/files/{file_path:path}
|
||||
```
|
||||
У цьому випадку ім'я параметра — `file_path`, а остання частина `:path` вказує на те, що параметр повинен відповідати будь-якому *шляху*.
|
||||
|
||||
Отже, Ви можете використати його так:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial004.py hl[6] *}
|
||||
|
||||
/// tip | Порада
|
||||
|
||||
Вам може знадобитися, щоб параметр містив `/home/johndoe/myfile.txt` із початковою косою рискою (`/`).
|
||||
|
||||
У такому випадку URL виглядатиме так: `/files//home/johndoe/myfile.txt`, із подвійною косою рискою (`//`) між `files` і `home`.
|
||||
|
||||
///
|
||||
|
||||
## Підсумок
|
||||
|
||||
З **FastAPI**, використовуючи короткі, інтуїтивно зрозумілі та стандартні оголошення типів Python, Ви отримуєте:
|
||||
|
||||
* Підтримку в редакторі: перевірка помилок, автодоповнення тощо.
|
||||
* "<abbr title="перетворення рядка, що надходить з HTTP-запиту, у типи даних Python">Парсинг</abbr>" даних
|
||||
* Валідацію даних
|
||||
* Анотацію API та автоматичну документацію
|
||||
|
||||
І вам потрібно оголосити їх лише один раз.
|
||||
|
||||
Це, ймовірно, основна видима перевага **FastAPI** порівняно з альтернативними фреймворками (окрім високої продуктивності).
|
||||
@@ -0,0 +1,68 @@
|
||||
# Моделі Query параметрів
|
||||
|
||||
Якщо у Вас є група **query параметрів**, які пов’язані між собою, Ви можете створити **Pydantic-модель** для їх оголошення.
|
||||
|
||||
Це дозволить Вам **повторно використовувати модель** у **різних місцях**, а також оголошувати перевірки та метадані для всіх параметрів одночасно. 😎
|
||||
|
||||
/// note | Примітка
|
||||
|
||||
Ця можливість підтримується, починаючи з версії FastAPI `0.115.0`. 🤓
|
||||
|
||||
///
|
||||
|
||||
## Query параметри з Pydantic-моделлю
|
||||
|
||||
Оголосіть **query параметри**, які Вам потрібні, у **Pydantic-моделі**, а потім оголосіть цей параметр як `Query`:
|
||||
|
||||
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
|
||||
|
||||
**FastAPI** буде **витягувати** дані для **кожного поля** з **query параметрів** у запиті та передавати їх у визначену вами Pydantic-модель.
|
||||
|
||||
## Перевірте документацію
|
||||
|
||||
Ви можете побачити параметри запиту в UI документації за `/docs`:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/query-param-models/image01.png">
|
||||
</div>
|
||||
|
||||
## Заборона зайвих Query параметрів
|
||||
|
||||
У деяких особливих випадках (ймовірно, не дуже поширених) Ви можете захотіти **обмежити** query параметри, які дозволено отримувати.
|
||||
|
||||
Ви можете використати конфігурацію моделі Pydantic, щоб заборонити (`forbid`) будь-які зайві (`extra`) поля:
|
||||
|
||||
{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *}
|
||||
|
||||
Якщо клієнт спробує надіслати **зайві** дані у **query параметрах**, він отримає **помилку**.
|
||||
|
||||
Наприклад, якщо клієнт спробує надіслати query параметр `tool` зі значенням `plumbus`, як у цьому запиті:
|
||||
|
||||
```http
|
||||
https://example.com/items/?limit=10&tool=plumbus
|
||||
```
|
||||
|
||||
Він отримає відповідь з **помилкою**, яка повідомить, що query параметр `tool ` не дозволено:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "extra_forbidden",
|
||||
"loc": ["query", "tool"],
|
||||
"msg": "Extra inputs are not permitted",
|
||||
"input": "plumbus"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Підсумок
|
||||
|
||||
Ви можете використовувати **Pydantic-моделі** для оголошення **query параметрів** у **FastAPI**. 😎
|
||||
|
||||
/// tip | Підказка
|
||||
|
||||
Спойлер: Ви також можете використовувати Pydantic-моделі для оголошення cookie та заголовків, але про це Ви дізнаєтеся пізніше в цьому посібнику. 🤫
|
||||
|
||||
///
|
||||
@@ -0,0 +1,491 @@
|
||||
# Query параметри та валідація рядків
|
||||
|
||||
**FastAPI** дозволяє оголошувати додаткову інформацію та виконувати валідацію для Ваших параметрів.
|
||||
|
||||
Розглянемо цей додаток як приклад:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
|
||||
|
||||
Query параметр `q` має тип `str | None`, що означає, що він може бути як `str`, так і `None`. За замовчуванням він має значення `None`, тому FastAPI розуміє, що цей параметр не є обов'язковим.
|
||||
|
||||
/// note | Примітка
|
||||
|
||||
FastAPI знає, що `q` не є обов’язковим, завдяки значенню за замовчуванням `= None`.
|
||||
|
||||
Використання `str | None` дозволить Вашому редактору коду надавати кращу підтримку та виявляти помилки.
|
||||
|
||||
///
|
||||
|
||||
## Додаткова валідація
|
||||
|
||||
Ми хочемо, щоб навіть якщо `q` є необов’язковим, **його довжина не перевищувала 50 символів**, якщо він все ж буде переданий.
|
||||
|
||||
### Імпорт `Query` та `Annotated`
|
||||
|
||||
Щоб це зробити, спочатку імпортуємо:
|
||||
|
||||
* `Query` з `fastapi`
|
||||
* `Annotated` з `typing`
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
FastAPI додав підтримку `Annotated` (і почав рекомендувати його) у версії 0.95.0.
|
||||
|
||||
Якщо у Вас старіша версія, під час використання `Annotated` можуть виникати помилки.
|
||||
|
||||
Переконайтеся, що Ви [оновили версію FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} до принаймні 0.95.1, перш ніж використовувати `Annotated`.
|
||||
|
||||
///
|
||||
|
||||
## Використання `Annotated` у типі параметра `q`
|
||||
|
||||
Пам’ятаєте, як я раніше розповідав, що `Annotated` можна використовувати для додавання метаданих до параметрів у [Вступі до типів Python](../python-types.md#type-hints-with-metadata-annotations){.internal-link target=_blank}?
|
||||
|
||||
Зараз саме час використати його разом із FastAPI. 🚀
|
||||
|
||||
Раніше ми мали таку анотацію типу:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Тепер ми загорнемо її у `Annotated`, і отримаємо:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
q: Annotated[str | None] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python
|
||||
q: Annotated[Union[str, None]] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Обидві ці версії означають одне й те саме: `q` — це параметр, який може бути `str` або `None`, і за замовчуванням має значення `None`.
|
||||
|
||||
А тепер переходимо до цікавого! 🎉
|
||||
|
||||
## Додавання `Query` до `Annotated` у параметр `q`
|
||||
|
||||
Тепер, коли у нас є `Annotated`, де ми можемо додавати додаткову інформацію (зокрема валідацію), додамо `Query` всередину `Annotated` і встановимо параметр `max_length` у `50`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *}
|
||||
|
||||
Зверніть увагу, що значення за замовчуванням усе ще `None`, тому параметр залишається необов'язковим.
|
||||
|
||||
Але тепер, додавши `Query(max_length=50)` всередину `Annotated`, ми повідомляємо FastAPI, що хочемо **додаткову валідацію** для цього значення — воно має містити максимум 50 символів. 😎
|
||||
|
||||
/// tip | Підказка
|
||||
|
||||
Ми використовуємо `Query()`, оскільки це **query параметр**. Далі ми розглянемо інші варіанти, як-от `Path()`, `Body()`, `Header()` та `Cookie()`, які приймають ті самі аргументи, що й `Query()`.
|
||||
|
||||
///
|
||||
|
||||
Тепер FastAPI:
|
||||
|
||||
* **Перевірить** дані, щоб переконатися, що їхня довжина не перевищує 50 символів
|
||||
* Покажe **чітку помилку** клієнту, якщо дані недійсні
|
||||
* **Задокументує** параметр в OpenAPI-схемі *операції шляху* (що відобразиться в **автоматично згенерованій документації**)
|
||||
|
||||
## Альтернативний (застарілий) метод: Query як значення за замовчуванням
|
||||
|
||||
У попередніх версіях FastAPI (до <abbr title="до 2023-03">0.95.0</abbr>) `Query` використовувався як значення за замовчуванням для параметра, а не всередині `Annotated`. Ви, ймовірно, побачите код, який використовує цей підхід, тому варто розглянути його.
|
||||
|
||||
/// tip | Підказка
|
||||
|
||||
Для нового коду та коли це можливо, використовуйте `Annotated`, як показано вище. Це має багато переваг (пояснених нижче) і не має недоліків. 🍰
|
||||
|
||||
///
|
||||
|
||||
Раніше ми писали `Query()` як значення за замовчуванням для параметра функції, встановлюючи `max_length` у 50:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
|
||||
|
||||
Оскільки в цьому випадку (без `Annotated`) нам потрібно замінити `None` у функції на `Query()`, тепер ми повинні явно встановити значення за замовчуванням через параметр `Query(default=None)`. Це виконує ту саму роль визначення значення за замовчуванням (принаймні для FastAPI).
|
||||
|
||||
Таким чином:
|
||||
|
||||
```Python
|
||||
q: str | None = Query(default=None)
|
||||
```
|
||||
|
||||
...робить параметр необов’язковим зі значенням за замовчуванням `None`, що еквівалентно:
|
||||
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
Але у версії з `Query` ми явно вказуємо, що це query параметр.
|
||||
|
||||
Далі ми можемо передавати `Query` додаткові параметри, зокрема `max_length`, який застосовується до рядків:
|
||||
|
||||
```Python
|
||||
q: str | None = Query(default=None, max_length=50)
|
||||
```
|
||||
|
||||
Це забезпечить валідацію даних, виведе зрозумілу помилку у разі недійсних даних і задокументує параметр у схемі OpenAPI *операції шляху*.
|
||||
|
||||
### `Query` як значення за замовчуванням або всередині `Annotated`
|
||||
|
||||
Важливо пам’ятати, якщо використовувати `Query` всередині `Annotated`, не можна задавати параметр `default` у `Query`.
|
||||
|
||||
Замість цього використовуйте значення за замовчуванням у самій функції. Інакше це буде нелогічно.
|
||||
|
||||
Наприклад, цей варіант є некоректним:
|
||||
|
||||
```Python
|
||||
q: Annotated[str, Query(default="rick")] = "morty"
|
||||
```
|
||||
|
||||
...тому, що не зрозуміло, яке значення має бути значенням за замовчуванням: `"rick"` чи `"morty"`.
|
||||
|
||||
Коректні варіанти:
|
||||
|
||||
```Python
|
||||
q: Annotated[str, Query()] = "rick"
|
||||
```
|
||||
|
||||
...або у старих кодових базах Ви знайдете:
|
||||
|
||||
```Python
|
||||
q: str = Query(default="rick")
|
||||
```
|
||||
|
||||
### Переваги використання `Annotated`
|
||||
|
||||
**Використання `Annotated` є рекомендованим** замість задання значення за замовчуванням у параметрах функції, оскільки воно **краще** з кількох причин. 🤓
|
||||
|
||||
Значення **за замовчуванням** параметра **функції** є його **фактичним значенням за замовчуванням**, що є більш інтуїтивним у Python загалом. 😌
|
||||
|
||||
Ви можете **викликати** ту саму функцію **в інших місцях** без FastAPI, і вона **працюватиме очікувано**. Якщо параметр є **обов’язковим** (без значення за замовчуванням), Ваш **редактор** повідомить про помилку, а **Python** також видасть помилку, якщо Ви виконаєте функцію без передавання цього параметра.
|
||||
|
||||
Якщо Ви не використовуєте `Annotated`, а використовуєте **(старий) стиль значень за замовчуванням**, то при виклику цієї функції без FastAPI **в інших місцях**, потрібно **не забути** передати їй аргументи, інакше значення будуть відрізнятися від очікуваних (наприклад, Ви отримаєте `QueryInfo` або подібне замість `str`). Ваш редактор не повідомить про помилку, і Python також не видасть помилку при запуску функції, поки не виникне помилка під час виконання операцій усередині.
|
||||
|
||||
Оскільки `Annotated` може містити кілька анотацій метаданих, Ви навіть можете використовувати ту саму функцію з іншими інструментами, такими як <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">Typer</a>. 🚀
|
||||
|
||||
## Додавання додаткових валідацій
|
||||
|
||||
Ви також можете додати параметр `min_length`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *}
|
||||
|
||||
## Додавання регулярних виразів
|
||||
|
||||
Ви можете визначити <abbr title="Регулярний вираз (regex або regexp) — це послідовність символів, яка визначає шаблон для пошуку в рядках.">регулярний вираз</abbr> pattern, якому має відповідати параметр:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
|
||||
|
||||
Цей конкретний шаблон регулярного виразу перевіряє, що отримане значення параметра:
|
||||
|
||||
* `^`: починається з наступних символів, перед якими немає інших символів.
|
||||
* `fixedquery`: точно відповідає значенню `fixedquery`.
|
||||
* `$`: закінчується тут, після `fixedquery` немає жодних символів.
|
||||
|
||||
Якщо Ви почуваєтеся розгублено щодо **"регулярних виразів"**, не хвилюйтеся. Вони є складною темою для багатьох людей. Ви все одно можете зробити багато речей без їх використання.
|
||||
|
||||
Але тепер Ви знаєте, що коли вони знадобляться, їх можна застосовувати у **FastAPI**.
|
||||
|
||||
### Pydantic v1 `regex` замість `pattern`
|
||||
|
||||
До версії Pydantic 2 і FastAPI 0.100.0 параметр називався `regex` замість `pattern`, але тепер він застарів.
|
||||
|
||||
Ви все ще можете зустріти код, який використовує його:
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial004_regex_an_py310.py hl[11] *}
|
||||
|
||||
////
|
||||
|
||||
Але майте на увазі, що він є застарілим і його слід оновити до нового параметра `pattern`. 🤓
|
||||
|
||||
## Значення за замовчуванням
|
||||
|
||||
Ви можете використовувати значення за замовчуванням, відмінні від `None`.
|
||||
|
||||
Наприклад, якщо Ви хочете оголосити параметр запиту `q` з `min_length` `3` і значенням за замовчуванням `"fixedquery"`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial005_an_py39.py hl[9] *}
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Наявність значення за замовчуванням будь-якого типу, включаючи `None`, робить параметр необов’язковим (not required).
|
||||
|
||||
///
|
||||
|
||||
## Обов’язкові параметри
|
||||
|
||||
Якщо нам не потрібно вказувати додаткові перевірки або метадані, ми можемо зробити параметр `q` обов’язковим, просто не оголошуючи значення за замовчуванням, наприклад:
|
||||
|
||||
```Python
|
||||
q: str
|
||||
```
|
||||
|
||||
замість:
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
Але тепер ми оголошуємо його з `Query`, наприклад:
|
||||
|
||||
//// tab | Annotated
|
||||
|
||||
```Python
|
||||
q: Annotated[str | None, Query(min_length=3)] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Тому, якщо Вам потрібно зробити значення обов’язковим, використовуючи `Query`, просто не вказуйте значення за замовчуванням:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
|
||||
|
||||
### Обов’язкове значення, яке може бути `None`
|
||||
|
||||
Ви можете вказати, що параметр може приймати `None`, але при цьому залишається обов’язковим. Це змусить клієнтів надіслати значення, навіть якщо воно дорівнює `None`.
|
||||
|
||||
Щоб зробити це, оголосіть, що `None` є допустимим типом, але не вказуйте значення за замовчуванням:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
|
||||
|
||||
## Список параметрів запиту / кілька значень
|
||||
|
||||
Якщо Ви визначаєте параметр запиту за допомогою `Query`, Ви також можете дозволити отримання списку значень, тобто дозволити отримання кількох значень.
|
||||
|
||||
Наприклад, щоб дозволити параметру запиту `q` з'являтися кілька разів в URL, можна написати:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *}
|
||||
|
||||
Тоді, у випадку запиту за URL:
|
||||
|
||||
```
|
||||
http://localhost:8000/items/?q=foo&q=bar
|
||||
```
|
||||
|
||||
Ви отримаєте кілька значень *query параметра* `q` (`foo` і `bar`) у вигляді списку `list` в Python у Вашій *функції обробки шляху*, у *параметрі функції* `q`.
|
||||
|
||||
Отже, відповідь на цей URL буде:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"q": [
|
||||
"foo",
|
||||
"bar"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
/// tip | Підказка
|
||||
|
||||
Щоб оголосити параметр запиту з типом `list`, як у наведеному вище прикладі, потрібно явно використовувати `Query`, інакше він буде інтерпретований як тіло запиту.
|
||||
|
||||
///
|
||||
|
||||
Інтерактивна API-документація оновиться відповідно, дозволяючи передавати кілька значень:
|
||||
|
||||
<img src="/img/tutorial/query-params-str-validations/image02.png">
|
||||
|
||||
### Список параметрів запиту / кілька значень за замовчуванням
|
||||
|
||||
Ви також можете визначити значення за замовчуванням для `list`, якщо жодне значення не було передане:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *}
|
||||
|
||||
Якщо Ви перейдете за посиланням:
|
||||
|
||||
```
|
||||
http://localhost:8000/items/
|
||||
```
|
||||
|
||||
то значення `q` за замовчуванням буде: `["foo", "bar"]`, і Ваша відповідь виглядатиме так:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"q": [
|
||||
"foo",
|
||||
"bar"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Використання тільки `list`
|
||||
|
||||
Ви також можете використовувати `list` без уточнення типу, замість `list[str]`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Майте на увазі, що в цьому випадку FastAPI не перевірятиме вміст списку.
|
||||
|
||||
Наприклад, `list[int]` перевірятиме (і документуватиме), що всі елементи списку є цілими числами. Але `list` без уточнення цього не робитиме.
|
||||
|
||||
///
|
||||
|
||||
## Додавання додаткових метаданих
|
||||
|
||||
Ви можете додати більше інформації про параметр.
|
||||
|
||||
Ця інформація буде включена у згенерований OpenAPI та використана в інтерфейсах документації та зовнішніх інструментах.
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Майте на увазі, що різні інструменти можуть мати різний рівень підтримки OpenAPI.
|
||||
|
||||
Деякі з них можуть ще не відображати всю додаткову інформацію, хоча в більшості випадків ця функція вже запланована для розробки.
|
||||
|
||||
///
|
||||
|
||||
Ви можете додати `title` :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *}
|
||||
|
||||
А також `description`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
|
||||
|
||||
## Аліаси параметрів
|
||||
|
||||
Уявіть, що Ви хочете, щоб параметр називався `item-query`.
|
||||
|
||||
Наприклад:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?item-query=foobaritems
|
||||
```
|
||||
|
||||
Але `item-query` — це некоректна назва змінної в Python.
|
||||
|
||||
Найближчий допустимий варіант — `item_query`.
|
||||
|
||||
Проте Вам потрібно, щоб параметр залишався саме `item-query`...
|
||||
|
||||
У такому випадку можна оголосити `alias`, і саме він буде використовуватися для отримання значення параметра:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
|
||||
|
||||
## Виведення параметрів як застарілих
|
||||
|
||||
Припустимо, що Ви більше не хочете використовувати цей параметр.
|
||||
|
||||
Вам потрібно залишити його на деякий час, оскільки ним користуються клієнти, але Ви хочете, щоб документація чітко показувала, що він є <abbr title="застарілий, не рекомендується до використання">застарілим</abbr>.
|
||||
|
||||
Тоді Ви можете передати параметр `deprecated=True` до `Query`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *}
|
||||
|
||||
Документація буде показувати це таким чином:
|
||||
|
||||
<img src="/img/tutorial/query-params-str-validations/image01.png">
|
||||
|
||||
## Виняток параметрів з OpenAPI
|
||||
|
||||
Щоб виключити параметр запиту зі згенерованої схеми OpenAPI (і, таким чином, з автоматичних систем документації), встановіть параметр `include_in_schema` для `Query` в `False`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
|
||||
|
||||
## Кастомна валідація
|
||||
|
||||
Можуть бути випадки, коли Вам потрібно провести **кастомну валідацію**, яку не можна реалізувати за допомогою параметрів, показаних вище.
|
||||
|
||||
У таких випадках ви можете використати **кастомну функцію валідації**, яка буде застосована після звичайної валідації (наприклад, після перевірки, що значення є типом `str`).
|
||||
|
||||
Це можна досягти за допомогою <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator" class="external-link" target="_blank">Pydantic's `AfterValidator`</a> в середині `Annotated`.
|
||||
|
||||
/// tip | Підказка
|
||||
|
||||
Pydantic також має <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator" class="external-link" target="_blank">`BeforeValidator`</a> та інші. 🤓
|
||||
|
||||
///
|
||||
|
||||
Наприклад, цей кастомний валідатор перевіряє, чи починається ID елемента з `isbn-` для номера книги <abbr title="ISBN означає Міжнародний стандартний номер книги">ISBN</abbr> або з `imdb-` для ID URL фільму на <abbr title="IMDB (Internet Movie Database) це вебсайт з інформацією про фільми">IMDB</abbr>:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Це доступно з версії Pydantic 2 або вище. 😎
|
||||
|
||||
///
|
||||
|
||||
/// tip | Підказка
|
||||
|
||||
Якщо Вам потрібно виконати будь-яку валідацію, яка вимагає взаємодії з будь-яким **зовнішнім компонентом**, таким як база даних чи інший API, ви повинні замість цього використовувати **FastAPI Dependencies**. Ви дізнаєтесь про них пізніше.
|
||||
|
||||
Ці кастомні валідатори використовуються для речей, які можна перевірити лише з **тими даними**, що надані в запиті.
|
||||
|
||||
///
|
||||
|
||||
### Зрозумійте цей код
|
||||
|
||||
Головний момент – це використання **`AfterValidator` з функцією всередині `Annotated`**. Можете пропустити цю частину, якщо хочете. 🤸
|
||||
|
||||
---
|
||||
|
||||
Але якщо Вам цікаво розібратися в цьому конкретному прикладі коду і Вам ще не набридло, ось кілька додаткових деталей.
|
||||
|
||||
#### Рядок із `value.startswith()`
|
||||
|
||||
Звернули увагу? Рядок із `value.startswith()` може приймати кортеж, і тоді він перевірятиме кожне значення в кортежі:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
|
||||
|
||||
#### Випадковий елемент
|
||||
|
||||
За допомогою `data.items()` ми отримуємо <abbr title="Об'єкт, який можна перебирати в циклі, як-от список чи множину.">ітерабельний об'єкт</abbr> із кортежами, що містять ключ і значення для кожного елемента словника.
|
||||
|
||||
Ми перетворюємо цей ітерабельний об'єкт у звичайний `list` за допомогою `list(data.items())`.
|
||||
|
||||
Потім, використовуючи `random.choice()`, ми можемо отримати випадкове значення зі списку, тобто отримуємо кортеж із `(id, name)`. Це може бути щось на зразок `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`.
|
||||
|
||||
Далі ми **присвоюємо ці два значення** кортежу змінним `id` і `name`.
|
||||
|
||||
Тож, якщо користувач не вказав ID елемента, він все одно отримає випадкову рекомендацію.
|
||||
|
||||
...і все це реалізовано в **одному рядку коду**. 🤯 Хіба не прекрасний Python? 🐍
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
|
||||
|
||||
## Підсумок
|
||||
|
||||
Ви можете оголошувати додаткові валідації та метаінформацію для своїх параметрів.
|
||||
|
||||
Загальні валідації та метаінформація:
|
||||
|
||||
* `alias`
|
||||
* `title`
|
||||
* `description`
|
||||
* `deprecated`
|
||||
|
||||
Валідації, специфічні для рядків:
|
||||
|
||||
* `min_length`
|
||||
* `max_length`
|
||||
* `pattern`
|
||||
|
||||
Кастомні валідації за допомогою `AfterValidator`.
|
||||
|
||||
У цих прикладах Ви побачили, як оголошувати валідації для значень `str`.
|
||||
|
||||
Дивіться наступні розділи, щоб дізнатися, як оголошувати валідації для інших типів, наприклад чисел.
|
||||
@@ -0,0 +1,191 @@
|
||||
# Query Параметри
|
||||
|
||||
Коли Ви оголошуєте інші параметри функції, які не є частиною параметрів шляху, вони автоматично інтерпретуються як "query" параметри.
|
||||
|
||||
{* ../../docs_src/query_params/tutorial001.py hl[9] *}
|
||||
|
||||
Query параметри — це набір пар ключ-значення, що йдуть після символу `?` в URL, розділені символами `&`.
|
||||
|
||||
Наприклад, в URL:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?skip=0&limit=10
|
||||
```
|
||||
|
||||
...query параметрами є:
|
||||
|
||||
* `skip`: зі значенням `0`
|
||||
* `limit`: зі значенням `10`
|
||||
|
||||
Оскільки вони є частиною URL, вони "за замовчуванням" є рядками.
|
||||
|
||||
Але коли Ви оголошуєте їх із типами Python (у наведеному прикладі як `int`), вони перетворюються на цей тип і проходять перевірку відповідності.
|
||||
|
||||
Увесь той самий процес, який застосовується до параметрів шляху, також застосовується до query параметрів:
|
||||
|
||||
* Підтримка в редакторі (автодоповнення, перевірка помилок)
|
||||
* <abbr title="перетворення рядка, що надходить з HTTP-запиту, у типи даних Python">"Парсинг"</abbr> даних
|
||||
* Валідація даних
|
||||
* Автоматична документація
|
||||
|
||||
|
||||
## Значення за замовчуванням
|
||||
|
||||
Оскільки query параметри не є фіксованою частиною шляху, вони можуть бути необов’язковими та мати значення за замовчуванням.
|
||||
|
||||
У наведеному вище прикладі вони мають значення за замовчуванням: `skip=0` і `limit=10`.
|
||||
|
||||
Отже, результат переходу за URL:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/
|
||||
```
|
||||
буде таким самим, як і перехід за посиланням:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?skip=0&limit=10
|
||||
```
|
||||
|
||||
Але якщо Ви перейдете, наприклад, за посиланням:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?skip=20
|
||||
```
|
||||
|
||||
Значення параметрів у вашій функції будуть такими:
|
||||
|
||||
* `skip=20`: оскільки Ви вказали його в URL
|
||||
* `limit=10`: оскільки це значення за замовчуванням
|
||||
|
||||
## Необов'язкові параметри
|
||||
|
||||
Аналогічно, Ви можете оголосити необов’язкові query параметри, встановивши для них значення за замовчуванням `None`:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *}
|
||||
|
||||
У цьому випадку параметр функції `q` буде необов’язковим і за замовчуванням матиме значення `None`.
|
||||
|
||||
/// check | Примітка
|
||||
|
||||
Також зверніть увагу, що **FastAPI** достатньо розумний, щоб визначити, що параметр шляху `item_id` є параметром шляху, а `q` — ні, отже, це query параметр.
|
||||
|
||||
///
|
||||
|
||||
## Перетворення типу Query параметра
|
||||
|
||||
Ви також можете оголошувати параметри типу `bool`, і вони будуть автоматично конвертовані:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *}
|
||||
|
||||
У цьому випадку, якщо Ви звернетесь до:
|
||||
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo?short=1
|
||||
```
|
||||
|
||||
або
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo?short=True
|
||||
```
|
||||
|
||||
або
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo?short=true
|
||||
```
|
||||
|
||||
або
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo?short=on
|
||||
```
|
||||
|
||||
або
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo?short=yes
|
||||
```
|
||||
|
||||
або будь-який інший варіант написання (великі літери, перша літера велика тощо), ваша функція побачить параметр `short` зі значенням `True` з типом даних `bool`. В іншому випадку – `False`.
|
||||
|
||||
## Кілька path і query параметрів
|
||||
|
||||
Ви можете одночасно оголошувати кілька path і query параметрів, і **FastAPI** автоматично визначить, який з них до чого належить.
|
||||
|
||||
|
||||
Не потрібно дотримуватись певного порядку їх оголошення.
|
||||
|
||||
Вони визначаються за назвою:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *}
|
||||
|
||||
## Обов’язкові Query параметри
|
||||
|
||||
Якщо Ви оголошуєте значення за замовчуванням для параметрів, які не є path-параметрами (у цьому розділі ми бачили поки що лише path параметри), тоді вони стають необов’язковими.
|
||||
|
||||
Якщо Ви не хочете вказувати конкретні значення, але хочете зробити параметр опціональним, задайте `None` як значення за замовчуванням.
|
||||
|
||||
Але якщо Ви хочете зробити query параметр обов’язковим, просто не вказуйте для нього значення за замовчуванням:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial005.py hl[6:7] *}
|
||||
|
||||
Тут `needy` – обов’язковий query параметр типу `str`.
|
||||
|
||||
Якщо Ви відкриєте у браузері URL-адресу:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo-item
|
||||
```
|
||||
|
||||
...без додавання обов’язкового параметра `needy`, Ви побачите помилку:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"query",
|
||||
"needy"
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Оскільки `needy` є обов’язковим параметром, вам потрібно вказати його в URL:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
|
||||
```
|
||||
|
||||
...цей запит поверне:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"item_id": "foo-item",
|
||||
"needy": "sooooneedy"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Звичайно, Ви можете визначити деякі параметри як обов’язкові, інші зі значенням за замовчуванням, а ще деякі — повністю опціональні:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *}
|
||||
|
||||
У цьому випадку є 3 query параметри:
|
||||
|
||||
* `needy`, обов’язковий `str`.
|
||||
* `skip`, `int` зі значенням за замовчуванням `0`.
|
||||
* `limit`, опціональний `int`.
|
||||
|
||||
|
||||
/// tip | Підказка
|
||||
|
||||
Ви також можете використовувати `Enum`-и, так само як і з [Path Parameters](path-params.md#predefined-values){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
@@ -0,0 +1,175 @@
|
||||
# Запит файлів
|
||||
|
||||
Ви можете визначити файли, які будуть завантажуватися клієнтом, використовуючи `File`.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Щоб отримувати завантажені файли, спочатку встановіть <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">python-multipart</a>.
|
||||
|
||||
Переконайтеся, що Ви створили [віртуальне середовище](../virtual-environments.md){.internal-link target=_blank}, активували його та встановили пакет, наприклад:
|
||||
|
||||
```console
|
||||
$ pip install python-multipart
|
||||
```
|
||||
|
||||
Це необхідно, оскільки завантажені файли передаються у вигляді "форматованих даних форми".
|
||||
|
||||
///
|
||||
|
||||
## Імпорт `File`
|
||||
|
||||
Імпортуйте `File` та `UploadFile` з `fastapi`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[3] *}
|
||||
|
||||
## Визначення параметрів `File`
|
||||
|
||||
Створіть параметри файлів так само як Ви б створювали `Body` або `Form`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[9] *}
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
`File` — це клас, який безпосередньо успадковує `Form`.
|
||||
|
||||
Але пам’ятайте, що коли Ви імпортуєте `Query`, `Path`, `File` та інші з `fastapi`, це насправді функції, які повертають спеціальні класи.
|
||||
|
||||
///
|
||||
|
||||
/// tip | Підказка
|
||||
|
||||
Щоб оголосити тіла файлів, Вам потрібно використовувати `File`, тому що інакше параметри будуть інтерпретовані як параметри запиту або параметри тіла (JSON).
|
||||
|
||||
///
|
||||
|
||||
Файли будуть завантажені у вигляді "форматованих даних форми".
|
||||
|
||||
Якщо Ви оголосите тип параметра функції обробника маршруту як `bytes`, **FastAPI** прочитає файл за Вас, і Ви отримаєте його вміст у вигляді `bytes`.
|
||||
|
||||
Однак майте на увазі, що весь вміст буде збережено в пам'яті. Це працюватиме добре для малих файлів.
|
||||
|
||||
Але в деяких випадках Вам може знадобитися `UploadFile`.
|
||||
|
||||
## Параметри файлу з `UploadFile`
|
||||
|
||||
Визначте параметр файлу з типом `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[14] *}
|
||||
|
||||
Використання `UploadFile` має кілька переваг перед `bytes`:
|
||||
|
||||
* Вам не потрібно використовувати `File()` у значенні за замовчуванням параметра.
|
||||
* Використовується "буферизований" файл:
|
||||
* Файл зберігається в пам'яті до досягнення певного обмеження, після чого він записується на диск.
|
||||
* Це означає, що він добре працює для великих файлів, таких як зображення, відео, великі двійкові файли тощо, не споживаючи всю пам'ять.
|
||||
Ви можете отримати метадані про завантажений файл.
|
||||
* Він має <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">file-like</a> `асинхронний файловий інтерфейс` interface.
|
||||
* Він надає фактичний об'єкт Python <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">`SpooledTemporaryFile`</a>, який можна передавати безпосередньо іншим бібліотекам.
|
||||
|
||||
### `UploadFile`
|
||||
|
||||
`UploadFile` має такі атрибути:
|
||||
|
||||
* `filename`: Рядок `str` з оригінальною назвою файлу, який був завантажений (наприклад, `myimage.jpg`).
|
||||
* `content_type`: Рядок `str` з MIME-типом (наприклад, `image/jpeg`).
|
||||
* `file`: Об'єкт <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">SpooledTemporaryFile</a> (<a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">файлоподібний</a> об'єкт). Це фактичний файловий об'єкт Python, який можна безпосередньо передавати іншим функціям або бібліотекам, що очікують "файлоподібний" об'єкт.
|
||||
|
||||
`UploadFile` має такі асинхронні `async` методи. Вони викликають відповідні методи файлу під капотом (використовуючи внутрішній `SpooledTemporaryFile`).
|
||||
|
||||
* `write(data)`: Записує `data` (`str` або `bytes`) у файл.
|
||||
* `read(size)`: Читає `size` (`int`) байтів/символів з файлу.
|
||||
* `seek(offset)`: Переміщується до позиції `offset` (`int`) у файлі.
|
||||
* Наприклад, `await myfile.seek(0)` поверне курсор на початок файлу.
|
||||
* This is especially useful if you run `await myfile.read()` once and then need to read the contents again. Це особливо корисно, якщо Ви виконуєте await `await myfile.read()` один раз, а потім потрібно знову прочитати вміст.
|
||||
* `close()`: Закриває файл.
|
||||
|
||||
Оскільки всі ці методи є асинхронними `async`, Вам потрібно використовувати "await":
|
||||
|
||||
Наприклад, всередині `async` *функції обробки шляху* Ви можете отримати вміст за допомогою:
|
||||
|
||||
```Python
|
||||
contents = await myfile.read()
|
||||
```
|
||||
Якщо Ви знаходитесь у звичайній `def` *функції обробки шляху*, Ви можете отримати доступ до `UploadFile.file` безпосередньо, наприклад:
|
||||
|
||||
```Python
|
||||
contents = myfile.file.read()
|
||||
```
|
||||
|
||||
/// note | Технічні деталі `async`
|
||||
|
||||
Коли Ви використовуєте `async` методи, **FastAPI** виконує файлові операції у пулі потоків та очікує їх завершення.
|
||||
|
||||
///
|
||||
|
||||
/// note | Технічні деталі Starlette
|
||||
|
||||
`UploadFile` у **FastAPI** успадковується безпосередньо від `UploadFile` у **Starlette**, але додає деякі необхідні частини, щоб зробити його сумісним із **Pydantic** та іншими компонентами FastAPI.
|
||||
|
||||
///
|
||||
|
||||
## Що таке "Form Data"
|
||||
|
||||
Спосіб, у який HTML-форми (`<form></form>`) надсилають дані на сервер, зазвичай використовує "спеціальне" кодування, відмінне від JSON.
|
||||
|
||||
**FastAPI** забезпечує правильне зчитування цих даних з відповідної частини запиту, а не з JSON.
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Дані з форм зазвичай кодуються за допомогою "media type" `application/x-www-form-urlencoded`, якщо вони не містять файлів.
|
||||
|
||||
Але якщо форма містить файли, вона кодується у форматі `multipart/form-data`. Якщо Ви використовуєте `File`, **FastAPI** визначить, що потрібно отримати файли з відповідної частини тіла запиту.
|
||||
|
||||
Щоб дізнатися більше про ці типи кодування та формові поля, ознайомтеся з <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network">документацією MDN</abbr> щодо <code>POST</code></a>.
|
||||
|
||||
///
|
||||
|
||||
/// warning | Увага
|
||||
|
||||
Ви можете оголосити кілька параметрів `File` і `Form` в *операції шляху*, але Ви не можете одночасно оголошувати поля `Body`, які мають надходити у форматі JSON, оскільки тіло запиту буде закодоване у форматі `multipart/form-data`, а не `application/json`.
|
||||
|
||||
Це не обмеження **FastAPI**, а особливість протоколу HTTP.
|
||||
|
||||
///
|
||||
|
||||
## Опціональне Завантаження Файлів
|
||||
|
||||
Файл можна зробити необов’язковим, використовуючи стандартні анотації типів і встановлюючи значення за замовчуванням `None`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
|
||||
|
||||
## `UploadFile` із Додатковими Мета Даними
|
||||
|
||||
Ви також можете використовувати `File()` разом із `UploadFile`, наприклад, для встановлення додаткових метаданих:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}
|
||||
|
||||
## Завантаження Кількох Файлів
|
||||
|
||||
Можна завантажувати кілька файлів одночасно.
|
||||
|
||||
Вони будуть пов’язані з одним і тим самим "form field", який передається у вигляді "form data".
|
||||
|
||||
Щоб це реалізувати, потрібно оголосити список `bytes` або `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial002_an_py39.py hl[10,15] *}
|
||||
|
||||
Ви отримаєте, як і було оголошено, `list` із `bytes` або `UploadFile`.
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Ви також можете використати `from starlette.responses import HTMLResponse`.
|
||||
|
||||
**FastAPI** надає ті ж самі `starlette.responses`, що й `fastapi.responses`, для зручності розробників. Однак більшість доступних відповідей надходять безпосередньо від Starlette.
|
||||
|
||||
///
|
||||
|
||||
### Завантаження декількох файлів із додатковими метаданими
|
||||
|
||||
Так само як і раніше, Ви можете використовувати `File()`, щоб встановити додаткові параметри навіть для `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}
|
||||
|
||||
## Підсумок
|
||||
|
||||
Використовуйте `File`, `bytes`та `UploadFile`, щоб оголошувати файли для завантаження у запитах, які надсилаються у вигляді form data.
|
||||
@@ -0,0 +1,78 @@
|
||||
# Моделі форм (Form Models)
|
||||
|
||||
У FastAPI Ви можете використовувати **Pydantic-моделі** для оголошення **полів форми**.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Щоб використовувати форми, спочатку встановіть <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">python-multipart</a>.
|
||||
|
||||
Переконайтеся, що Ви створили [віртуальне середовище](../virtual-environments.md){.internal-link target=_blank}, активували його, а потім встановили бібліотеку, наприклад:
|
||||
|
||||
```console
|
||||
$ pip install python-multipart
|
||||
```
|
||||
|
||||
///
|
||||
|
||||
/// note | Підказка
|
||||
|
||||
Ця функція підтримується, починаючи з FastAPI версії `0.113.0`. 🤓
|
||||
|
||||
///
|
||||
|
||||
## Використання Pydantic-моделей для форм
|
||||
|
||||
Вам просто потрібно оголосити **Pydantic-модель** з полями, які Ви хочете отримати як **поля форми**, а потім оголосити параметр як `Form`:
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial001_an_py39.py hl[9:11,15] *}
|
||||
|
||||
**FastAPI** **витягне** дані для **кожного поля** з **формових даних** у запиті та надасть вам Pydantic-модель, яку Ви визначили.
|
||||
|
||||
## Перевірка документації
|
||||
|
||||
Ви можете перевірити це в UI документації за `/docs`:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/request-form-models/image01.png">
|
||||
</div>
|
||||
|
||||
## Заборона додаткових полів форми
|
||||
|
||||
У деяких особливих випадках (ймовірно, рідко) Ви можете **обмежити** форму лише тими полями, які були оголошені в Pydantic-моделі, і **заборонити** будь-які **додаткові** поля.
|
||||
|
||||
/// note | Підказка
|
||||
|
||||
Ця функція підтримується, починаючи з FastAPI версії `0.114.0`. 🤓
|
||||
|
||||
///
|
||||
|
||||
Ви можете використати конфігурацію Pydantic-моделі, щоб заборонити `forbid` будь-які додаткові `extra` поля:
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial002_an_py39.py hl[12] *}
|
||||
|
||||
Якщо клієнт спробує надіслати додаткові дані, він отримає **відповідь з помилкою**.
|
||||
|
||||
Наприклад, якщо клієнт спробує надіслати наступні поля форми:
|
||||
|
||||
* `username`: `Rick`
|
||||
* `password`: `Portal Gun`
|
||||
* `extra`: `Mr. Poopybutthole`
|
||||
|
||||
Він отримає відповідь із помилкою, яка повідомляє, що поле `extra` не дозволено:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "extra_forbidden",
|
||||
"loc": ["body", "extra"],
|
||||
"msg": "Extra inputs are not permitted",
|
||||
"input": "Mr. Poopybutthole"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Підсумок
|
||||
|
||||
Ви можете використовувати Pydantic-моделі для оголошення полів форми у FastAPI. 😎
|
||||
@@ -0,0 +1,41 @@
|
||||
# Запити з формами та файлами
|
||||
|
||||
У FastAPI Ви можете одночасно отримувати файли та поля форми, використовуючи `File` і `Form`.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Щоб отримувати завантажені файли та/або дані форми, спочатку встановіть <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">python-multipart</a>.
|
||||
|
||||
Переконайтеся, що Ви створили [віртуальне середовище](../virtual-environments.md){.internal-link target=_blank}, активували його, а потім встановили бібліотеку, наприклад:
|
||||
|
||||
```console
|
||||
$ pip install python-multipart
|
||||
```
|
||||
|
||||
///
|
||||
|
||||
## Імпорт `File` та `Form`
|
||||
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[3] *}
|
||||
|
||||
## Оголошення параметрів `File` та `Form`
|
||||
|
||||
Створіть параметри файлів та форми так само як і для `Body` або `Query`:
|
||||
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[10:12] *}
|
||||
|
||||
Файли та поля форми будуть завантажені як формові дані, і Ви отримаєте як файли, так і введені користувачем поля.
|
||||
|
||||
Ви також можете оголосити деякі файли як `bytes`, а деякі як `UploadFile`.
|
||||
|
||||
/// warning | Увага
|
||||
|
||||
Ви можете оголосити кілька параметрів `File` і `Form` в операції *шляху*, але не можете одночасно оголошувати `Body`-поля, які очікуєте отримати у форматі JSON, оскільки запит матиме тіло, закодоване за допомогою `multipart/form-data`, а не `application/json`.
|
||||
|
||||
Це не обмеження **FastAPI**, а частина протоколу HTTP.
|
||||
|
||||
///
|
||||
|
||||
## Підсумок
|
||||
|
||||
Використовуйте `File` та `Form` разом, коли вам потрібно отримувати дані форми та файли в одному запиті.
|
||||
@@ -0,0 +1,73 @@
|
||||
# Дані форми
|
||||
|
||||
Якщо Вам потрібно отримувати поля форми замість JSON, Ви можете використовувати `Form`.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Щоб використовувати форми, спочатку встановіть <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>.
|
||||
|
||||
Переконайтеся, що Ви створили [віртуальне середовище](../virtual-environments.md){.internal-link target=_blank}, активували його, і потім встановили бібліотеку, наприклад:
|
||||
|
||||
```console
|
||||
$ pip install python-multipart
|
||||
```
|
||||
|
||||
///
|
||||
|
||||
## Імпорт `Form`
|
||||
|
||||
Імпортуйте `Form` з `fastapi`:
|
||||
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[3] *}
|
||||
|
||||
## Оголошення параметрів `Form`
|
||||
|
||||
Створюйте параметри форми так само як Ви б створювали `Body` або `Query`:
|
||||
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[9] *}
|
||||
|
||||
Наприклад, один зі способів використання специфікації OAuth2 (так званий "password flow") вимагає надсилати `username` та `password` як поля форми.
|
||||
|
||||
<abbr title="Специфікація">spec</abbr> вимагає, щоб ці поля мали точні назви `username` і `password` та надсилалися у вигляді полів форми, а не JSON.
|
||||
|
||||
З `Form` Ви можете оголошувати ті ж конфігурації, що і з `Body` (та `Query`, `Path`, `Cookie`), включаючи валідацію, приклади, псевдоніми (наприклад, `user-name` замість `username`) тощо.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
`Form` — це клас, який безпосередньо наслідується від `Body`.
|
||||
|
||||
///
|
||||
|
||||
/// tip | Порада
|
||||
|
||||
Щоб оголосити тіло форми, потрібно явно використовувати `Form`, оскільки без нього параметри будуть інтерпретуватися як параметри запиту або тіла (JSON).
|
||||
|
||||
///
|
||||
|
||||
## Про "поля форми"
|
||||
|
||||
HTML-форми (`<form></form>`) надсилають дані на сервер у "спеціальному" кодуванні, яке відрізняється від JSON.
|
||||
|
||||
**FastAPI** подбає про те, щоб зчитати ці дані з правильного місця, а не з JSON.
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Дані з форм зазвичай кодуються за допомогою "типу медіа" `application/x-www-form-urlencoded`.
|
||||
|
||||
Але якщо форма містить файли, вона кодується як `multipart/form-data`. Ви дізнаєтеся про обробку файлів у наступному розділі.
|
||||
|
||||
Якщо Ви хочете дізнатися більше про ці кодування та поля форм, зверніться до <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network">MDN</abbr> вебдокументації для <code>POST</code></a>.
|
||||
|
||||
///
|
||||
|
||||
/// warning | Попередження
|
||||
|
||||
Ви можете оголосити кілька параметрів `Form` в *операції шляху*, але не можете одночасно оголосити поля `Body`, які Ви очікуєте отримати у форматі JSON, оскільки тіло запиту буде закодовано у форматі `application/x-www-form-urlencoded`, а не `application/json`.
|
||||
|
||||
Це не обмеження **FastAPI**, а частина HTTP-протоколу.
|
||||
|
||||
///
|
||||
|
||||
## Підсумок
|
||||
|
||||
Використовуйте `Form` для оголошення вхідних параметрів у вигляді даних форми.
|
||||
@@ -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`, щоб повертати лише явно встановлені значення.
|
||||
@@ -0,0 +1,100 @@
|
||||
# Статус коди Відповідей
|
||||
|
||||
Так само як Ви можете вказати модель відповіді, Ви також можете оголосити HTTP код статусу для відповіді за допомогою параметра `status_code` в будь-якій з *операцій шляху*:
|
||||
|
||||
* `@app.get()`
|
||||
* `@app.post()`
|
||||
* `@app.put()`
|
||||
* `@app.delete()`
|
||||
* тощо.
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
|
||||
|
||||
/// note | Нотатка
|
||||
|
||||
Зверніть увагу, що `status_code` є параметром методу "декоратора" (`get`, `post` і т.д.), а не Вашої *функції операції шляху*, як усі інші параметри та тіло запиту.
|
||||
|
||||
///
|
||||
|
||||
Параметр `status_code` приймає число, яке відповідає HTTP коду статусу.
|
||||
|
||||
/// info | Інформація
|
||||
`status_code` також може отримувати значення з `IntEnum`, наприклад, з Python <a href="https://docs.python.org/3/library/http.html#http.HTTPStatus" class="external-link" target="_blank">`http.HTTPStatus`</a>.
|
||||
|
||||
///
|
||||
|
||||
Він буде:
|
||||
|
||||
* Повертати вказаний код статусу у відповіді.
|
||||
* Документувати його як такий у схемі OpenAPI (і, таким чином, в інтерфейсі користувача):
|
||||
|
||||
<img src="/img/tutorial/response-status-code/image01.png">
|
||||
|
||||
/// note | Нотатка
|
||||
|
||||
Деякі коди відповіді (див. наступний розділ) вказують, що відповідь не має тіла.
|
||||
|
||||
FastAPI знає про це і створить OpenAPI документацію, яка вказує, що тіла відповіді немає.
|
||||
|
||||
///
|
||||
|
||||
## Про HTTP статус коди
|
||||
|
||||
/// note | Нотатка
|
||||
|
||||
Якщо Ви вже знаєте, що таке HTTP коди статусу, переходьте до наступного розділу.
|
||||
|
||||
///
|
||||
|
||||
В HTTP Ви надсилаєте числовий код статусу з 3 цифр як частину відповіді.
|
||||
|
||||
Ці коди статусу мають пов’язану назву для їх розпізнавання, але найважливішою частиною є саме число.
|
||||
|
||||
Коротко:
|
||||
|
||||
* **`100 - 199`** "Інформаційні" відповіді. Ви рідко використовуєте їх напряму. Відповіді з такими кодами не можуть мати тіла.
|
||||
* **`200 - 299`** "Успішні" відповіді. Це ті, які Ви використовуватимете найчастіше.
|
||||
* `200` - код за замовчуванням, який означає, що все пройшло "OK".
|
||||
* Інший приклад – `201`, "Created" (створено). Його зазвичай використовують після створення нового запису в базі даних.
|
||||
* Особливий випадок – `204`, "No Content" (немає вмісту). Ця відповідь використовується, коли немає даних для повернення клієнту, тому відповідь не повинна мати тіла.
|
||||
* **`300 - 399`** "Перенаправлення". Відповіді з цими кодами можуть мати або не мати тіла, за винятком `304`, "Not Modified" (не змінено), яка не повинна мати тіла.
|
||||
* **`400 - 499`** "Помилка клієнта". Це другий тип, який Ви, ймовірно, будете використовувати найчастіше.
|
||||
* Приклад `404`, "Not Found" (не знайдено).
|
||||
* Для загальних помилок клієнта можна використовувати `400`.
|
||||
* `500 - 599` "Помилки сервера". Ви майже ніколи не використовуєте їх напряму. Якщо в коді Вашого застосунку або на сервері щось пішло не так, автоматично буде повернено один із цих кодів статусу.
|
||||
|
||||
/// tip | Порада
|
||||
|
||||
Щоб дізнатися більше про кожен код статусу і призначення кожного з них, перегляньте документацію <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank"><abbr title="Mozilla Developer Network">MDN</abbr> про HTTP коди статусу</a>.
|
||||
|
||||
///
|
||||
|
||||
## Легкий спосіб запам'ятати назви
|
||||
|
||||
Розглянемо ще раз попередній приклад:
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
|
||||
|
||||
`201` - це код статусу для "Created" (створено).
|
||||
|
||||
Але Вам не потрібно запам'ятовувати, що означає кожен із цих кодів.
|
||||
|
||||
Ви можете використовувати зручні змінні з `fastapi.status`
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *}
|
||||
|
||||
Ці змінні просто для зручності. Вони містять ті ж самі числа, але Ви можете скористатися автозаповненням в редакторі:
|
||||
|
||||
<img src="/img/tutorial/response-status-code/image02.png">
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Ви також можете використати `from starlette import status`.
|
||||
|
||||
**FastAPI** надає ті ж самі змінні `starlette.status` як `fastapi.status`, просто для зручності розробника. Однак вони походять безпосередньо зі Starlette.
|
||||
|
||||
///
|
||||
|
||||
## Зміна значення за замовчуванням
|
||||
|
||||
Далі, у Посібнику для досвідчених користувачів{.internal-link target=_blank}, Ви дізнаєтесь, як повернути інший код статусу, ніж той, який Ви оголосили тут.
|
||||
@@ -0,0 +1,222 @@
|
||||
# Декларування прикладів вхідних даних
|
||||
|
||||
Ви можете задати приклади даних, які Ваш застосунок може отримувати.
|
||||
|
||||
Ось кілька способів, як це зробити.
|
||||
|
||||
## Додаткові дані JSON-схеми в моделях Pydantic
|
||||
|
||||
Ви можете задати `examples` для моделі Pydantic, які буде додано до згенерованої JSON-схеми.
|
||||
|
||||
//// tab | Pydantic v2
|
||||
|
||||
{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *}
|
||||
|
||||
////
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
{* ../../docs_src/schema_extra_example/tutorial001_pv1_py310.py hl[13:23] *}
|
||||
|
||||
////
|
||||
|
||||
Ця додаткова інформація буде додана як є до **JSON-схеми**, і вона буде використовуватися в документації до API.
|
||||
|
||||
//// tab | Pydantic v2
|
||||
|
||||
У версії Pydantic 2 використовується атрибут `model_config`, який приймає `dict`, як описано в <a href="https://docs.pydantic.dev/latest/api/config/" class="external-link" target="_blank">документації Pydantic: Конфігурація</a>.
|
||||
|
||||
Ви можете встановити `"json_schema_extra"` як `dict`, що містить будь-які додаткові дані, які Ви хочете відобразити у згенерованій JSON-схемі, включаючи `examples`.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
У версії Pydantic 1 використовується внутрішній клас `Config` і параметр `schema_extra`, як описано в <a href="https://docs.pydantic.dev/1.10/usage/schema/#schema-customization" class="external-link" target="_blank">документації Pydantic: Налаштування схеми</a>.
|
||||
|
||||
Ви можете задати `schema_extra` як `dict`, що містить будь-які додаткові дані, які Ви хочете бачити у згенерованій JSON-схемі, включаючи `examples`.
|
||||
|
||||
////
|
||||
|
||||
/// tip | Підказка
|
||||
|
||||
Ви можете використати ту ж техніку, щоб розширити JSON-схему і додати власну додаткову інформацію.
|
||||
|
||||
Наприклад, Ви можете використати її для додавання метаданих для інтерфейсу користувача на фронтенді тощо.
|
||||
|
||||
///
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
OpenAPI 3.1.0 (який використовується починаючи з FastAPI 0.99.0) додав підтримку `examples`, що є частиною стандарту **JSON-схеми**.
|
||||
|
||||
До цього підтримувався лише ключ `example` з одним прикладом. Він все ще підтримується в OpenAPI 3.1.0, але є застарілим і не входить до стандарту JSON Schema. Тому рекомендується перейти з `example` на `examples`. 🤓
|
||||
|
||||
Більше про це можна прочитати в кінці цієї сторінки.
|
||||
|
||||
///
|
||||
|
||||
## Додаткові аргументи `Field`
|
||||
|
||||
Коли ви використовуєте `Field()` у моделях Pydantic, Ви також можете вказати додаткові `examples`:
|
||||
|
||||
{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *}
|
||||
|
||||
## `examples` у JSON-схемі — OpenAPI
|
||||
|
||||
При використанні будь-кого з наступного:
|
||||
|
||||
* `Path()`
|
||||
* `Query()`
|
||||
* `Header()`
|
||||
* `Cookie()`
|
||||
* `Body()`
|
||||
* `Form()`
|
||||
* `File()`
|
||||
|
||||
Ви також можете задати набір `examples` з додатковою інформацією, яка буде додана до їхніх **JSON-схем** у **OpenAPI**.
|
||||
|
||||
### `Body` з `examples`
|
||||
|
||||
Тут ми передаємо `examples`, які містять один приклад очікуваних даних у `Body()`:
|
||||
|
||||
{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *}
|
||||
|
||||
### Приклад у UI документації
|
||||
|
||||
За допомогою будь-якого з наведених вище методів це виглядатиме так у документації за `/docs`:
|
||||
|
||||
<img src="/img/tutorial/body-fields/image01.png">
|
||||
|
||||
### `Body` з кількома `examples`
|
||||
|
||||
Звичайно, Ви також можете передати кілька `examples`:
|
||||
|
||||
{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *}
|
||||
|
||||
Коли Ви це робите, приклади будуть частиною внутрішньої **JSON-схеми** для цих даних.
|
||||
|
||||
Втім, на момент написання цього (<abbr title="2023-08-26">26 серпня 2023</abbr>), Swagger UI — інструмент, який відповідає за відображення UI документації — не підтримує показ кількох прикладів у **JSON-схеми**. Але нижче можна прочитати про обхідний шлях.
|
||||
|
||||
### Специфічні для OpenAPI `examples`
|
||||
|
||||
Ще до того, як **JSON-схема** почала підтримувати `examples`, OpenAPI вже мала підтримку поля з такою ж назвою — `examples`.
|
||||
|
||||
Це **специфічне для OpenAPI** поле `examples` розміщується в іншій частині специфікації OpenAPI — у **деталях кожної *операції шляху***, а не всередині самої JSON-схеми.
|
||||
|
||||
Swagger UI вже давно підтримує це поле `examples`. Тому Ви можете використовувати його, щоб **відображати** кілька **прикладів у документації**.
|
||||
|
||||
Це поле `examples` у специфікації OpenAPI — це `dict` (словник) з **кількома прикладами** (а не список `list`), кожен із яких може містити додаткову інформацію, що буде додана до **OpenAPI**.
|
||||
|
||||
Воно не включається до JSON Schema кожного параметра, а розміщується зовні, безпосередньо в *операції шляху*.
|
||||
|
||||
### Використання параметра `openapi_examples`
|
||||
|
||||
Ви можете оголосити специфічні для OpenAPI `examples` у FastAPI за допомогою параметра `openapi_examples` для:
|
||||
|
||||
* `Path()`
|
||||
* `Query()`
|
||||
* `Header()`
|
||||
* `Cookie()`
|
||||
* `Body()`
|
||||
* `Form()`
|
||||
* `File()`
|
||||
|
||||
Ключі словника (`dict`) ідентифікують кожен приклад, а кожне значення `dict` — кожен специфічний словник `dict` в `examples` може містити:
|
||||
|
||||
* `summary`: короткий опис прикладу.
|
||||
* `description`: розгорнутий опис (може містити Markdown).
|
||||
* `value`: сам приклад, наприклад, словник (`dict`).
|
||||
* `externalValue`: альтернатива `value`, URL-адреса, що вказує на приклад. Проте ця опція може не підтримуватися більшістю інструментів, на відміну від `value`.
|
||||
|
||||
Використання виглядає так:
|
||||
|
||||
{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *}
|
||||
|
||||
### Приклади OpenAPI у UI документації
|
||||
|
||||
З параметром `openapi_examples`, доданим до `Body()`, документація `/docs` виглядатиме так:
|
||||
|
||||
<img src="/img/tutorial/body-fields/image02.png">
|
||||
|
||||
## Технічні деталі
|
||||
|
||||
/// tip | Підказка
|
||||
|
||||
Якщо Ви вже використовуєте **FastAPI** версії **0.99.0 або вище**, Ви можете **пропустити** цей розділ.
|
||||
|
||||
Він більш актуальний для старих версій, до появи OpenAPI 3.1.0.
|
||||
|
||||
Можна вважати це коротким **історичним екскурсом** у OpenAPI та JSON Schema. 🤓
|
||||
|
||||
///
|
||||
|
||||
/// warning | Попередження
|
||||
|
||||
Це дуже технічна інформація про стандарти **JSON Schema** і **OpenAPI**.
|
||||
|
||||
Якщо вищезгадані ідеї вже працюють у Вас — можете не заглиблюватися в ці деталі.
|
||||
|
||||
///
|
||||
|
||||
До OpenAPI 3.1.0 специфікація використовувала стару та модифіковану версію **JSON Schema**.
|
||||
|
||||
Оскільки JSON Schema раніше не підтримувала `examples`, OpenAPI додала власне поле `examples`.
|
||||
|
||||
OpenAPI також додала `example` і `examples` до інших частин специфікації:
|
||||
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object" class="external-link" target="_blank">`Parameter Object` (в специфікації)</a> використовується FastAPI для:
|
||||
* `Path()`
|
||||
* `Query()`
|
||||
* `Header()`
|
||||
* `Cookie()`
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object" class="external-link" target="_blank">`Request Body Object`, в полі `content`, в `Media Type Object` (в специфікації)</a> використовується FastAPI для:
|
||||
* `Body()`
|
||||
* `File()`
|
||||
* `Form()`
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Цей старий параметр `examples`, специфічний для OpenAPI, тепер називається `openapi_examples`, починаючи з FastAPI версії `0.103.0`.
|
||||
|
||||
///
|
||||
|
||||
### Поле `examples` у JSON Schema
|
||||
|
||||
Пізніше JSON Schema додала поле <a href="https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5" class="external-link" target="_blank">`examples`</a> у нову версію специфікації.
|
||||
|
||||
І вже OpenAPI 3.1.0 базується на цій новій версії (JSON Schema 2020-12), яка включає поле `examples`.
|
||||
|
||||
Тепер це поле `examples` є пріоритетним і замінює старе (і кастомне) поле `example`, яке стало застарілим.
|
||||
|
||||
Нове поле `examples` у JSON Schema — це **просто список (`list`)** прикладів, без додаткових метаданих (на відміну від OpenAPI).
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Навіть після того, як з'явився OpenAPI 3.1.0, який підтримував examples у JSON Schema, інструмент Swagger UI ще деякий час не підтримував цю версію (підтримка з’явилась з версії 5.0.0 🎉).
|
||||
|
||||
Через це версії FastAPI до 0.99.0 все ще використовували версії OpenAPI нижчі за 3.1.0.
|
||||
|
||||
///
|
||||
|
||||
### `Examples` в Pydantic і FastAPI
|
||||
|
||||
Коли Ви додаєте `examples` у модель Pydantic через `schema_extra` або `Field(examples=["something"])`, ці приклади додаються до **JSON Schema** цієї моделі.
|
||||
|
||||
І ця **JSON Schema** Pydantic-моделі включається до **OpenAPI** Вашого API, а потім використовується в UI документації (docs UI).
|
||||
|
||||
У версіях FastAPI до 0.99.0 (починаючи з 0.99.0 використовується новіший OpenAPI 3.1.0), коли Ви використовували `example` або `examples` з іншими утилітами (`Query()`, `Body()` тощо), ці приклади не додавалися до JSON Schema, який описує ці дані (навіть не до власної версії JSON Schema у OpenAPI). Натомість вони додавалися безпосередньо до опису *обробника шляху* *(path operation)* в OpenAPI (тобто поза межами частин, які використовують JSON Schema).
|
||||
|
||||
Але тепер, коли FastAPI 0.99.0 і вище використовують OpenAPI 3.1.0, а той — JSON Schema 2020-12, разом із Swagger UI 5.0.0 і вище — все стало більш узгодженим, і examples тепер включаються до JSON Schema.
|
||||
|
||||
### Swagger UI та специфічні для OpenAPI `examples`
|
||||
|
||||
Раніше (станом на 26 серпня 2023 року) Swagger UI не підтримував кілька прикладів у JSON Schema, тому користувачі не мали можливості показати декілька прикладів у документації.
|
||||
|
||||
Щоб вирішити це, FastAPI починаючи з версії 0.103.0 **додав підтримку** старого **OpenAPI-специфічного** поля `examples` через новий параметр `openapi_examples`. 🤓
|
||||
|
||||
### Підсумок
|
||||
|
||||
Раніше я казав, що не люблю історію... а тепер ось я — розповідаю "технічні історичні" лекції. 😅
|
||||
|
||||
Коротко: **оновіться до FastAPI 0.99.0 або вище** — і все стане значно **простішим, узгодженим та інтуїтивно зрозумілим**, і Вам не доведеться знати всі ці історичні деталі. 😎
|
||||
@@ -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.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Статичні файли
|
||||
|
||||
Ви можете автоматично надавати статичні файли з каталогу, використовуючи `StaticFiles`.
|
||||
|
||||
## Використання `StaticFiles`
|
||||
|
||||
* Імпортуйте `StaticFiles`.
|
||||
* "Під'єднати" екземпляр `StaticFiles()` з вказанням необхідного шляху.
|
||||
|
||||
{* ../../docs_src/static_files/tutorial001.py hl[2,6] *}
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Ви також можете використовувати `from starlette.staticfiles import StaticFiles`.
|
||||
|
||||
**FastAPI** надає той самий `starlette.staticfiles`, що й `fastapi.staticfiles` для зручності розробників. Але фактично він безпосередньо походить із Starlette.
|
||||
|
||||
///
|
||||
|
||||
### Що таке "Під'єднання"
|
||||
|
||||
"Під'єднання" означає додавання повноцінного "незалежного" застосунку за певним шляхом, який потім обробляє всі під шляхи.
|
||||
|
||||
Це відрізняється від використання `APIRouter`, оскільки під'єднаний застосунок є повністю незалежним. OpenAPI та документація вашого основного застосунку не будуть знати нічого про ваш під'єднаний застосунок.
|
||||
|
||||
Ви можете дізнатися більше про це в [Посібнику для просунутих користувачів](../advanced/index.md){.internal-link target=_blank}.
|
||||
|
||||
## Деталі
|
||||
|
||||
Перше `"/static"` вказує на під шлях, за яким буде "під'єднано" цей новий "застосунок". Тому будь-який шлях, який починається з `"/static"`, буде оброблятися ним.
|
||||
|
||||
`directory="static"` визначає каталог, що містить ваші статичні файли.
|
||||
|
||||
`name="static"` це ім'я, яке можна використовувати всередині **FastAPI**.
|
||||
|
||||
Усі ці параметри можуть бути змінені відповідно до потреб і особливостей вашого застосунку.
|
||||
|
||||
## Додаткова інформація
|
||||
|
||||
Детальніше про налаштування та можливості можна дізнатися в <a href="https://www.starlette.dev/staticfiles/" class="external-link" target="_blank">документації Starlette про статичні файли</a>.
|
||||
@@ -0,0 +1,240 @@
|
||||
# Тестування
|
||||
|
||||
Тестування **FastAPI** додатків є простим та ефективним завдяки бібліотеці <a href="https://www.starlette.dev/testclient/" class="external-link" target="_blank">Starlette</a>, яка базується на <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>.
|
||||
Оскільки HTTPX розроблений на основі Requests, його API є інтуїтивно зрозумілим для тих, хто вже знайомий з Requests.
|
||||
|
||||
З його допомогою Ви можете використовувати <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a> безпосередньо з **FastAPI**.
|
||||
|
||||
## Використання `TestClient`
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Щоб використовувати `TestClient`, спочатку встановіть <a href="https://www.python-httpx.org" class="external-link" target="_blank">`httpx`</a>.
|
||||
|
||||
Переконайтеся, що Ви створили [віртуальне середовище](../virtual-environments.md){.internal-link target=_blank}, активували його, а потім встановили саму бібліотеку, наприклад:
|
||||
|
||||
```console
|
||||
$ pip install httpx
|
||||
```
|
||||
|
||||
///
|
||||
|
||||
Імпортуйте `TestClient`.
|
||||
|
||||
Створіть `TestClient`, передавши йому Ваш застосунок **FastAPI**.
|
||||
|
||||
Створюйте функції з іменами, що починаються з `test_` (це стандартна угода для `pytest`).
|
||||
|
||||
Використовуйте об'єкт `TestClient` так само як і `httpx`.
|
||||
|
||||
Записуйте прості `assert`-вирази зі стандартними виразами Python, які потрібно перевірити (це також стандарт для `pytest`).
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial001.py hl[2,12,15:18] *}
|
||||
|
||||
|
||||
/// tip | Порада
|
||||
|
||||
Зверніть увагу, що тестові функції — це звичайні `def`, а не `async def`.
|
||||
|
||||
Виклики клієнта також звичайні, без використання `await`.
|
||||
|
||||
Це дозволяє використовувати `pytest` без зайвих ускладнень.
|
||||
|
||||
///
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Ви також можете використовувати `from starlette.testclient import TestClient`.
|
||||
|
||||
**FastAPI** надає той самий `starlette.testclient` під назвою `fastapi.testclient` для зручності розробників, але він безпосередньо походить із Starlette.
|
||||
|
||||
///
|
||||
|
||||
/// tip | Порада
|
||||
|
||||
Якщо Вам потрібно викликати `async`-функції у ваших тестах, окрім відправлення запитів до FastAPI-застосунку (наприклад, асинхронні функції роботи з базою даних), перегляньте [Асинхронні тести](../advanced/async-tests.md){.internal-link target=_blank} у розширеному керівництві.
|
||||
|
||||
///
|
||||
|
||||
## Розділення тестів
|
||||
|
||||
У реальному застосунку Ваші тести, ймовірно, будуть в окремому файлі.
|
||||
|
||||
Також Ваш **FastAPI**-застосунок може складатися з кількох файлів або модулів тощо.
|
||||
|
||||
### Файл застосунку **FastAPI**
|
||||
|
||||
Припустимо, у Вас є структура файлів, описана в розділі [Більші застосунки](bigger-applications.md){.internal-link target=_blank}:
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ └── main.py
|
||||
```
|
||||
У файлі `main.py` знаходиться Ваш застосунок **FastAPI** :
|
||||
|
||||
{* ../../docs_src/app_testing/main.py *}
|
||||
|
||||
### Файл тестування
|
||||
|
||||
Ви можете створити файл `test_main.py` з Вашими тестами. Він може знаходитися в тому ж пакеті Python (у тій самій директорії з файлом `__init__.py`):
|
||||
|
||||
|
||||
``` hl_lines="5"
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py
|
||||
│ └── test_main.py
|
||||
```
|
||||
|
||||
Оскільки цей файл знаходиться в тому ж пакеті, Ви можете використовувати відносний імпорт, щоб імпортувати об'єкт `app` із модуля `main` (`main.py`):
|
||||
|
||||
{* ../../docs_src/app_testing/test_main.py hl[3] *}
|
||||
|
||||
|
||||
...і написати код для тестів так само як і раніше.
|
||||
|
||||
## Тестування: розширений приклад
|
||||
|
||||
Тепер розширимо цей приклад і додамо більше деталей, щоб побачити, як тестувати різні частини.
|
||||
|
||||
### Розширений файл застосунку **FastAPI**
|
||||
|
||||
Залишимо ту саму структуру файлів:
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py
|
||||
│ └── test_main.py
|
||||
```
|
||||
|
||||
Припустимо, що тепер файл `main.py` із Вашим **FastAPI**-застосунком містить додаткові операції шляху (**path operations**).
|
||||
|
||||
Він має `GET`-операцію, яка може повертати помилку.
|
||||
|
||||
Він має `POST`-операцію, яка може повертати кілька помилок.
|
||||
|
||||
Обидві операції шляху вимагають заголовок `X-Token`.
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
{!> ../../docs_src/app_testing/app_b_an_py310/main.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
{!> ../../docs_src/app_testing/app_b_an_py39/main.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python
|
||||
{!> ../../docs_src/app_testing/app_b_an/main.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip | Порада
|
||||
|
||||
Бажано використовувати версію з `Annotated`, якщо це можливо
|
||||
|
||||
///
|
||||
|
||||
```Python
|
||||
{!> ../../docs_src/app_testing/app_b_py310/main.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip | Порада
|
||||
|
||||
Бажано використовувати версію з `Annotated`, якщо це можливо
|
||||
|
||||
///
|
||||
|
||||
```Python
|
||||
{!> ../../docs_src/app_testing/app_b/main.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
### Розширений тестовий файл
|
||||
|
||||
Потім Ви можете оновити `test_main.py`, додавши розширені тести:
|
||||
|
||||
{* ../../docs_src/app_testing/app_b/test_main.py *}
|
||||
|
||||
Коли Вам потрібно передати клієнту інформацію в запиті, але Ви не знаєте, як це зробити, Ви можете пошукати (наприклад, у Google) спосіб реалізації в `httpx`, або навіть у `requests`, оскільки HTTPX розроблений на основі дизайну Requests.
|
||||
|
||||
Далі Ви просто повторюєте ці ж дії у ваших тестах.
|
||||
|
||||
Наприклад:
|
||||
|
||||
* Щоб передати *path* або *query* параметр, додайте його безпосередньо до URL.
|
||||
* Щоб передати тіло JSON, передайте Python-об'єкт (наприклад, `dict`) у параметр `json`.
|
||||
* Якщо потрібно надіслати *Form Data* замість JSON, використовуйте параметр `data`.
|
||||
* Щоб передати заголовки *headers*, використовуйте `dict` у параметрі `headers`.
|
||||
* Для *cookies* використовуйте `dict` у параметрі `cookies`.
|
||||
|
||||
Докладніше про передачу даних у бекенд (за допомогою `httpx` або `TestClient`) можна знайти в <a href="https://www.python-httpx.org" class="external-link" target="_blank">документації HTTPX</a>.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Зверніть увагу, що `TestClient` отримує дані, які можна конвертувати в JSON, а не Pydantic-моделі.
|
||||
Якщо у Вас є Pydantic-модель у тесті, і Ви хочете передати її дані в додаток під час тестування, Ви можете використати `jsonable_encoder`, описаний у розділі [JSON Compatible Encoder](encoder.md){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
## Запуск тестів
|
||||
|
||||
Після цього вам потрібно встановити `pytest`.
|
||||
|
||||
Переконайтеся, що Ви створили [віртуальне середовище]{.internal-link target=_blank}, активували його і встановили необхідні пакети, наприклад:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install pytest
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
`pytest` автоматично знайде файли з тестами, виконає їх і надасть вам результати.
|
||||
|
||||
Запустіть тести за допомогою:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pytest
|
||||
|
||||
================ test session starts ================
|
||||
platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1
|
||||
rootdir: /home/user/code/superawesome-cli/app
|
||||
plugins: forked-1.1.3, xdist-1.31.0, cov-2.8.1
|
||||
collected 6 items
|
||||
|
||||
---> 100%
|
||||
|
||||
test_main.py <span style="color: green; white-space: pre;">...... [100%]</span>
|
||||
|
||||
<span style="color: green;">================= 1 passed in 0.03s =================</span>
|
||||
```
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
INHERIT: ../en/mkdocs.yml
|
||||
Reference in New Issue
Block a user