Ачааллж байна...
Өмнөх хичээлд бид Pydantic model-ыг тусад нь, туршилтын файлд ажиллуулсан. Буруу өгөгдөл өгөхөд ValidationError гарч, объект үүсэхгүй байхыг харсан. Гэхдээ тэр алдаа зөвхөн терминал дээр, Python traceback хэлбэрээр гарч байсан.
Одоо бид model-ыг FastAPI-тай холбоно. Тэгэхэд ид шид болно: яг тэр ValidationError нь автоматаар 422 JSON хариу болж хувирна. Хэрэглэгч буруу өгөгдөл илгээхэд таны функц огт дуудагдалгүйгээр, дэлгэрэнгүй, эелдэг алдааны хариу буцна.
Энэ бол энэ бүлгийн онцлох хичээл. Бид тэр 422 хариуг эхнээс нь дуустал, талбар бүрээр нь уншина. Энэ хариу бол таны API-ийн хамгаалалтын зүрх бөгөөд түүнийг сайн ойлгосон хүн итгэлтэй backend бичдэг.
main.py файлаа дараах кодоор солино:
python
# main.py
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(title="Номын API")
class Book(BaseModel):
title: str
author: str
year: int
pages: int
@app.post("/books")
def create_book(book: Book):
return bookХадгална.
Кодын задаргаа
Дээд талд Pydantic model — өмнөх хичээлээс танил.
Доор нь endpoint. Гурван шинэ зүйлийг анзаараарай.
@app.post — @app.get биш. POST бол "шинэ зүйл үүсгэ" гэсэн method. Бид POST-ыг Бүлэг 5-д зөв сурна; одоохондоо зөвхөн Pydantic-д анхаарлаа хандуулъя. POST хэрэглэсэн шалтгаан нь: өгөгдөл хүлээж авах endpoint нь GET биш байдаг.
book: Book — энэ бол гол шинэ зүйл. Функцийн параметр дээр type hint нь Pydantic model байна. Бүлэг 2-т та book_id: int гэж бичсэн — ганц утгын type hint. Одоо book: Book — бүтэн объектын type hint.
Энэ жижиг ялгаа бүх зүйлийг өөрчилдөг. FastAPI book: Book гэсэн мөрийг хараад ингэж бодно: "Энэ параметрийн төрөл нь Pydantic model байна. Тэгэхээр энэ өгөгдөл хаягнаас биш, хүсэлтийн бие (body)-ээс ирнэ. Түүнийг Book model-оор шалга."
return book — одоохондоо зүгээр л хүлээж авсан объектоо буцааж байна ("echo"). Энэ нь өгөгдөл зөв хүлээж авагдсан эсэхийг харахад тохиромжтой.
Нэг ойлголтыг тодруулъя, учир нь энэ нь шинэ.
Бүлэг 2-т бүх өгөгдөл хаягт байсан: /books/2, /cities?limit=5. Хаяг бол богино, энгийн, зөвхөн текст. Ганц тоо, ганц үг багтана.
Гэхдээ бүтэн ном (нэр, зохиолч, он, хуудас) хаягт багтахгүй. Хэтэрхий том, хэтэрхий бүтэцтэй. Түүнд өөр суваг хэрэгтэй.
Тэр суваг бол request body — хүсэлтийн бие. Энэ бол хүсэлттэй хамт илгээгддэг JSON өгөгдөл, хаягнаас тусдаа. Курс 2-ын Бүлэг 11-т та requests.post(url, json=...) гэж бичиж байсан бол тэр json= хэсэг нь яг request body юм.
Зоогийн газрын зүйрлэлээр: хаяг бол "аль ширээ" гэдгийг заадаг бол request body бол "юу захиалж байна" гэсэн бичсэн захиалгын хуудас юм. Богино зүйл (ширээний дугаар) амаар хэлж болно; урт зүйл (бүтэн захиалга) бичиж өгдөг.
POST endpoint-ийг browser-ийн хаягийн мөрөөр туршиж болохгүй — хаягийн мөр зөвхөн GET илгээдэг, request body явуулж чадахгүй. Тиймээс бид /docs ашиглана.
Сервер ажиллаж байгаа эсэхийг шалгаад /docs хуудсаа нээнэ:
http://127.0.0.1:8000/docsPOST /books гэсэн мөр харагдана (өнгө нь ногоон биш, POST-ын өнгө). Дээр нь товшино.
Request body гэсэн шинэ хэсэг харагдана. Тэнд FastAPI таны Book model-оос үүсгэсэн жишээ JSON байна:
json
{
"title": "string",
"author": "string",
"year": 0,
"pages": 0
}Анзаараарай: FastAPI таны model-ын бүх талбарыг, тус бүрийн төрөлтэй нь харуулж байна. title нь текст ("string"), year нь тоо (0). Та энэ жишээг бичээгүй — Pydantic model-оос автоматаар үүссэн.
Try it out дарна. Одоо JSON талбар засварлах боломжтой болно. Доторх текстийг дараахаар солино:
json
{
"title": "Цог хийморь",
"author": "Ч. Лодойдамба",
"year": 1962,
"pages": 560
}Execute дарна.
Server response (статус 200):
json
{
"title": "Цог хийморь",
"author": "Ч. Лодойдамба",
"year": 1962,
"pages": 560
}Юу болов, алхам алхмаар. Та JSON body илгээв. FastAPI түүнийг хүлээж авав. book: Book гэсэн type hint-ийг хараад Pydantic-д "энэ өгөгдлийг Book model-оор шалга" гэж хэлэв. Pydantic бүх талбарыг шалгав — бүгд зөв. Book объект үүсэв. Функц дуудагдав, book-ыг буцаав. FastAPI түүнийг дахин JSON болгоод илгээв.
Таны функц ажиллаж эхлэх үед book нь аль хэдийн зөв, шалгагдсан объект байсан. Та юу ч шалгаагүй. Ганц if бичээгүй.
Одоо хамгийн чухал хэсэг. Try it out талбарт дараах буруу өгөгдлийг оруулна:
json
{
"title": "Цог хийморь",
"author": "Ч. Лодойдамба",
"year": "маш эрт",
"pages": 560
}year нь тоо биш, текст.
Execute дарна.
Server response (статус 422):
json
{
"detail": [
{
"type": "int_parsing",
"loc": [
"body",
"year"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "маш эрт"
}
]
}Ид шид болов. Өмнөх хичээлд туршилтын файлд харсан Python ValidationError нь одоо цэвэрхэн JSON хариу болж хувирлаа. Таны функц огт дуудагдаагүй.
Энэ хариуг эхнээс нь дуустал уншъя. Бүлэг 2-т бид path parameter дээр ижил бүтцийг үзсэн, гэхдээ одоо нэг чухал ялгаа бий.
detail
json
"detail": [ ... ]Танил бүтэц. detail бол жагсаалт, учир нь олон алдаа зэрэг гарч болно. Одоохондоо нэг алдаа.
type
json
"type": "int_parsing"Алдааны машины код: "бүхэл тоо болгож задалж чадсангүй". Өмнөх хичээлийн туршилтын файлд яг энэ int_parsing гарч байсан.
loc — энд шинэ зүйл байна
json
"loc": ["body", "year"]Анхаараарай: эхний элемент нь "body".
Бүлэг 2-т энэ "path" эсвэл "query" байсан. Одоо "body" — алдаа хүсэлтийн бие дотор гарсан гэсэн үг. FastAPI өгөгдөл хаанаас ирснийг яг мэдэж байгаа: хаягнаас биш, body-оос.
Хоёр дахь элемент "year" — яг аль талбар. Хамтдаа: "алдаа нь body доторх year талбар дээр."
Энэ нарийвчлал нь том model дээр асар үнэ цэнэтэй. Хорин талбартай маягт илгээхэд loc талбар яг аль талбар буруу байсныг хэлж өгнө.
msg болон input
json
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "маш эрт"msg — хүнд зориулсан тайлбар. input — бодитоор ирсэн утга. Хэрэглэгч "маш эрт" илгээснийг яг харуулж байна.
Эдгээр талбарыг Бүлэг 2-т дэлгэрэнгүй үзсэн тул дахин нурших шаардлагагүй. Гол зүйл: бүтэц яг ижилхэн. Path, query, body — хаана ч байсан ижил бүтэцтэй алдаа. Нэг удаа ойлгосон бол хаана ч уншина.
Одоо олон талбар эвдэж, Pydantic-ийн жинхэнэ хүчийг харъя. Try it out талбарт:
json
{
"title": 42,
"year": "маш эрт"
}title-д тоо (текст байх ёстой). year-т текст (тоо байх ёстой). author дутуу. pages дутуу.
Execute дарна.
Server response (статус 422):
json
{
"detail": [
{
"type": "string_type",
"loc": ["body", "title"],
"msg": "Input should be a valid string",
"input": 42
},
{
"type": "missing",
"loc": ["body", "author"],
"msg": "Field required",
"input": {
"title": 42,
"year": "маш эрт"
}
},
{
"type": "int_parsing",
"loc": ["body", "year"],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "маш эрт"
},
{
"type": "missing",
"loc": ["body", "pages"],
"msg": "Field required",
"input": {
"title": 42,
"year": "маш эрт"
}
}
]
}Дөрвөн алдаа, нэг дор. Жагсаалтад дөрвөн элемент.
Тус бүрийг харъя. title — string_type (текст байх ёстой, тоо ирсэн). author — missing (дутуу). year — int_parsing (тоо байх ёстой, буруу текст ирсэн). pages — missing (дутуу).
Энэ бол Pydantic-ийн жинхэнэ давуу тал. Өмнөх хичээлд ярьсан гар аргын кодыг санаж байна уу? Тэр эхний алдаан дээр зогсох байсан — хэрэглэгч title-аа засаад дахин илгээж, дараа нь author-оо, дараа нь year-аа... дөрвөн удаа.
Pydantic бүх талбарыг шалгаж, бүх асуудлыг нэг дор мэдээлдэг. Хэрэглэгч дөрвүүлээ нэг удаад хараад, нэг удаад засна. Энэ бол сайн API-ийн шинж.
Анзаараарай: missing алдааны input талбар нь бүтэн илгээсэн объектыг харуулж байна — учир нь дутуу талбарын хувьд "юу ирсэн" гэдэг нь тухайн талбарын утга биш, харин бүхэл контекст юм.
Өмнөх хичээлд Pydantic боломжтой текстийг тоо болгодгийг харсан. Body дээр ч ижил. Try it out талбарт:
json
{
"title": "Цог хийморь",
"author": "Ч. Лодойдамба",
"year": "1962",
"pages": "560"
}year болон pages-т текст ("1962", "560").
Execute дарна.
Server response (статус 200):
json
{
"title": "Цог хийморь",
"author": "Ч. Лодойдамба",
"year": 1962,
"pages": 560
}Алдаагүй. Хариу дотор year болон pages нь тоо (хашилтгүй) болсон. Pydantic боломжтой хувиргалтыг хийсэн.
Энэ нь практикт чухал: өөр өөр программ өгөгдлийг өөр өөр хэлбэрээр илгээдэг. Зарим нь 1962 (тоо), зарим нь "1962" (текст) илгээнэ. Pydantic хоёуланг нь зөв хүлээж авдаг тул таны API уян хатан болно.
/docs дээр POST /books мөрийг задалж, Responses хэсгийг хараарай:
200 Successful Response
422 Validation ErrorFastAPI өөрөө 422-ыг баримт бичигт нэмсэн. Учир нь энэ endpoint Pydantic model хүлээж авдаг тул validation алдаа гарч болзошгүйг мэдэж байгаа. Та юу ч бичээгүй; FastAPI таны model-оос ойлгосон.
Бүлэг 3-т бид нэг ялгааг тодруулсан: 422 бол функц дуудагдахаас өмнө, 404 бол функц дуудагдсаны дараа. Одоо түүнийг батлая.
create_book функцэд print() нэмнэ:
python
@app.post("/books")
def create_book(book: Book):
print(f"Функц дуудагдлаа: {book.title}")
return bookЗөв өгөгдөл илгээвэл терминал дээр:
Функц дуудагдлаа: Цог хийморьБуруу өгөгдөл (year: "маш эрт") илгээвэл терминал дээр юу ч гарахгүй. Функц огт дуудагдаагүй. Pydantic хаалган дээр зогсоосон.
Энэ бол Pydantic-ийн үнэ цэнийн гол цөм. Таны функцийн дотор өгөгдөл нь үргэлж зөв байна. Та if book.year бол тоо мөн үү гэж хэзээ ч шалгах шаардлагагүй — Pydantic түүнийг аль хэдийн баталгаажуулсан. Таны функц зөвхөн жинхэнэ ажилдаа анхаарна.
Одоо та validation-ыг гурван өөр газраас харлаа. Тэдгээрийг нэг дор тавья, учир нь тэд бүгд нэг механизм юм.
Path parameter (Бүлэг 2): book_id: int, буруу бол loc: ["path", "book_id"].
Query parameter (Бүлэг 2): limit: int = 10, буруу бол loc: ["query", "limit"].
Request body (одоо): book: Book, буруу бол loc: ["body", "year"].
Гурвуулаа type hint дээр суурилдаг. Гурвуулаа Pydantic-аар шалгагддаг. Гурвуулаа ижил бүтэцтэй 422 өгдөг. Ялгаа нь зөвхөн loc талбарын эхний элемент — өгөгдөл хаанаас ирснийг заана.
Нэг систем, гурван байршил. Үүнийг ойлгосон нь FastAPI-ийн validation-ыг бүхэлд нь ойлгосон гэсэн үг.
GET дээр body хүлээх
python
@app.get("/books")
def get_books(book: Book):
return bookЭнэ нь техникийн хувьд ажиллаж болох ч буруу зохиомж юм. GET нь "унших" method — өгөгдөл авдаг, өгдөггүй. GET хүсэлтэд ихэвчлэн body байдаггүй, олон хэрэгсэл GET body-г дэмждэггүй. Өгөгдөл хүлээж авах бол POST (эсвэл PUT) ашиглана. Бүлэг 5-д үүнийг зөв сурна.
Model-ыг import хийхгүй эсвэл зарлахгүй
python
@app.post("/books")
def create_book(book: Book): # Book хаана ч зарлагдаагүй
return bookГаралт:
NameError: name 'Book' is not definedBook model-ыг endpoint-ийн өмнө зарласан байх ёстой.
Хоосон body илгээх
/docs дээр Try it out хийж, JSON-ыг бүхэлд нь устгаад хоосон илгээвэл:
Server response (статус 422):
json
{
"detail": [
{
"type": "missing",
"loc": ["body", "title"],
"msg": "Field required",
...
},
...
]
}Бүх заавал талбар missing гэж жагсагдана. Энэ бол зөв зан төлөв — хоосон body бол бүх талбар дутуу гэсэн үг.
JSON синтакс буруу
Хэрэв та /docs-д гараар JSON бичихдээ синтакс алдаа гаргавал (жишээ нь таслал дутуу):
json
{
"title": "Цог хийморь"
"author": "Ч. Лодойдамба"
}Server response (статус 422):
json
{
"detail": [
{
"type": "json_invalid",
"loc": ["body", ...],
"msg": "JSON decode error",
...
}
]
}json_invalid — өгөгдөл нь хүчинтэй JSON ч биш. Энэ нь талбарын алдаанаас өмнө гардаг: эхлээд "энэ JSON мөн үү?", дараа нь "талбарууд зөв үү?". Swagger UI ихэвчлэн ийм синтакс алдааг өөрөө анзаарч, илгээхээс өмнө анхааруулдаг.
Хүсвэл /docs дээр янз бүрийн буруу өгөгдөл илгээж, гарах алдааны төрлүүдийг цуглуулж үзээрэй. year-т bool өгвөл? Талбарыг бүгдийг нь орхивол? pages-т сөрөг тоо өгвөл (энэ нь одоохондоо өнгөрнө — учир нь бид сөрөг тоог хараахан хориглоогүй)? Алдааны төрөл бүрийг харах нь бүтцийг бат тогтооно.
Сонирхвол терминал дээр print(book.model_dump()) нэмж, зөв өгөгдөл илгээгээд, функцийн дотор объект ямар харагдахыг ажиглаарай. Хэрэглэгчийн JSON нь таны функцэд бүрэн Python объект болж ирж байгааг харна.
Функцийн параметрт Pydantic model type hint (book: Book) өгвөл FastAPI өгөгдлийг request body-оос авч, Pydantic-аар шалгана.
Request body — хаягнаас тусдаа илгээгддэг JSON өгөгдөл; урт, бүтэцтэй өгөгдөлд зориулагдсан.
POST endpoint-ийг /docs-оор туршина — хаягийн мөр body илгээж чадахгүй.
Буруу өгөгдөл илгээвэл яг тэр ValidationError нь автоматаар 422 JSON хариу болно.
loc талбарын эхний элемент нь "body" — path/query-ээс ялгарна.
Олон алдаа нэг дор ирнэ — хэрэглэгч бүгдийг нэг удаад засна.
Функц дуудагдахаас өмнө шалгагдана; таны функцийн дотор өгөгдөл үргэлж зөв.
Path/query/body validation бол нэг систем — ялгаа нь зөвхөн loc.
Одоо та бүтэн объект хүлээж авч, автоматаар шалгуулж чадна. Гэхдээ таны бүх талбар одоогоор заавал байна — хэрэглэгч дөрвүүлээ өгөх ёстой. Бодит амьдралд зарим талбар заавал биш байдаг: номын тайлбар байж ч болно, байхгүй ч болно. Дараагийн хичээлд бид Optional талбар ба default утга сурна — Курс 2-ын Бүлэг 4-ын Optional/None мэдлэг FastAPI-д хэрхэн ажиллаж, заавал ба заавал бус талбарын ялгаа /docs дээр хэрхэн харагдахыг үзнэ. Тэр нь Бүлэг 4-ийн сүүлчийн эд анги бөгөөд түүний дараа бид POST-ыг бүрэн эзэмшихээр Бүлэг 5 руу орно.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.