Ачааллж байна...
Бүлэг 4-т би Pydantic-ийн хоёр үүргийг дурдсан. Эхнийх нь оролтын харуул — хэрэглэгчээс ирж буй өгөгдлийг шалгах. Түүнийг та Бүлэг 4-5-д бүрэн сурсан. Хоёр дахь нь гарцын шүүлтүүр — хэрэглэгч рүү гарч буй өгөгдлийг хянах. Энэ бүлэг түүнд зориулагдана.
Одоог хүртэл таны endpoint-ууд хүлээж авсан бүх зүйлээ, дотоод бүх өгөгдлөө буцааж байсан. Ихэнх тохиолдолд энэ нь асуудалгүй. Гэхдээ заримдаа энэ нь аюултай: хэрэглэгчид харуулах ёсгүй талбар — нууц үг, дотоод тэмдэглэл, засварын хэрэгсэл — санамсаргүй гадагш алдагдаж болно. Мөн та хариуны бүтэц яг ямар байхыг баталгаажуулах хэрэгтэй болно.
response_model нь хоёуланг шийднэ. Энэ хичээлд бид түүнийг ашиглаж, зөвхөн зарласан талбарыг гаргаж, нууц өгөгдлийг хамгаалж, хариуны бүтцийг тогтмол болгож сурна.
Дараах жишээг харъя. Хэрэглэгчийн API бичиж байна гэж бодъё. Хэрэглэгч бүр нэр, имэйл, болон нууц үгтэй. Нууц үгийг заавал хадгалах ёстой (нэвтрэхэд хэрэгтэй), гэхдээ хэзээ ч хэрэглэгч рүү буцаах ёсгүй.
main.py файлаа дараах кодоор солино:
python
# main.py
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(title="Хэрэглэгчийн API")
users = []
next_id = 1
class User(BaseModel):
name: str
email: str
password: str
@app.post("/users", status_code=201)
def create_user(user: User):
global next_id
new_user = user.model_dump()
new_user["id"] = next_id
users.append(new_user)
next_id += 1
return new_userХадгална.
Асуудлыг туршиж харах
/docs -> POST /users -> Try it out:
json
{
"name": "Дулгуун",
"email": "dulguun@example.com",
"password": "нууц12345"
}Execute дарна.
Server response (статус 201):
json
{
"name": "Дулгуун",
"email": "dulguun@example.com",
"password": "нууц12345",
"id": 1
}Асуудал тодорхой байна. Нууц үг ("нууц12345") хариунд буцсан. Хэрэглэгч өөрийн нууц үгээ буцааж авах нь тийм ч аюултай биш байж магадгүй, гэхдээ энэ endpoint нь GET /users (бүх хэрэглэгчийг харах) гэсэн endpoint-той хослох үед аймшигтай болно — тэгвэл хэн ч бүх хэрэглэгчийн нууц үгийг харах болно.
Нууц үг хэзээ ч гадагш гарах ёсгүй. Гэхдээ бид түүнийг хадгалах ёстой (оролтод хэрэгтэй). Тэгэхээр асуудал нь: оролтод хэрэгтэй, гаралтад хэрэггүй талбарыг хэрхэн зохицуулах вэ?
Зоогийн газрын зүйрлэлээ ашиглая. Гал тогоонд хоол бэлтгэхэд бүх мэдээлэл байдаг: жор, нийлүүлэгчийн үнэ, ашгийн хэмжээ, тогоочийн тэмдэглэл. Гэхдээ зочинд хоол гарахад тэр бүхэн гардаггүй — зочин зөвхөн хоол болон түүний нэрийг харна. Үнэ, жор, дотоод тэмдэглэл гал тогоонд үлддэг.
response_model бол гал тогоо ба ширээний хоорондох хаалга юм. Тэр хаалгаар зөвхөн зочинд зориулсан зүйл гардаг; дотоод зүйл гал тогоонд үлддэг. Та "энэ хоол ширээнд гарахдаа ийм харагдах ёстой" гэж зарлахад хаалга үлдсэнийг нь шүүж хаядаг.
Гарцад зориулсан тусдаа Pydantic model үүсгэнэ — нууц үггүй:
python
# main.py
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(title="Хэрэглэгчийн API")
users = []
next_id = 1
class User(BaseModel):
name: str
email: str
password: str
class UserPublic(BaseModel):
id: int
name: str
email: str
@app.post("/users", status_code=201, response_model=UserPublic)
def create_user(user: User):
global next_id
new_user = user.model_dump()
new_user["id"] = next_id
users.append(new_user)
next_id += 1
return new_userХадгална.
Кодын задаргаа
Одоо хоёр model байна.
User — оролтын model. Хэрэглэгч илгээх ёстой зүйл: name, email, password. Нууц үг энд байна, учир нь бүртгэлд хэрэгтэй.
UserPublic — гаралтын model. Хэрэглэгч рүү буцаах зүйл: id, name, email. Нууц үг байхгүй. Мөн id байна — гаралтад id хэрэгтэй (оролтод байхгүй байсан).
Decorator дээр шинэ параметр:
python
@app.post("/users", status_code=201, response_model=UserPublic)response_model=UserPublic — "энэ endpoint юу буцаасан ч, түүнийг UserPublic хэлбэрээр шүүж гарга" гэж хэлж байна.
Функцийн дотор new_user нь бүх талбартай (нууц үг ч орсон) dictionary хэвээр. Гэхдээ return new_user хийхэд FastAPI түүнийг UserPublic-аар шүүнэ — зөвхөн id, name, email гаргаж, password-ыг хаяна.
Туршина
/docs -> POST /users -> ижил өгөгдөл:
json
{
"name": "Дулгуун",
"email": "dulguun@example.com",
"password": "нууц12345"
}Server response (статус 201):
json
{
"id": 1,
"name": "Дулгуун",
"email": "dulguun@example.com"
}Нууц үг алга. Функц түүнийг агуулсан dictionary буцаасан ч response_model түүнийг шүүсэн. Хэрэглэгч зөвхөн id, name, email авсан.
Дотоод болон гадаад ялгагдлаа. password серверт хадгалагдсан (users жагсаалтад байгаа) боловч хэрэглэгч рүү гараагүй. Энэ бол response_model-ийн гол хүч.
Нууц үг зөвхөн гарцад шүүгдсэн, устгагдаагүй гэдгийг батлая. Түр зуурын GET endpoint нэмнэ:
python
# main.py (нэмэлт, түр зуурын)
@app.get("/debug/users")
def debug_users():
return users/docs -> GET /debug/users:
Server response:
json
[
{"name":"Дулгуун","email":"dulguun@example.com","password":"нууц12345","id":1}
]Нууц үг серверт байсаар байна — users жагсаалтад бүрэн хадгалагдсан. response_model түүнийг устгаагүй, зөвхөн тодорхой endpoint-ийн гарцад шүүсэн.
Энэ ялгаа чухал: response_model бол гарцын шүүлтүүр, өгөгдлийн устгагч биш. Дотоод өгөгдөл бүрэн хэвээр; зөвхөн хэрэглэгч рүү юу гарахыг хянадаг.
(Энэ debug/users endpoint бол зөвхөн батлах зорилготой. Бодит API-д ийм endpoint байх ёсгүй — устгах эсвэл response_model-оор хамгаалах хэрэгтэй. Одоохондоо туршилтын дараа устгаж болно.)
response_model зөвхөн шүүдэггүй — баталгаажуулдаг ч. Хэрэв та зарласан талбарыг буцаахгүй бол алдаа гарна.
Туршиж харъя. UserPublic-д email заавал байхад, түүнгүй өгөгдөл буцаах endpoint бичье:
python
# main.py (нэмэлт, туршилт)
@app.get("/broken", response_model=UserPublic)
def broken():
return {"id": 1, "name": "Дулгуун"} # email дутуу!/docs -> GET /broken:
Server response (статус 500):
json
{"detail":"Internal Server Error"}Терминал дээр:
pydantic_core._pydantic_core.ValidationError: 1 validation error for UserPublic
email
Field required [type=missing, ...]Юу болов? response_model=UserPublic нь "буцаах зүйл заавал email-тэй байх ёстой" гэж зарласан. Гэтэл функц email-гүй өгөгдөл буцаасан. Pydantic гарцад шалгаж, дутуугаа олж, зогсоов.
Энэ бол сайн зан төлөв. Энэ нь таны API-ийн хариу үргэлж амласан бүтэцтэй байхыг баталгаажуулна. Хэрэв та санамсаргүй талбар мартвал, 500 алдаа таныг шууд анхааруулна — хэрэглэгч эвдэрсэн хариу авахаас өмнө.
Анзаараарай: энэ 500 нь таны алдаа (сервер), хэрэглэгчийн биш. Тиймээс 5xx зөв. Хэрэглэгч буруу зүйл хийгээгүй; таны код амласнаа биелүүлээгүй.
Туршилтын дараа энэ /broken endpoint-ыг устгаарай.
Одоо та API зохион бүтээх хамгийн түгээмэл загваруудын нэгийг харлаа: нөөц бүрт хоёр (заримдаа гурван) model.
Input model (User, NoteCreate) — хэрэглэгч илгээх зүйл. Ихэвчлэн id байхгүй (сервер үүсгэдэг), нууц зүйл багтаж болно.
Output model (UserPublic, NotePublic) — хэрэглэгч рүү буцах зүйл. id байна, нууц зүйл байхгүй.
Яагаад тусад нь? Учир нь оролт ба гаралт өөр зүйл. Хэрэглэгч нууц үг илгээдэг (оролт), гэхдээ хэзээ ч авдаггүй (гаралт). Хэрэглэгч id илгээдэггүй (оролт), гэхдээ үргэлж авдаг (гаралт). Хоёр өөр хэлбэрийг хоёр өөр model-оор илэрхийлэх нь тодорхой, аюулгүй.
Энэ загвар нь бүхэл сургалтын турш давтагдана. Түвшин 4-т бид үүнд гурав дахь model (өгөгдлийн сангийн model) нэмнэ. Одоохондоо хоёрын ялгааг ойлгох нь хангалттай.
response_model нь зөвхөн талбар хасдаггүй — өөрчилж ч болно. Жишээ нь оролтод байхгүй, гаралтад тооцоолсон талбар нэмэх:
python
# main.py
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(title="Номын API")
books = []
next_id = 1
class BookCreate(BaseModel):
title: str
author: str
year: int
class BookPublic(BaseModel):
id: int
title: str
author: str
year: int
century: int
@app.post("/books", status_code=201, response_model=BookPublic)
def create_book(book: BookCreate):
global next_id
new_book = book.model_dump()
new_book["id"] = next_id
new_book["century"] = (book.year // 100) + 1
books.append(new_book)
next_id += 1
return new_bookBookCreate (оролт) — century байхгүй. BookPublic (гаралт) — century байна. Функц түүнийг тооцоолж, dictionary-д нэмдэг.
Туршина
json
{"title": "Цог хийморь", "author": "Ч. Лодойдамба", "year": 1962}Server response (статус 201):
json
{
"id": 1,
"title": "Цог хийморь",
"author": "Ч. Лодойдамба",
"year": 1962,
"century": 20
}century нь 20 — оролтод байгаагүй, сервер тооцоолж нэмсэн, response_model түүнийг гаргахыг зөвшөөрсөн. Оролт ба гаралт өөр хэлбэртэй, хоёр model хоёуланг зөв илэрхийлж байна.
/docs дээр POST /books мөрийг задалж, хоёр хэсгийг харьцуулаарай.
Request body хэсэгт BookCreate-ийн бүтэц: title, author, year. Хэрэглэгч илгээх ёстой зүйл.
Responses хэсэгт, 201 дор, BookPublic-ийн бүтэц: id, title, author, year, century. Хэрэглэгч авах зүйл.
/docs хоёр model-ыг тусад нь харуулж байна — оролт нэг бүтэцтэй, гаралт өөр бүтэцтэй. Хэрэглэгч юу илгээх, юу авахаа хоёуланг тодорхой харна. Та ганц мөр баримт бичиг бичээгүй; хоёр model-оос автоматаар үүссэн.
response_model-д илүү талбар зарлах
python
class BookPublic(BaseModel):
id: int
title: str
author: str
year: int
century: int
rating: float # функц энэ талбарыг буцаадаггүй!
@app.post("/books", response_model=BookPublic)
def create_book(book: BookCreate):
...
return new_book # rating байхгүйГаралт: 500. response_model нь rating-ыг заавал гэж зарласан, гэтэл функц түүнийг буцаагаагүй. Field required алдаа. Засвар: эсвэл функц rating буцаах, эсвэл response_model-д rating-ыг заавал бус (rating: float | None = None) болгох, эсвэл огт хасах.
Оролт-гаралтыг андуурах
python
@app.post("/users", response_model=User) # User бол оролтын model!
def create_user(user: User):
...response_model=User гэж бичвэл нууц үг дахин гарцад орно, учир нь User model-д password байна. Гаралтад гаралтын model (UserPublic) ашиглах ёстой. Хоёр model-ыг андуурах нь энэ бүлгийн хамгийн түгээмэл алдаа.
Гаралтад id мартах
python
class UserPublic(BaseModel):
name: str
email: str
# id байхгүйЭнэ нь алдаа өгөхгүй — зүгээр л хариунд id гарахгүй. Гэхдээ хэрэглэгчид ихэвчлэн id хэрэгтэй (дараа нь тэр хэрэглэгчийг засах, устгахад). Гаралтын model-д id оруулах нь бараг үргэлж зөв.
Шударга байя: энгийн API-д оролт-гаралтын model ижил байвал response_model заавал биш. Түвшин 1-ийн хотуудын API-д бид түүнийг ашиглаагүй — тэнд нуух зүйл байгаагүй, оролт-гаралт ижил байсан.
response_model дараах тохиолдолд чухал болно:
Нуух ёстой талбар байвал (нууц үг, дотоод өгөгдөл).
Оролт-гаралт өөр бүтэцтэй бол (оролтод id байхгүй, гаралтад байгаа).
Хариуны бүтцийг хатуу баталгаажуулах шаардлагатай бол.
Тиймээс дүрэм: нуух зүйл, эсвэл оролт-гаралтын ялгаа байвал response_model ашигла. Байхгүй бол заавал биш, гэхдээ ашиглах нь хэзээ ч буруу биш — хариуны бүтцийг тодорхой болгодог.
Бид Түвшин 2-ын бүтээн байгуулалт болон capstone-д response_model-ыг тогтмол ашиглана, учир нь мэргэжлийн API ингэж бүтдэг.
Хүсвэл UserPublic-д created_at: str гэсэн талбар нэмээд, функцэд new_user["created_at"] = "2026-07-15" гэж тавьж туршаад үзээрэй. Оролтод байхгүй, гаралтад нэмэгдсэн талбар хэрхэн ажиллахыг ажиглаарай.
Сонирхвол хоёр model-ыг санаатайгаар зөрчүүлж (жишээ нь response_model-д функц буцаадаггүй талбар нэмж) 500 алдаа үүсгээд, терминал дээрх Pydantic-ийн мессежийг уншаарай. response_model баталгаажуулалт хэрхэн ажилладгийг гар дээрээ мэдрэх нь ойлголтыг гүнзгийрүүлнэ.
response_model нь Pydantic-ийн гарцын шүүлтүүр үүрэг — хэрэглэгч рүү юу гарахыг хянана.
@app.post(..., response_model=UserPublic) — функц юу буцаасан ч, зөвхөн зарласан талбарыг гаргана.
Зарлаагүй талбар (нууц үг) шүүгдэнэ; серверт хадгалагдсан хэвээр — устгагддаггүй.
response_model нь баталгаажуулдаг ч: зарласан талбар дутуу бол 500 (таны алдаа).
Түгээмэл загвар: оролтын model (id-гүй, нууцтай) ба гаралтын model (id-тэй, нууцгүй) тусад нь.
Гаралтад тооцоолсон талбар нэмж болно (century) — оролт-гаралт өөр хэлбэртэй.
/docs оролт-гаралтыг тусад нь харуулна.
Нуух зүйл, эсвэл оролт-гаралтын ялгаа байвал response_model ашигла.
Одоо та хариугаа хянаж чадна. Гэхдээ бидний бүх model одоог хүртэл хавтгай байсан — талбарууд нь энгийн төрлүүд (текст, тоо, bool). Бодит өгөгдөл ихэвчлэн илүү гүн байдаг: ном дотор зохиолчийн бүтэн мэдээлэл, зохиолч дотор төрсөн газар. Дараагийн хичээлд бид model доторх model сурна — Курс 2-ын үүрлэсэн бүтцийн мэдлэг Pydantic-д хэрхэн ажиллаж, гүн JSON хэрхэн орж гардгийг үзнэ. Тэр нь Бүлэг 6-ийн сүүлчийн ойлголт бөгөөд түүний дараа бид Түвшин 2-ын гол бүтээн байгуулалт руу орно.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.