Ачааллж байна...
Бүлэг 7-ийн төгсгөлд бид шинэ асуудлыг нэрлэсэн: давтагдсан логик. Router-ууд өсөхөд олон endpoint-д ижил зүйл хэрэгтэй болдог — ижил параметр, ижил шалгалт, ижил холболт. Тэр логикийг endpoint бүрт хуулах нь алдаатай, засахад бэрх.
Энэ бүлэг FastAPI-ийн шийдэлд зориулагдана: Depends, буюу хамаарлын систем (dependency injection). Энэ бол хуваалцсан логикийг нэг газар бичээд, олон endpoint руу автоматаар "тарих" механизм юм.
Эхлээд шударга сануулга: энэ ойлголт эхэндээ хийсвэр санагддаг. Олон хүн Depends-ыг анх харахад "яг юу болж байгаа юм бэ" гэж эргэлздэг. Энэ бол хэвийн. Тиймээс бид энэ хичээлд яарахгүй — жижиг жишээнээс эхэлж, аажмаар ойлголтыг барина. Дараагийн хоёр хичээлд түүнийг бодит ажилд хэрэглэхэд утга нь бүрэн тодорно.
Тэмдэглэлийн API-д гурван жагсаалтын endpoint байна гэж бодъё: бүх тэмдэглэл, дууссан тэмдэглэл, чухал тэмдэглэл. Гурвуулаа ижил хуудаслалтын параметр (skip, limit) хэрэгтэй:
python
@router.get("/notes")
def get_notes(skip: int = 0, limit: int = 10):
return notes[skip : skip + limit]
@router.get("/notes/done")
def get_done_notes(skip: int = 0, limit: int = 10):
result = [n for n in notes if n["done"]]
return result[skip : skip + limit]
@router.get("/notes/important")
def get_important_notes(skip: int = 0, limit: int = 10):
result = [n for n in notes if n["priority"] == "чухал"]
return result[skip : skip + limit]skip: int = 0, limit: int = 10 гурван удаа давтагдсан. Одоохондоо тэвчиж болно.
Гэхдээ асуудал өсдөг. Хэрэв та limit-ийн дээд хязгаарыг 100 болгож шалгах хэрэгтэй болбол — гурван газар нэмнэ. Хэрэв sort_by параметр нэмбэл — гурван газар. Хэрэв энэ параметрүүд арван endpoint дээр байвал — арван газар засна. Нэг газар мартвал API тогтворгүй болно.
Энэ бол программчлалын хамгийн эртний асуудал: давталт. Шийдэл нь ч мэдэгдэж байна: давтагдсан зүйлийг нэг газар гаргаж, дуудах. Функц бол яг тэр зорилготой хэрэгсэл.
Гэхдээ энд сорилт бий. Эдгээр параметрүүд нь endpoint-ийн параметр — FastAPI тэдгээрийг хаягнаас уншиж, шалгаж, дамжуулдаг. Энгийн функц дуудалт үүнийг хийж чадахгүй. Танд FastAPI-ийн параметрийн системд орж ажилладаг механизм хэрэгтэй.
Тэр механизм нь Depends.
Том зоогийн газрын гал тогоог төсөөлье. Хоол бүр өөр өөр жортой, гэхдээ бараг бүгд ижил бэлтгэл шаарддаг: ногоо угаах, хэрчих, амтлагч бэлдэх.
Муу зохион байгуулалт: тогооч бүр өөрийн ажлын байран дээр ногоогоо угааж, хэрчиж, амтлагчаа бэлддэг. Ижил ажил арван газар давтагдана. Хэрэв "ногоог одооноос хоёр удаа угаах" гэсэн шинэ дүрэм гарвал арван тогоочид тус тусад нь хэлэх ёстой. Нэгд нь хэлэхээ мартвал тэр хуучнаар үргэлжлүүлнэ.
Сайн зохион байгуулалт: гал тогооны нэг буланд нийтийн бэлтгэл станц байна. Тэнд ногоо угаагдаж, хэрчигдэж, амтлагч бэлдэгддэг. Тогооч бүр хоол хийхээсээ өмнө тэр станцаас бэлэн орц авдаг. Дүрэм өөрчлөгдвөл станц дээр нэг удаа өөрчилнө — бүх тогооч автоматаар шинэ дүрмээр ажиллана.
Dependency бол тэр бэлтгэл станц юм. Та түүнийг нэг газар бичнэ. Endpoint бүр "надад тэр станцын үр дүн хэрэгтэй" гэж зарлана. FastAPI станцыг ажиллуулж, үр дүнг нь endpoint рүү дамжуулна.
Гол ялгааг анзаараарай: тогооч станц руу очиж, ажиллуулж, буцаж ирдэггүй. Тогооч зөвхөн "надад бэлэн ногоо хэрэгтэй" гэж хэлдэг, ногоо түүний ширээн дээр гарч ирдэг. Энэ бол "injection" (тарилт) гэдэг үгийн утга — та зүйлийг татдаггүй, тэр танд өгөгддөг.
Одоо код руу орно. Хамгийн энгийн жишээнээс эхэлье — юу ч хийдэггүй, зөвхөн тогтмол утга буцаадаг dependency.
main.py файлаа дараах кодоор солино (энэ хичээлд бид нэг файлаар ажиллана, ойлголтод анхаарлаа төвлөрүүлэхийн тулд):
python
# main.py
from fastapi import FastAPI, Depends
app = FastAPI(title="Depends сурах")
def get_greeting():
return "Сайн байна уу"
@app.get("/hello")
def hello(greeting: str = Depends(get_greeting)):
return {"message": greeting}Хадгална.
Кодын задаргаа
from fastapi import FastAPI, Depends — Depends-ыг import хийж байна.
python
def get_greeting():
return "Сайн байна уу"Энэ бол dependency функц. Ердийн Python функц, ямар ч онцгой зүйл байхгүй. Decorator байхгүй, FastAPI-ийн зүйл байхгүй. Зүгээр л текст буцаана.
python
def hello(greeting: str = Depends(get_greeting)):Энэ бол гол мөр. Endpoint-ийн параметр дээр Depends(get_greeting) гэж бичсэн. Энэ нь FastAPI-д ингэж хэлж байна: "Энэ endpoint дуудагдахаас өмнө get_greeting функцийг ажиллуул, түүний буцаасан утгыг greeting параметрт хий."
Анзаараарай: Depends(get_greeting) гэж бичсэн, Depends(get_greeting()) биш. Хаалт байхгүй. Учир нь та функцийг дуудаж байгаа биш, харин FastAPI-д аль функцийг дуудахыг зааж байна. FastAPI өөрөө дуудна. Энэ бол хамгийн түгээмэл алдаа тул тодорхой санаж авах нь зүйтэй.
Туршина
http://127.0.0.1:8000/helloХариу:
json
{"message":"Сайн байна уу"}greeting параметр нь "Сайн байна уу" болсон — хэрэглэгч юу ч илгээгээгүй, get_greeting функц түүнийг өгсөн.
Гүйцэтгэлийн дараалал
Юу болсныг алхам алхмаар харъя. Хүсэлт /hello руу ирлээ. FastAPI hello функцийн параметрүүдийг шалгав. greeting параметр дээр Depends(get_greeting) байна. Тиймээс FastAPI эхлээд get_greeting()-ыг дуудав. Тэр "Сайн байна уу" буцаав. FastAPI тэр утгыг greeting-т хийж, дараа нь hello(greeting="Сайн байна уу")-г дуудав. Функц хариу буцаав.
Dependency нь endpoint-ээс өмнө ажилладаг. Энэ дараалал чухал — dependency нь "бэлтгэл", endpoint нь "үндсэн ажил".
Үүнийг батлахын тулд print нэмж үзье:
python
def get_greeting():
print("1. Dependency ажиллаж байна")
return "Сайн байна уу"
@app.get("/hello")
def hello(greeting: str = Depends(get_greeting)):
print("2. Endpoint ажиллаж байна")
return {"message": greeting}/hello руу хандвал терминал дээр:
1. Dependency ажиллаж байна
2. Endpoint ажиллаж байнаДараалал батлагдлаа. Dependency эхэлж, endpoint дараа нь.
Одоо илүү ашигтай жишээ рүү орно. Dependency функц нь өөрөө параметртэй байж болно — тэгвэл FastAPI тэдгээрийг хаягнаас уншина.
python
# main.py
from fastapi import FastAPI, Depends
app = FastAPI(title="Depends сурах")
notes = [
{"id": 1, "text": "Тэмдэглэл нэг"},
{"id": 2, "text": "Тэмдэглэл хоёр"},
{"id": 3, "text": "Тэмдэглэл гурав"},
{"id": 4, "text": "Тэмдэглэл дөрөв"},
{"id": 5, "text": "Тэмдэглэл тав"},
]
def pagination(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
@app.get("/notes")
def get_notes(page: dict = Depends(pagination)):
start = page["skip"]
end = start + page["limit"]
return notes[start:end]Хадгална.
Кодын задаргаа
python
def pagination(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}Dependency функц одоо хоёр параметртэй, тус бүр type hint болон default утгатай. Энэ нь endpoint-ийн параметртэй яг ижил харагдаж байна.
Учир нь FastAPI тэдгээрийг яг ижил аргаар боловсруулдаг: dependency функцийн параметрүүд нь endpoint-ийн параметр шиг ажилладаг. skip болон limit нь query parameter болно — FastAPI тэдгээрийг хаягнаас уншиж, шалгаж, хувиргаж, dependency функцэд дамжуулна.
python
def get_notes(page: dict = Depends(pagination)):Endpoint нь pagination-ийн буцаасан dictionary-г page нэрээр хүлээж авна.
Туршина
http://127.0.0.1:8000/notesХариу:
json
[{"id":1,"text":"Тэмдэглэл нэг"},{"id":2,"text":"Тэмдэглэл хоёр"},{"id":3,"text":"Тэмдэглэл гурав"},{"id":4,"text":"Тэмдэглэл дөрөв"},{"id":5,"text":"Тэмдэглэл тав"}]Бүх тав. skip=0, limit=10 (default) тул notes[0:10] — бүгд.
Одоо параметртэйгээр:
http://127.0.0.1:8000/notes?skip=1&limit=2Хариу:
json
[{"id":2,"text":"Тэмдэглэл хоёр"},{"id":3,"text":"Тэмдэглэл гурав"}]Эхний нэгийг алгасаж, хоёрыг авлаа. notes[1:3].
Энэ бол чухал мөч. skip болон limit параметрүүд нь get_notes endpoint дээр огт бичигдээгүй. Тэдгээр нь pagination функц дээр бичигдсэн. Гэтэл хаягнаас уншигдаж, шалгагдаж, ажилласан. FastAPI dependency функцийн параметрүүдийг endpoint-ийнх шиг үзсэн.
Validation ч ажиллана
http://127.0.0.1:8000/notes?skip=abcХариу (статус 422):
json
{
"detail": [
{
"type": "int_parsing",
"loc": ["query", "skip"],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "abc"
}
]
}Танил бүтэц. loc нь ["query", "skip"] — dependency доторх параметр ч гэсэн ердийн query parameter шиг шалгагдаж, ижил алдааны хариу өгч байна.
/docs дээр харагдана
/docs дээр GET /notes мөрийг задална. Parameters хэсэгт:
skip integer (query) Default: 0
limit integer (query) Default: 10Хоёр параметр харагдаж байна — endpoint дээр бичигдээгүй ч. FastAPI dependency-г шинжилж, түүний параметрүүдийг баримт бичигт оруулсан.
Хэрэглэгчийн зүгээс ямар ч ялгаа байхгүй. Тэд ?skip=1&limit=2 гэж бичнэ, endpoint ажиллана. Dependency бол зөвхөн кодыг зохион байгуулах дотоод механизм — API-ийн гадаад төрх өөрчлөгдөхгүй.
Хичээлийн эхэнд ярьсан асуудал руу буцъя. Гурван endpoint, ижил параметр. Одоо dependency-тэйгээр:
python
# main.py (нэмэлт)
@app.get("/notes/done")
def get_done_notes(page: dict = Depends(pagination)):
result = [n for n in notes if n.get("done")]
start = page["skip"]
return result[start : start + page["limit"]]
@app.get("/notes/important")
def get_important_notes(page: dict = Depends(pagination)):
result = [n for n in notes if n.get("priority") == "чухал"]
start = page["skip"]
return result[start : start + page["limit"]]Гурван endpoint, гурвуулаа Depends(pagination). skip: int = 0, limit: int = 10 гурван удаа бичигдээгүй — нэг удаа, pagination функц дээр.
Одоо дүрэм өөрчлөх нь хялбар. Жишээ нь limit-ийг 100-аар хязгаарлахыг хүсвэл зөвхөн нэг газар засна:
python
def pagination(skip: int = 0, limit: int = 10):
if limit > 100:
limit = 100
return {"skip": skip, "limit": limit}Гурван endpoint бүгд шинэ дүрмээр автоматаар ажиллана. Нэг газар, гурван (эсвэл гуч) ашиг.
Энэ бол бэлтгэл станцын зүйрлэлийн бодит утга: дүрэм нэг газар өөрчлөгдөхөд бүх тогооч шинэ дүрмээр ажилладаг.
Дээрх жишээнд dependency нь dictionary буцаасан. Гэхдээ юу ч буцааж болно: текст, тоо, объект, Pydantic model, эсвэл юу ч биш.
python
def get_current_year():
return 2026
@app.get("/year")
def year(y: int = Depends(get_current_year)):
return {"year": y}Тоо буцаах dependency. Ижил механизм.
Түвшин 4-т бид өгөгдлийн сангийн session буцаах dependency бичнэ. Тэр нь энэ бүлгийн хамгийн чухал хэрэглээ бөгөөд Бүлэг 12-т бид түүн рүү эргэж ирнэ. Одоохондоо dependency нь ямар ч төрлийн зүйл буцааж чадна гэдгийг мэдэж байхад хангалттай.
Шударга байя: бүх зүйлийг dependency болгох шаардлагагүй.
Хэрэв параметр зөвхөн нэг endpoint дээр хэрэгтэй бол шууд бичих нь илүү энгийн:
python
@app.get("/notes")
def get_notes(search: str = ""): # dependency хэрэггүй
...Dependency нь дараах үед утгатай:
Ижил логик хэд хэдэн endpoint-д хэрэгтэй.
Логик нь өөрөө нарийн (шалгалт, тооцоолол) бөгөөд тусад нь байвал ойлгомжтой.
Тэр логик ирээдүйд өөрчлөгдөх магадлалтай (нэг газар засах нь давуу тал).
Дүрэм: хоёр ба түүнээс дээш газар давтагдвал dependency болго. Ганц газар бол шууд бич.
Depends дотор функцийг дуудах
python
def get_notes(page: dict = Depends(pagination())): # хаалт байна — буруу!Depends(pagination()) нь pagination функцийг шууд дуудаж, түүний үр дүнг Depends-д өгч байна. Гэтэл Depends нь функц өөрийг нь хүлээж байна.
Гаралт (ойролцоогоор):
TypeError: pagination() missing ... argumentsэсвэл хачирхалтай зан төлөв. Засвар: хаалтыг ав — Depends(pagination).
Санаж авах арга: та функцийг дуудахгүй, FastAPI дуудна. Та зөвхөн заана.
Depends-ыг import хийхгүй
python
from fastapi import FastAPI
# Depends дутуу
@app.get("/notes")
def get_notes(page: dict = Depends(pagination)):
...Гаралт:
NameError: name 'Depends' is not definedЗасвар: from fastapi import FastAPI, Depends.
Dependency функц дээр decorator тавих
python
@app.get("/pagination") # буруу — dependency бол endpoint биш
def pagination(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}Dependency функц бол endpoint биш. Түүнд decorator хэрэггүй, хаяг хэрэггүй. Хэрэв decorator тавибал тэр нь нэмэлт endpoint болно (хэрэглэгч /pagination руу хандаж чадна) — таны хүсээгүй зүйл.
Dependency бол зөвхөн ердийн функц, Depends() дотор нэрлэгдэх л хангалттай.
Хичээлийн эхэнд амласанчлан шударга байя: энэ ойлголт эхэндээ хийсвэр байдаг.
"Функцийг зөвхөн нэрлэхэд FastAPI өөрөө дуудна" гэдэг нь ердийн Python сэтгэлгээнээс өөр. Курс 1-2-т та функцийг дуудаж сурсан: greet("Дулгуун"). Одоо та функцийг зааж байна, дуудахгүй.
Хэрэв энэ нь бүрэн тодорхой болоогүй бол санаа зовох хэрэггүй. Ойлголт нь хэрэглээгээр тодордог. Дараагийн хоёр хичээлд бид Depends-ыг бодит ажилд ашиглана: дахин ашиглагдах шүүлтүүр (Хичээл 2) болон API key хамгаалалт (Хичээл 3). Тэнд утга нь илүү тодорхой болно.
Түвшин 4-т, өгөгдлийн сангийн session-ыг Depends-ээр өгөх үед энэ ойлголт бүрэн байрандаа орно. Тэр мөчид та "аа, тиймээс ингэж хийдэг байсан юм байна" гэж хэлэх болно. Одоохондоо механизмыг мэдэж, жишээг дагаж чадвал хангалттай.
Хүсвэл pagination dependency-д гурав дахь параметр нэмж үзээрэй — жишээ нь sort: str = "id". Дараа нь /docs дээр шинэ параметр гурван endpoint дээр бүгдэд нь автоматаар гарч ирснийг ажиглаарай. Нэг газар нэмээд, гурван газар ашиглагдахыг гар дээрээ харах нь dependency-ийн утгыг тодруулна.
Сонирхвол print ашиглан dependency болон endpoint хэдэн удаа дуудагдаж байгааг ажиглаарай. Гурван endpoint руу дараалан хандвал pagination гурван удаа (хүсэлт бүрд нэг удаа) дуудагдана — dependency нь хүсэлт бүрд шинээр ажилладаг, нэг удаа тооцоолоод хадгалдаггүй.
Dependency бол хуваалцсан логикийг нэг газар бичээд олон endpoint руу автоматаар дамжуулах механизм.
Зүйрлэл: нийтийн бэлтгэл станц — тогооч бүр бэлэн орц авдаг, өөрөө бэлддэггүй.
Depends(функцийн_нэр) — хаалтгүй. Та функцийг дуудахгүй, FastAPI дуудна.
Dependency нь endpoint-ээс өмнө ажиллана.
Dependency функцийн параметрүүд endpoint-ийнх шиг ажилладаг — query parameter болж, шалгагдаж, /docs-д гарна.
Dependency юу ч буцааж болно: dictionary, тоо, объект, model.
Давталт арилна: дүрэм өөрчлөхөд нэг газар засна, бүх endpoint шинэчлэгдэнэ.
Дүрэм: хоёроос дээш газар давтагдвал dependency болго; ганц газар бол шууд бич.
Dependency функц бол endpoint биш — decorator тавихгүй.
Одоо та Depends-ийн механизмыг мэднэ. Дараагийн хичээлд бид түүнийг бодит ажилд хэрэглэнэ: дахин ашиглагдах query шүүлтүүрийн dependency бүтээж, олон endpoint дээр ашиглана. Тэнд бид мөн Бүлэг 13-ын pagination болон Бүлэг 12-ын өгөгдлийн сангийн session руу хийх суурийг тодорхой тавина — энэ бүлэгт сурсан зүйл тэр хоёрт шууд ажиллах болно.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.