Ачааллж байна...
Бидний бүх Pydantic model одоог хүртэл хавтгай байсан. Талбар бүр нь энгийн төрөл: текст, тоо, bool. Гэхдээ бодит өгөгдөл ихэвчлэн илүү гүн байдаг. Ном дотор зохиолчийн бүтэн мэдээлэл (нэр, төрсөн он, улс) байж болно. Захиалга дотор хаягийн бүрэн бүтэц (хот, дүүрэг, гудамж) байж болно.
Курс 2-ын Бүлэг 5-т та үүрлэсэн бүтэц сурсан: dictionary доторх dictionary, list доторх dictionary. Тэр үед би "энэ бол яг JSON-ы авдаг хэлбэр" гэж хэлж байсан. Одоо бид түүнийг Pydantic-д авчирна: model доторх model. Энэ бол Бүлэг 6-ийн сүүлчийн ойлголт бөгөөд түүний дараа бид Түвшин 2-ын гол бүтээн байгуулалт руу орно.
Ном бүрт зохиолч байдаг. Одоог хүртэл бид зохиолчийг зүгээр л текстээр (author: str) илэрхийлж ирсэн: "Ч. Лодойдамба". Гэхдээ зохиолчийн талаар илүү мэдэх шаардлагатай бол? Төрсөн он, улс, намтар?
Гэнэн шийдэл нь номын model-д зохиолчийн бүх талбарыг шахах:
python
class Book(BaseModel):
title: str
author_name: str
author_birth_year: int
author_country: str
year: intЭнэ ажиллана, гэхдээ асуудалтай. Талбарын нэр урт, эвгүй (author_birth_year). Хэрэв та хэд хэдэн model-д зохиолч хэрэгтэй бол (ном, өгүүллэг, шүлэг) зохиолчийн талбаруудыг хаана хаанаа давтана. Зохиолчийн бүтцийг өөрчлөхөд бүх газар засах ёстой.
Илүү сайн шийдэл нь: зохиолчийг өөрийн model болгож, номд түүнийг оруулах.
test_model.py файлд (эсвэл шинэ туршилтын файлд) бичнэ:
python
# test_model.py
from pydantic import BaseModel
class Author(BaseModel):
name: str
birth_year: int
country: str
class Book(BaseModel):
title: str
year: int
author: Author
book = Book(
title="Цог хийморь",
year=1962,
author=Author(
name="Ч. Лодойдамба",
birth_year=1917,
country="Монгол",
),
)
print(book)
print(book.author.name)Ажиллуулна:
python test_model.pyГаралт:
title='Цог хийморь' year=1962 author=Author(name='Ч. Лодойдамба', birth_year=1917, country='Монгол')
Ч. ЛодойдамбаКодын задаргаа
Author — тусдаа model, гурван талбартай.
Book — model, гэхдээ түүний author талбарын type hint нь Author. Энгийн төрөл (str, int) биш, харин өөр Pydantic model. Энэ бол гол шинэ зүйл.
Book(...) үүсгэхэд author талбарт Author(...) объект өгч байна. Model доторх model.
book.author.name — хоёр цэгээр гүн рүү хандаж байна. book.author нь Author объект; book.author.name нь тэр зохиолчийн нэр. Курс 2-оос танил — үүрлэсэн бүтцэд гүн хандах.
Хамгийн чухал зан төлөв: Pydantic гүн рүү шалгадаг. Дотоод model ч бас шалгагдана.
python
# test_model.py (Book(...) хэсгийг солино)
book = Book(
title="Цог хийморь",
year=1962,
author=Author(
name="Ч. Лодойдамба",
birth_year="маш эрт", # тоо байх ёстой!
country="Монгол",
),
)Гаралт:
pydantic_core._pydantic_core.ValidationError: 1 validation error for Author
birth_year
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='маш эрт', input_type=str]Дотоод Author model-ын birth_year буруу байсныг Pydantic барив. Гадаад model зөв байсан ч, дотоод model буруу тул бүхэлдээ татгалзсан.
Энэ бол чухал: гүн ямар ч байсан, бүх түвшин шалгагдана. Ном доторх зохиолч, зохиолч доторх өөр model — хаана ч байсан Pydantic хүрч шалгана.
Одоо энэ мэдлэгээ endpoint-д авчирна. main.py файлаа дараах кодоор солино:
python
# main.py
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(title="Номын API")
books = []
next_id = 1
class Author(BaseModel):
name: str
birth_year: int
country: str = "Монгол"
class Book(BaseModel):
title: str
year: int
author: Author
@app.post("/books", status_code=201)
def create_book(book: Book):
global next_id
new_book = book.model_dump()
new_book["id"] = next_id
books.append(new_book)
next_id += 1
return new_bookХадгална.
Туршина
/docs -> POST /books -> Try it out. Одоо жишээ JSON нь үүрлэсэн харагдана:
json
{
"title": "string",
"year": 0,
"author": {
"name": "string",
"birth_year": 0,
"country": "Монгол"
}
}author талбар нь дотроо бүтэцтэй — FastAPI Author model-ыг таньж, түүний бүтцийг харуулсан. JSON-ыг солино:
json
{
"title": "Цог хийморь",
"year": 1962,
"author": {
"name": "Ч. Лодойдамба",
"birth_year": 1917,
"country": "Монгол"
}
}Execute дарна.
Server response (статус 201):
json
{
"title": "Цог хийморь",
"year": 1962,
"author": {
"name": "Ч. Лодойдамба",
"birth_year": 1917,
"country": "Монгол"
},
"id": 1
}Үүрлэсэн JSON орж, үүрлэсэн JSON гарлаа. author бүтэн объект хэлбэрээр хадгалагдсан. model_dump() нь гүн бүтцийг ч бүрэн dictionary болгосныг анзаараарай.
Одоо дотоод model буруу байвал endpoint яаж хариулахыг харъя. /docs дээр:
json
{
"title": "Цог хийморь",
"year": 1962,
"author": {
"name": "Ч. Лодойдамба",
"birth_year": "маш эрт"
}
}author.birth_year буруу төрөл, author.country дутуу (гэхдээ default-тай тул зүгээр).
Server response (статус 422):
json
{
"detail": [
{
"type": "int_parsing",
"loc": ["body", "author", "birth_year"],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "маш эрт"
}
]
}loc талбарыг сайтар хараарай: ["body", "author", "birth_year"]. Гурван түвшин. body — хүсэлтийн бие. author — доторх зохиолч. birth_year — түүний талбар.
Бүлэг 2-4-т loc нь хоёр элементтэй байсан (["body", "year"]). Одоо гурав. Pydantic алдаа яг хаана, ямар гүнд байгааг заана. Хорин талбартай гүн бүтцэд ч энэ нарийвчлал таныг шууд зөв газар руу хөтөлнө.
Өмнөх хичээлд сурсан response_model нь үүрлэсэн model дээр ч ажиллана. Гаралтад зохиолчийн зөвхөн нэрийг харуулж, төрсөн он, улсыг нуухыг хүсвэл:
python
# main.py
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(title="Номын API")
books = []
next_id = 1
class Author(BaseModel):
name: str
birth_year: int
country: str = "Монгол"
class Book(BaseModel):
title: str
year: int
author: Author
class AuthorPublic(BaseModel):
name: str
class BookPublic(BaseModel):
id: int
title: str
year: int
author: AuthorPublic
@app.post("/books", status_code=201, response_model=BookPublic)
def create_book(book: Book):
global next_id
new_book = book.model_dump()
new_book["id"] = next_id
books.append(new_book)
next_id += 1
return new_bookХадгална.
Кодын задаргаа
AuthorPublic — зохиолчийн гаралтын model, зөвхөн name. birth_year, country байхгүй.
BookPublic — номын гаралтын model. Түүний author талбар нь AuthorPublic (бүтэн Author биш).
Гаралтын model-ууд ч үүрлэдэг: BookPublic дотор AuthorPublic. Оролтын бүтэц (Book дотор Author) болон гаралтын бүтэц (BookPublic дотор AuthorPublic) хоёулаа гүн, гэхдээ өөр талбартай.
Туршина
Ижил бүрэн өгөгдөл илгээнэ:
json
{
"title": "Цог хийморь",
"year": 1962,
"author": {
"name": "Ч. Лодойдамба",
"birth_year": 1917,
"country": "Монгол"
}
}Server response (статус 201):
json
{
"id": 1,
"title": "Цог хийморь",
"year": 1962,
"author": {
"name": "Ч. Лодойдамба"
}
}Зохиолчийн birth_year болон country шүүгдсэн. Хэрэглэгч зөвхөн зохиолчийн нэрийг харна. Гарцын шүүлтүүр гүн рүү ажиллаж, дотоод model-ыг ч шүүсэн.
Серверт бүрэн зохиолчийн мэдээлэл (birth_year, country) хадгалагдсан хэвээр — зөвхөн гарцад шүүгдсэн. Өмнөх хичээлийн зарчим гүн бүтцэд ч хүчинтэй.
Model доторх нэг объект биш, объектуудын жагсаалт ч байж болно. Жишээ нь номд олон tag (шошго) байх:
python
# main.py-д Tag model нэмээд Book-д tags талбар нэмнэ (жишээ)
class Tag(BaseModel):
name: str
color: str = "цэнхэр"
class Book(BaseModel):
title: str
year: int
author: Author
tags: list[Tag] = []tags: list[Tag] — Tag объектуудын жагсаалт. Курс 2-ын Бүлэг 5-ын "list доторх dict" бүтэц, одоо Pydantic хэлбэрээр.
Ийм өгөгдөл ингэж илгээгдэнэ:
json
{
"title": "Цог хийморь",
"year": 1962,
"author": {"name": "Ч. Лодойдамба", "birth_year": 1917},
"tags": [
{"name": "роман", "color": "улаан"},
{"name": "сонгодог"}
]
}Хоёр tag. Эхнийх өнгөтэй, хоёр дахь нь default өнгөтэй ("цэнхэр"). Pydantic жагсаалтын элемент бүрийг Tag model-оор шалгана.
Хэрэв нэг tag буруу байвал loc нь индексээр заана: ["body", "tags", 1, "name"] — "tags жагсаалтын 1-р (хоёр дахь) элементийн name талбар". Гүн болон индекс хосолсон нарийвчлал.
Model доторх model доторх model ч боломжтой. Жишээ нь зохиолч доторх төрсөн газар:
python
class Location(BaseModel):
city: str
country: str
class Author(BaseModel):
name: str
birthplace: Location
class Book(BaseModel):
title: str
author: AuthorОдоо book.author.birthplace.city — гурван цэг, гурван түвшин. JSON нь:
json
{
"title": "Цог хийморь",
"author": {
"name": "Ч. Лодойдамба",
"birthplace": {
"city": "Дорнод",
"country": "Монгол"
}
}
}Гүн ямар ч байсан Pydantic бүх түвшинг шалгана. Практик хязгаар байхгүй — хэдэн ч түвшин үүрлэж болно. Гэхдээ хэт гүн бүтэц уншихад хэцүү болдог тул хэмжээгээ мэдэх нь зүйтэй.
Нэг практик зөвлөгөө. Дотоод model-ыг гадаад model-оос өмнө зарлах ёстой. Python кодыг дээрээс доош уншдаг тул Book-д Author ашиглахын өмнө Author аль хэдийн зарлагдсан байх ёстой:
python
class Author(BaseModel): # эхлээд
name: str
class Book(BaseModel): # дараа нь — Author-ыг ашиглана
title: str
author: AuthorЭсрэгээр бичвэл (Book эхэлж, Author дараа) NameError: name 'Author' is not defined гарна. Дүрэм энгийн: ашиглагдах зүйл эхэлж зарлагдана.
Дотоод model-ыг оролдож зарлаагүй
python
class Book(BaseModel):
title: str
author: Author # Author хаана ч зарлагдаагүйГаралт:
NameError: name 'Author' is not definedAuthor model-ыг Book-ийн өмнө зарлах ёстой.
Үүрлэсэн өгөгдлийг хавтгайгаар илгээх
Хэрэглэгч author-ыг объект биш, текстээр илгээвэл:
json
{
"title": "Цог хийморь",
"year": 1962,
"author": "Ч. Лодойдамба"
}Server response (статус 422):
json
{
"detail": [
{
"type": "model_attributes_type",
"loc": ["body", "author"],
"msg": "Input should be a valid dictionary or object to extract fields from",
"input": "Ч. Лодойдамба"
}
]
}author нь объект (dictionary) байх ёстой, гэтэл текст ирсэн. Pydantic "энэ талбар model-ыг хүлээж байна, текст биш" гэж хэлж байна. type нь model_attributes_type.
model_dump үүрлэсэн бүтцэд
Санаа зовох хэрэггүй зүйл: book.model_dump() нь гүн бүтцийг бүрэн dictionary болгодог. Дотоод model-ууд ч dictionary болно. Тиймээс new_book нь бүх түвшиндээ энгийн Python dictionary болж, JSON-д асуудалгүй хувирна. Энэ автоматаар зөв ажилладаг тул нэмэлт код бичих шаардлагагүй.
Шударга зөвлөгөө: бүх зүйлийг үүрлэх шаардлагагүй. Заримдаа хавтгай бүтэц илүү энгийн, зөв байдаг.
Зохиолчийн зөвхөн нэр хэрэгтэй бол author: str хангалттай — тусдаа model илүүц. Зохиолчийн олон талбар (нэр, он, улс, намтар) хэрэгтэй, эсвэл зохиолчийг олон газар ашиглах бол Author model нь зөв.
Дүрэм: дотоод зүйл олон талбартай, эсвэл олон газар ашиглагдах бол model болго. Ганц утга бол хавтгай үлдээ. Хэт их үүрлэх нь кодыг төвөгтэй болгодог; хэт бага үүрлэх нь давтагдал үүсгэдэг. Тэнцвэр хэрэгтэй.
Хүсвэл номд publisher (хэвлэлийн газар) гэсэн үүрлэсэн model нэмээд, түүнд name болон city талбар өгч туршаад үзээрэй. Гүн validation ажиллахыг харахын тулд дотоод талбарыг зориудаар буруу илгээгээд, loc талбарын гүнийг ажиглаарай.
Сонирхвол list[Tag] бүтцийг туршиж, олон tag илгээгээд, нэгийг нь буруу болгож, loc талбар индексийг хэрхэн заахыг (["body", "tags", 0, ...]) хараарай.
Model доторх model — талбарын type hint нь энгийн төрөл биш, өөр Pydantic model (author: Author).
Курс 2-ын үүрлэсэн бүтэц нь Pydantic хэлбэрээр — гүн JSON орж, гарна.
Гүн рүү хандах: book.author.name (олон цэг).
Pydantic гүн рүү шалгадаг — бүх түвшин, бүх дотоод model.
Алдааны loc талбар гүнийг заана: ["body", "author", "birth_year"].
response_model гүн рүү шүүнэ — дотоод model-ыг ч (AuthorPublic).
Объектуудын жагсаалт (list[Tag]) боломжтой; loc индексээр заана.
Дотоод model-ыг гадаад model-оос өмнө зарлана.
Хэт их үүрлэхгүй: олон талбартай/дахин ашиглагдах бол model; ганц утга бол хавтгай.
Бүлэг 6-ийн онолын хэсэг дууслаа. Та одоо Pydantic-ийн хоёр үүргийг бүрэн эзэмшсэн: оролтын харуул (Бүлэг 4-5) болон гарцын шүүлтүүр (Бүлэг 6). Хавтгай ба гүн model, оролт ба гаралтын ялгаа — бүгд таны гарт.
Дараагийн хичээлд бид Түвшин 2-ын бүх зүйлийг нэгтгэж, Тэмдэглэлийн API v1 бүтээнэ. Энэ бол бүрэн CRUD-тай, Pydantic-аар хамгаалагдсан, response_model-оор шүүгдсэн жинхэнэ API — үүсгэх, бүгдийг унших, нэгийг унших, засах, устгах. Гэрийн даалгавар биш, бид түүнийг мөр мөрөөр хамтдаа бичнэ. Мөн тэр API нь бүх сургалтын турш дагалдах Тэмдэглэлийн API-ийн эхний хувилбар болно — Түвшин 3-т түүнийг цэгцлэх, Түвшин 4-т өгөгдлийн сан залгах бөгөөд бид түүнийг өсөж төлөвшихийг харах болно.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.