Перейти до змісту

Більші застосунки - кілька файлів

🌐 Переклад ШІ та людьми

Цей переклад виконано ШІ під керівництвом людей. 🤝

Можливі помилки через неправильне розуміння початкового змісту або неприродні формулювання тощо. 🤖

Ви можете покращити цей переклад, допомігши нам краще спрямовувати AI LLM.

Англійська версія

Якщо ви створюєте застосунок або веб-API, рідко вдається вмістити все в один файл.

FastAPI надає зручний інструмент для структурування вашого застосунку, зберігаючи всю гнучкість.

Інформація

Якщо ви прийшли з Flask, це еквівалент «Blueprints» у Flask.

Приклад структури файлів

Припустімо, у вас така структура файлів:

.
├── app
│   ├── __init__.py
│   ├── main.py
│   ├── dependencies.py
│   └── routers
│   │   ├── __init__.py
│   │   ├── items.py
│   │   └── users.py
│   └── internal
│       ├── __init__.py
│       └── admin.py

Порада

Тут кілька файлів __init__.py: по одному в кожному каталозі та підкаталозі.

Саме це дозволяє імпортувати код з одного файлу в інший.

Наприклад, у app/main.py ви можете мати рядок:

from app.routers import items
  • Каталог app містить усе. І він має порожній файл app/__init__.py, тож це «пакет Python» (збірка «модулів Python»): app.
  • Він містить файл app/main.py. Оскільки він усередині пакета Python (каталог з файлом __init__.py), це «модуль» цього пакета: app.main.
  • Є також файл app/dependencies.py, так само як app/main.py, це «модуль»: app.dependencies.
  • Є підкаталог app/routers/ з іншим файлом __init__.py, отже це «підпакет Python»: app.routers.
  • Файл app/routers/items.py знаходиться в пакеті app/routers/, отже це підмодуль: app.routers.items.
  • Так само і app/routers/users.py, це інший підмодуль: app.routers.users.
  • Є також підкаталог app/internal/ з іншим файлом __init__.py, отже це інший «підпакет Python»: app.internal.
  • І файл app/internal/admin.py - ще один підмодуль: app.internal.admin.

Та сама структура файлів з коментарями:

.
├── app                  # «app» - це пакет Python
   ├── __init__.py      # цей файл робить «app» «пакетом Python»
   ├── main.py          # модуль «main», напр. import app.main
   ├── dependencies.py  # модуль «dependencies», напр. import app.dependencies
   └── routers          # «routers» - це «підпакет Python»
      ├── __init__.py  # робить «routers» «підпакетом Python»
      ├── items.py     # підмодуль «items», напр. import app.routers.items
      └── users.py     # підмодуль «users», напр. import app.routers.users
   └── internal         # «internal» - це «підпакет Python»
       ├── __init__.py  # робить «internal» «підпакетом Python»
       └── admin.py     # підмодуль «admin», напр. import app.internal.admin

APIRouter

Припустімо, файл, присвячений обробці лише користувачів, - це підмодуль у /app/routers/users.py.

Ви хочете мати операції шляху, пов'язані з користувачами, відокремлено від решти коду, щоб зберегти порядок.

Але це все одно частина того самого застосунку/веб-API FastAPI (це частина того самого «пакета Python»).

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

Імпортуйте APIRouter

Імпортуйте його та створіть «екземпляр» так само, як ви б робили з класом FastAPI:

app/routers/users.py
from fastapi import APIRouter

router = APIRouter()


@router.get("/users/", tags=["users"])
async def read_users():
    return [{"username": "Rick"}, {"username": "Morty"}]


@router.get("/users/me", tags=["users"])
async def read_user_me():
    return {"username": "fakecurrentuser"}


@router.get("/users/{username}", tags=["users"])
async def read_user(username: str):
    return {"username": username}

Операції шляху з APIRouter

Потім використовуйте його для оголошення операцій шляху.

Використовуйте його так само, як і клас FastAPI:

