Встановлення FastAPI та ваш перший застосунок¶
Цей розділ запускає застосунок FastAPI і будує той самий невеликий API двома способами — раз на FastAPI, раз на Django — щоб форма різниці була відчутною від самого початку. Обидві версії постачаються як супровідні проєкти, які можна запустити й протестувати.
Встановлення¶
FastAPI — це звичайний пакет із PyPI. Створіть віртуальне середовище й
встановіть його — додаток [standard] підтягує ті частини, які вам
насправді знадобляться в розробці (сервер Uvicorn, CLI fastapi, httpx
для тестового клієнта, Jinja2 та підтримку форм/файлів):
Це і все встановлення. Немає жодного startproject, жодного settings.py,
жодного згенерованого дерева каталогів — ви створюєте порожній файл .py
і починаєте писати.
З досвіду Django:
pip install Django, потімdjango-admin startproject config .розкладає пакет проєкту (settings.py,urls.py,wsgi.py,asgi.py) іmanage.py. FastAPI не розкладає нічого. Плюс — нуль церемоній; мінус — структура, налаштування та точки входу стають рішеннями, які ви ухвалюєте самі (у темі 1 є цілий розділ про розкладку проєкту).
Ваш перший застосунок¶
Помістіть це у main.py:
from fastapi import FastAPI, HTTPException
app = FastAPI(title="First App (FastAPI)")
ITEMS = [
{"id": 1, "name": "Widget"},
{"id": 2, "name": "Gadget"},
{"id": 3, "name": "Gizmo"},
]
@app.get("/")
def read_root():
return {"message": "It works!"}
@app.get("/items")
def list_items(limit: int = 10):
return {"items": ITEMS[:limit]}
@app.get("/items/{item_id}")
def get_item(item_id: int):
for item in ITEMS:
if item["id"] == item_id:
return item
raise HTTPException(status_code=404, detail="Item not found")
Відбуваються три речі, і всі вони керуються сигнатурами функцій:
- Маршрутизація — це декоратор.
@app.get("/items")прив'язує шлях і HTTP-метод безпосередньо до функції. Немає окремої таблиці URL. - Поверніть
dict— отримаєте JSON. FastAPI серіалізує повернуте значення й виставляєcontent-type: application/json. Для типового випадку ви взагалі не торкаєтеся об'єкта відповіді. - Параметри типізовані й валідовані.
item_id: intозначає, що сегмент шляху розбирається й валідується як ціле число;limit: int = 10стає необов'язковим параметром запиту зі значенням за замовчуванням.
Запуск¶
fastapi dev main.py # the fastapi[standard] CLI, with auto-reload
# or, equivalently:
uvicorn main:app --reload
Відкрийте http://127.0.0.1:8000/items, а потім http://127.0.0.1:8000/docs для інтерактивного Swagger UI, який FastAPI згенерував із тих самих сигнатур.
З досвіду Django:
fastapi dev main.py— цеmanage.py runserverцього світу: dev-сервер з автоперезавантаженням. Різниця під капотом:runserver— це WSGI dev-сервер, а Uvicorn — ASGI-сервер, і UI/docsне має відповідника вrunserver— щоб отримати щось подібне, ви б потягнулися до Browsable API з DRF абоdrf-spectacular.
Той самий API у Django¶
Ось той самий API, побудований на в'ю Django. Django потребує пакета
проєкту, застосунку, таблиці URL і в'ю, що повертають JsonResponse:
# config/urls.py
from django.urls import path
from items import views
urlpatterns = [
path("", views.read_root),
path("items", views.list_items),
path("items/<int:item_id>", views.get_item),
]
# items/views.py
from django.http import JsonResponse
ITEMS = [
{"id": 1, "name": "Widget"},
{"id": 2, "name": "Gadget"},
{"id": 3, "name": "Gizmo"},
]
def read_root(request):
return JsonResponse({"message": "It works!"})
def list_items(request):
limit = int(request.GET.get("limit", 10)) # convert/validate by hand
return JsonResponse({"items": ITEMS[:limit]})
def get_item(request, item_id):
for item in ITEMS:
if item["id"] == item_id:
return JsonResponse(item)
return JsonResponse({"detail": "Item not found"}, status=404)
Поставте їх поруч — і розподіл обов'язків стає очевидним:
| FastAPI | Django | |
|---|---|---|
| Прив'язка URL → обробник | декоратор @app.get("/items") |
запис у urlpatterns |
| Читання параметра запиту | limit: int = 10 (розбір + валідація) |
int(request.GET.get("limit", 10)) (вручну) |
| Повернення JSON | return {...} |
return JsonResponse({...}) |
| Сигнал «не знайдено» | raise HTTPException(404, ...) |
JsonResponse({...}, status=404) |
| Інтерактивна документація | вбудована на /docs |
не вбудована |
Одна різниця, яку варто помітити вже зараз: погані вхідні дані¶
Запросіть /items/abc — не-ціле число там, де очікується цілий id — і два
фреймворки розходяться:
GET /items/abc
# FastAPI → 422 Unprocessable Entity
# {"detail":[{"type":"int_parsing","loc":["path","item_id"], ...}]}
# Django → 404 Not Found
FastAPI оголошує item_id: int, тож він валідує шлях і повертає
структурований 422 з описом того, що було не так. URL-конвертер
<int:item_id> у Django просто не збігається з не-цілим числом, тож
резолвер провалюється до 404 — в'ю ніколи не виконується. Той самий
намір (відхилити погані вхідні дані), різний механізм і різний код статусу.
Обидва супровідні проєкти мають тест, що фіксує саме цю поведінку.
З досвіду Django: У DRF ви б отримали помилки валідації у стилі FastAPI (
400/422) від серіалізатора, але чистий Django залишає валідацію окремих параметрів на вас — викликint()уlist_itemsвище підняв биValueError(500) на?limit=abc. Модель FastAPI «підказка типу — це валідація» ближча до DRF, ніж до в'ю Django, але вона вбудована в сигнатуру функції, а не в окремий клас серіалізатора.
Супровідні проєкти¶
Обидва застосунки вище — це повноцінні робочі проєкти в каталозі
02-demo/ цього розділу:
02-demo/fastapi/(завантажити) —main.py, тести,requirements.txt. Запуск черезfastapi dev main.py; тестування черезpytest.02-demo/django/(завантажити) — проєктconfig/- застосунок
items/. Запуск черезpython manage.py runserver; тестування черезpython manage.py test.
Кожна сторона має невеликий набір тестів (шість тестів), що перевіряють
показані тут відповіді, включно з різницею 422/404. Кожен розділ,
починаючи звідси, постачається з такою парою.