Класи як залежності¶
🌐 Переклад ШІ та людьми
Цей переклад виконано ШІ під керівництвом людей. 🤝
Можливі помилки через неправильне розуміння початкового змісту або неприродні формулювання тощо. 🤖
Ви можете покращити цей переклад, допомігши нам краще спрямовувати AI LLM.
Перш ніж заглибитися у систему впровадження залежностей, оновімо попередній приклад.
dict з попереднього прикладу¶
У попередньому прикладі ми повертали dict із нашого «залежного»:
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
return commons
@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
return commons
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
return commons
@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
return commons
Але тоді ми отримуємо dict у параметрі commons функції операції шляху.
І ми знаємо, що редактори не можуть надати багато підтримки (наприклад, автодоповнення) для dict, адже вони не знають їхніх ключів і типів значень.
Можна зробити краще…
Що робить об’єкт залежністю¶
Дотепер ви бачили залежності, оголошені як функції.
Але це не єдиний спосіб оголошувати залежності (хоча, ймовірно, найпоширеніший).
Ключовий момент у тому, що залежність має бути «викликаємим».
«Викликаємий» у Python - це будь-що, що Python може «викликати», як функцію.
Отже, якщо у вас є об’єкт something (який може й не бути функцією) і ви можете «викликати» його (виконати) так:
something()
або
something(some_argument, some_keyword_argument="foo")
тоді це «викликаємий».
Класи як залежності¶
Ви могли помітити, що для створення екземпляра класу Python ви використовуєте той самий синтаксис.
Наприклад:
class Cat:
def __init__(self, name: str):
self.name = name
fluffy = Cat(name="Mr Fluffy")
У цьому випадку fluffy - екземпляр класу Cat.
А для створення fluffy ви «викликаєте» Cat.
Отже, клас Python також є викликаємим.
Тож у FastAPI ви можете використовувати клас Python як залежність.
Насправді FastAPI перевіряє, що це «викликаємий» об’єкт (функція, клас чи щось інше) і які параметри в нього оголошені.
Якщо ви передаєте «викликаємий» як залежність у FastAPI, він проаналізує параметри цього об’єкта і обробить їх так само, як параметри для функції операції шляху. Включно з підзалежностями.
Це також стосується викликаємих без жодних параметрів. Так само, як і для функцій операцій шляху без параметрів.
Тоді ми можемо змінити залежність common_parameters вище на клас CommonQueryParams:
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
Зверніть увагу на метод __init__, який використовують для створення екземпляра класу:
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
…він має ті самі параметри, що й наш попередній common_parameters:
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
return commons
@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
return commons
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
return commons
@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
return commons
Саме ці параметри FastAPI використає, щоб «розв’язати» залежність.
В обох випадках буде:
- Необов’язковий параметр запиту
q, який єstr. - Параметр запиту
skip, який єint, зі значенням за замовчуванням0. - Параметр запиту
limit, який єint, зі значенням за замовчуванням100.
В обох випадках дані будуть перетворені, перевірені й задокументовані в схемі OpenAPI тощо.
Використання¶
Тепер ви можете оголосити залежність, використовуючи цей клас.
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
FastAPI викликає клас CommonQueryParams. Це створює «екземпляр» цього класу, і цей екземпляр буде передано як параметр commons у вашу функцію.
Анотація типів проти Depends¶
Зверніть увагу, що вище ми двічі пишемо CommonQueryParams:
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
Порада
Надавайте перевагу варіанту з Annotated, якщо це можливо.
commons: CommonQueryParams = Depends(CommonQueryParams)
Останній CommonQueryParams у:
... Depends(CommonQueryParams)
- це те, що FastAPI фактично використає, щоб дізнатися, яка залежність.
Саме з нього FastAPI витягне оголошені параметри і саме його FastAPI буде викликати.
У цьому випадку перший CommonQueryParams у:
commons: Annotated[CommonQueryParams, ...
Порада
Надавайте перевагу варіанту з Annotated, якщо це можливо.
commons: CommonQueryParams ...
…не має жодного особливого значення для FastAPI. FastAPI не використовуватиме його для перетворення даних, перевірки тощо (адже для цього використовується Depends(CommonQueryParams)).
Насправді ви могли б написати просто:
commons: Annotated[Any, Depends(CommonQueryParams)]
Порада
Надавайте перевагу варіанту з Annotated, якщо це можливо.
commons = Depends(CommonQueryParams)
…як у:
from typing import Annotated, Any
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: Annotated[Any, Depends(CommonQueryParams)]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons=Depends(CommonQueryParams)):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
Але оголошувати тип рекомендується - так ваш редактор знатиме, що буде передано в параметр commons, і зможе допомагати з автодоповненням, перевірками типів тощо:

Скорочення¶
Але ви бачите, що тут маємо деяке дублювання коду - CommonQueryParams пишемо двічі:
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
Порада
Надавайте перевагу варіанту з Annotated, якщо це можливо.
commons: CommonQueryParams = Depends(CommonQueryParams)
FastAPI надає скорочення для таких випадків, коли залежність - це саме клас, який FastAPI «викличе», щоб створити екземпляр цього класу.
Для таких випадків ви можете зробити так:
Замість того щоб писати:
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
Порада
Надавайте перевагу варіанту з Annotated, якщо це можливо.
commons: CommonQueryParams = Depends(CommonQueryParams)
…напишіть:
commons: Annotated[CommonQueryParams, Depends()]
Порада
Надавайте перевагу варіанту з Annotated, якщо це можливо.
commons: CommonQueryParams = Depends()
Ви оголошуєте залежність як тип параметра і використовуєте Depends() без параметрів, замість того щоб вдруге писати повний клас усередині Depends(CommonQueryParams).
Той самий приклад виглядатиме так:
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends()]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends()):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
…і FastAPI знатиме, що робити.
Порада
Якщо це здається заплутанішим, ніж корисним, просто не використовуйте це - воно не є обов’язковим.
Це лише скорочення. Адже FastAPI дбає про мінімізацію дублювання коду.