app/routers/users.py
from fastapi import APIRouter

router = APIRouter()


@router.get("/users/", tags=["users"])
async def read_users():
    return [{"username": "Rick"}, {"username": "Morty"}]


@router.get("/users/me", tags=["users"])
async def read_user_me():
    return {"username": "fakecurrentuser"}


@router.get("/users/{username}", tags=["users"])
async def read_user(username: str):
    return {"username": username}

Можете думати про APIRouter як про «міні FastAPI».

Підтримуються ті самі опції.

Ті самі parameters, responses, dependencies, tags тощо.

Порада

У цьому прикладі змінна називається router, але ви можете назвати її як завгодно.

Ми включимо цей APIRouter у основний застосунок FastAPI, але спочатку розгляньмо залежності та інший APIRouter.

Залежності

Бачимо, що нам знадобляться кілька залежностей, які використовуються в різних місцях застосунку.

Тож помістимо їх у власний модуль dependencies (app/dependencies.py).

Тепер використаємо просту залежність для читання користувацького заголовка X-Token:

app/dependencies.py
from typing import Annotated

from fastapi import Header, HTTPException


async def get_token_header(x_token: Annotated[str, Header()]):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")


async def get_query_token(token: str):
    if token != "jessica":
        raise HTTPException(status_code=400, detail="No Jessica token provided")
🤓 Other versions and variants
app/dependencies.py
from typing import Annotated

from fastapi import Header, HTTPException


async def get_token_header(x_token: Annotated[str, Header()]):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")


async def get_query_token(token: str):
    if token != "jessica":
        raise HTTPException(status_code=400, detail="No Jessica token provided")

Tip

Prefer to use the Annotated version if possible.

app/dependencies.py
from fastapi import Header, HTTPException


async def get_token_header(x_token: str = Header()):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")


async def get_query_token(token: str):
    if token != "jessica":
        raise HTTPException(status_code=400, detail="No Jessica token provided")

Порада

Ми використовуємо вигаданий заголовок, щоб спростити приклад.

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

Інший модуль з APIRouter

Припустімо, у вас також є кінцеві точки для обробки «items» у модулі app/routers/items.py.

У вас є операції шляху для:

  • /items/
  • /items/{item_id}

Структура така сама, як у app/routers/users.py.

Але ми хочемо бути розумнішими й трохи спростити код.

Ми знаємо, що всі операції шляху в цьому модулі мають однакові:

  • Префікс шляху prefix: /items.
  • tags: (лише одна мітка: items).
  • Додаткові responses.
  • dependencies: усім потрібна залежність X-Token, яку ми створили.

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

app/routers/items.py
from fastapi import APIRouter, Depends, HTTPException

from ..dependencies import get_token_header

router = APIRouter(
    prefix="/items",
    tags=["items"],
    dependencies=[Depends(get_token_header)],
    responses={404: {"description": "Not found"}},
)


fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}


@router.get("/")
async def read_items():
    return fake_items_db


@router.get("/{item_id}")
async def read_item(item_id: str):
    if item_id not in fake_items_db:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"name": fake_items_db[item_id]["name"], "item_id": item_id}


@router.put(
    "/{item_id}",
    tags=["custom"],
    responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
    if item_id != "plumbus":
        raise HTTPException(
            status_code=403, detail="You can only update the item: plumbus"
        )
    return {"item_id": item_id, "name": "The great Plumbus"}

Оскільки шлях кожної операції шляху має починатися з /, як у:

@router.get("/{item_id}")
async def read_item(item_id: str):
    ...

...префікс не має містити кінцевий /.

Отже, у цьому випадку префікс - це /items.

Ми також можемо додати список tags та додаткові responses, які застосовуватимуться до всіх операцій шляху, включених у цей router.

І ми можемо додати список dependencies, які буде додано до всіх операцій шляху у router і які виконуватимуться/вирішуватимуться для кожного запиту до них.

Порада

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

У підсумку шляхи предметів тепер:

  • /items/
  • /items/{item_id}

...як ми і планували.

  • Вони будуть позначені списком міток, що містить один рядок "items".
    • Ці «мітки» особливо корисні для автоматичної інтерактивної документації (за допомогою OpenAPI).
  • Усі вони включатимуть наперед визначені responses.
  • Для всіх цих операцій шляху список dependencies буде оцінений/виконаний перед ними.
    • Якщо ви також оголосите залежності в конкретній операції шляху, вони також будуть виконані.
    • Спочатку виконуються залежності router'а, потім dependencies у декораторі, а потім звичайні параметричні залежності.
    • Ви також можете додати Security залежності з scopes.

Порада

Наявність dependencies у APIRouter можна використати, наприклад, щоб вимагати автентифікацію для всієї групи операцій шляху. Навіть якщо залежності не додані до кожної з них окремо.

Перевірте

Параметри prefix, tags, responses і dependencies - це (як і в багатьох інших випадках) просто можливість FastAPI, яка допомагає уникати дублювання коду.

Імпортуйте залежності

Цей код живе в модулі app.routers.items, у файлі app/routers/items.py.

І нам потрібно отримати функцію залежності з модуля app.dependencies, файлу app/dependencies.py.

Тож ми використовуємо відносний імпорт з .. для залежностей:

app/routers/items.py
from fastapi import APIRouter, Depends, HTTPException

from ..dependencies import get_token_header

router = APIRouter(
    prefix="/items",
    tags=["items"],
    dependencies=[Depends(get_token_header)],
    responses={404: {"description": "Not found"}},
)


fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}


@router.get("/")
async def read_items():
    return fake_items_db


@router.get("/{item_id}")
async def read_item(item_id: str):
    if item_id not in fake_items_db:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"name": fake_items_db[item_id]["name"], "item_id": item_id}


@router.put(
    "/{item_id}",
    tags=["custom"],
    responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
    if item_id != "plumbus":
        raise HTTPException(
            status_code=403, detail="You can only update the item: plumbus"
        )
    return {"item_id": item_id, "name": "The great Plumbus"}

Як працюють відносні імпорти

Порада

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

Одна крапка ., як у:

from .dependencies import get_token_header

означає:

  • Починаючи в тому самому пакеті, де знаходиться цей модуль (файл app/routers/items.py) (каталог app/routers/)...
  • знайти модуль dependencies (уявний файл app/routers/dependencies.py)...
  • і з нього імпортувати функцію get_token_header.

Але такого файлу не існує, наші залежності у файлі app/dependencies.py.

Згадайте, як виглядає структура нашого застосунку/файлів:


Дві крапки .., як у:

from ..dependencies import get_token_header

означають:

  • Починаючи в тому самому пакеті, де знаходиться цей модуль (файл app/routers/items.py) (каталог app/routers/)...
  • перейти до батьківського пакета (каталог app/)...
  • і там знайти модуль dependencies (файл app/dependencies.py)...
  • і з нього імпортувати функцію get_token_header.

Це працює правильно! 🎉


Так само, якби ми використали три крапки ..., як у:

from ...dependencies import get_token_header

це б означало:

  • Починаючи в тому самому пакеті, де знаходиться цей модуль (файл app/routers/items.py) (каталог app/routers/)...
  • перейти до батьківського пакета (каталог app/)...
  • потім перейти до батьківського пакета від того (немає батьківського пакета, app - верхній рівень 😱)...
  • і там знайти модуль dependencies (файл app/dependencies.py)...
  • і з нього імпортувати функцію get_token_header.

Це б посилалося на якийсь пакет вище за app/ з власним файлом __init__.py тощо. Але в нас такого немає. Тож у нашому прикладі це спричинить помилку. 🚨

Але тепер ви знаєте, як це працює, тож можете використовувати відносні імпорти у власних застосунках, незалежно від їхньої складності. 🤓

Додайте користувацькі tags, responses і dependencies

Ми не додаємо префікс /items ані tags=["items"] до кожної операції шляху, бо додали їх до APIRouter.

Але ми все ще можемо додати ще tags, які будуть застосовані до конкретної операції шляху, а також додаткові responses, специфічні для цієї операції шляху:

app/routers/items.py
from fastapi import APIRouter, Depends, HTTPException

from ..dependencies import get_token_header

router = APIRouter(
    prefix="/items",
    tags=["items"],
    dependencies=[Depends(get_token_header)],
    responses={404: {"description": "Not found"}},
)


fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}


@router.get("/")
async def read_items():
    return fake_items_db


@router.get("/{item_id}")
async def read_item(item_id: str):
    if item_id not in fake_items_db:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"name": fake_items_db[item_id]["name"], "item_id": item_id}


@router.put(
    "/{item_id}",
    tags=["custom"],
    responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
    if item_id != "plumbus":
        raise HTTPException(
            status_code=403, detail="You can only update the item: plumbus"
        )
    return {"item_id": item_id, "name": "The great Plumbus"}

Порада

Остання операція шляху матиме комбінацію міток: ["items", "custom"].

І вона також матиме в документації обидві відповіді: одну для 404 і одну для 403.

Основний FastAPI

Тепер розгляньмо модуль app/main.py.

Тут ви імпортуєте і використовуєте клас FastAPI.

Це буде головний файл вашого застосунку, який усе поєднує.

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

Імпортуйте FastAPI

Імпортуйте та створіть клас FastAPI, як зазвичай.

І ми навіть можемо оголосити глобальні залежності, які будуть поєднані із залежностями кожного APIRouter:

app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}
🤓 Other versions and variants
app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

Tip

Prefer to use the Annotated version if possible.

app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

Імпортуйте APIRouter

Тепер імпортуємо інші підмодулі, що мають APIRouter:

app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}
🤓 Other versions and variants
app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

Tip

Prefer to use the Annotated version if possible.

app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

Оскільки файли app/routers/users.py та app/routers/items.py - це підмодулі, що є частиною того самого пакета Python app, ми можемо використати одну крапку . для «відносних імпортів».

Як працює імпорт

Розділ:

from .routers import items, users

означає:

  • Починаючи в тому самому пакеті, де знаходиться цей модуль (файл app/main.py) (каталог app/)...
  • знайти підпакет routers (каталог app/routers/)...
  • і з нього імпортувати підмодулі items (файл app/routers/items.py) і users (файл app/routers/users.py)...

Модуль items матиме змінну router (items.router). Це та сама, що ми створили у файлі app/routers/items.py, це об’єкт APIRouter.

Потім ми робимо те саме для модуля users.

Ми також могли б імпортувати їх так:

from app.routers import items, users

Інформація

Перша версія - «відносний імпорт»:

from .routers import items, users

Друга версія - «абсолютний імпорт»:

from app.routers import items, users

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

Уникайте колізій імен

Ми імпортуємо підмодуль items напряму, замість імпорту лише його змінної router.

Це тому, що в підмодулі users також є змінна з назвою router.

Якби ми імпортували один за одним, як:

from .routers.items import router
from .routers.users import router

router з users перезаписав би той, що з items, і ми не змогли б використовувати їх одночасно.

Щоб мати змогу використовувати обидва в одному файлі, ми імпортуємо підмодулі напряму:

app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}
🤓 Other versions and variants
app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

Tip

Prefer to use the Annotated version if possible.

app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

Додайте APIRouter для users і items

Тепер додаймо router з підмодулів users і items:

app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}
🤓 Other versions and variants
app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

Tip

Prefer to use the Annotated version if possible.

app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

Інформація

users.router містить APIRouter у файлі app/routers/users.py.

А items.router містить APIRouter у файлі app/routers/items.py.

За допомогою app.include_router() ми можемо додати кожен APIRouter до основного застосунку FastAPI.

Це включить усі маршрути з цього router'а як частину застосунку.

Технічні деталі

Фактично, всередині для кожної операції шляху, оголошеної в APIRouter, буде створена окрема операція шляху.

Тобто за лаштунками все працюватиме так, ніби це один і той самий застосунок.

Перевірте

Вам не потрібно перейматися продуктивністю під час включення router'ів.

Це займе мікросекунди і відбуватиметься лише під час запуску.

Тож це не вплине на продуктивність. ⚡

Додайте APIRouter з власними prefix, tags, responses і dependencies

Уявімо, що ваша організація надала вам файл app/internal/admin.py.

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

Для цього прикладу він буде дуже простим. Але припустімо, що оскільки його спільно використовують з іншими проєктами організації, ми не можемо модифікувати його та додавати prefix, dependencies, tags тощо прямо до APIRouter:

app/internal/admin.py
from fastapi import APIRouter

router = APIRouter()


@router.post("/")
async def update_admin():
    return {"message": "Admin getting schwifty"}

Але ми все одно хочемо встановити користувацький prefix під час включення APIRouter, щоб усі його операції шляху починалися з /admin, хочемо захистити його за допомогою dependencies, які вже є в цьому проєкті, і хочемо додати tags та responses.

Ми можемо оголосити все це, не змінюючи оригінальний APIRouter, передавши ці параметри до app.include_router():

app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}
🤓 Other versions and variants
app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

Tip

Prefer to use the Annotated version if possible.

app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

Таким чином, вихідний APIRouter залишиться без змін, тож ми все ще зможемо спільно використовувати той самий файл app/internal/admin.py з іншими проєктами в організації.

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

  • Префікс /admin.
  • Мітку admin.
  • Залежність get_token_header.
  • Відповідь 418. 🍵

Але це вплине лише на цей APIRouter у нашому застосунку, а не на будь-який інший код, який його використовує.

Наприклад, інші проєкти можуть використовувати той самий APIRouter з іншим методом автентифікації.

Додайте операцію шляху

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

Тут ми це робимо... просто щоб показати, що так можна 🤷:

app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}
🤓 Other versions and variants
app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

Tip

Prefer to use the Annotated version if possible.

app/main.py
from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])


app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)


@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

і це працюватиме коректно разом з усіма іншими операціями шляху, доданими через app.include_router().

Дуже технічні деталі

Примітка: це дуже технічна деталь, яку ви, ймовірно, можете просто пропустити.


APIRouter не «монтуються», вони не ізольовані від решти застосунку.

Це тому, що ми хочемо включати їхні операції шляху в схему OpenAPI та інтерфейси користувача.

Оскільки ми не можемо просто ізолювати їх і «змонтувати» незалежно від решти, операції шляху «клонуються» (створюються заново), а не включаються безпосередньо.

Перевірте автоматичну документацію API

Тепер запустіть ваш застосунок:

$ fastapi dev app/main.py

<span style="color: green;">INFO</span>:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

І відкрийте документацію за адресою http://127.0.0.1:8000/docs.

Ви побачите автоматичну документацію API, що включає шляхи з усіх підмодулів, з правильними шляхами (і префіксами) та правильними мітками:

Включайте той самий router кілька разів з різними prefix

Ви також можете використовувати .include_router() кілька разів з одним і тим самим router'ом, але з різними префіксами.

Це може бути корисно, наприклад, щоб публікувати той самий API під різними префіксами, наприклад /api/v1 і /api/latest.

Це просунуте використання, яке вам може й не знадобитися, але воно є на випадок, якщо потрібно.

Включіть один APIRouter до іншого

Так само як ви можете включити APIRouter у застосунок FastAPI, ви можете включити APIRouter в інший APIRouter, використовуючи:

router.include_router(other_router)

Переконайтеся, що ви робите це до включення router в застосунок FastAPI, щоб операції шляху з other_router також були включені.