Ачааллж байна...
Өмнөх хичээлд бид SQLModel яагаад оршдогийг ойлгосон. Одоо эхний жинхэнэ хүснэгтээ үүсгэнэ.
Энэ хичээлд гурван шинэ ойлголт байна: table model (class-ыг хүснэгт болгох), engine (өгөгдлийн сантай холбогдох), create_all (хүснэгтүүдийг бодитоор үүсгэх). Тэдгээрийг нэг нэгээр нь барьж, эцэст нь .db файл файлын жагсаалт дээр гарч ирэхийг өөрийн нүдээр харна.
Тэр бол өгөгдлийн сан хийсвэр ойлголтоос бодит файл болж хувирах мөч юм. Бид түүнийг зохих ёсоор тэмдэглэнэ.
Энэ бүлэгт бид FastAPI-гүйгээр, зөвхөн SQLModel-тэй ажиллана. Ингэснээр өгөгдлийн сангийн ойлголтууд вэб орчны нэмэлт давхаргагүйгээр цэвэрхэн харагдана. Бүлэг 12-т бид тэдгээрийг API-даа залгана.
Төслийн хавтаст db_test.py файл үүсгэнэ. Түүнийг python db_test.py гэж ажиллуулна — fastapi dev биш.
Хоёр дахь терминал нээх нь тохиромжтой (VS Code-ийн терминал хэсэгт + товч), ингэснээр таны сервер нэг цонхонд ажиллаж, туршилт нөгөөд явна. Гэхдээ энэ бүлэгт сервер шаардлагагүй тул түүнийг зогсоож ч болно.
db_test.py файлд бичнэ:
python
# db_test.py
from sqlmodel import SQLModel, Field
class Note(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
text: str
priority: str = "энгийн"
done: bool = FalseХадгална. Ажиллуулна:
python db_test.pyЮу ч гарахгүй. Энэ бол зөв — бид зөвхөн class зарласан. Хүснэгт хараахан үүсээгүй; өгөгдлийн сан ч байхгүй.
Кодын задаргаа
python
from sqlmodel import SQLModel, FieldSQLModel — суурь class, түүнээс өвлөнө. Field — талбарт нэмэлт мэдээлэл хавсаргах хэрэгсэл.
python
class Note(SQLModel, table=True):Хоёр зүйл. SQLModel-ээс өвлөж байна (Pydantic-ийн BaseModel-ийн оронд). table=True нь гол тэмдэг — "энэ class бол хүснэгт" гэж хэлж байна.
table=True байхгүй бол энэ class нь зөвхөн ердийн Pydantic model байх байсан — validation хийдэг, гэхдээ өгөгдлийн сантай холбоогүй. Тэр ялгааг доор дэлгэрэнгүй үзнэ.
python
id: int | None = Field(default=None, primary_key=True)Өмнөх хичээлээс танил. int | None — id нь хадгалахаас өмнө None, дараа нь тоо. default=None — объект үүсгэхэд өгөх шаардлагагүй. primary_key=True — энэ бол үндсэн түлхүүр, мөр бүрийг ялгах давтагдашгүй утга.
python
text: str
priority: str = "энгийн"
done: bool = FalseЕрдийн талбарууд, Field хэрэггүй. text нь default байхгүй тул заавал. priority болон done нь default-тай тул заавал бус.
Энэ бол яг Pydantic-ийн синтакс. Бүлэг 4-т сурсан бүх зүйл — type hint, default утга, заавал/заавал бусын дүрэм — энд ижилхэн ажиллана.
Хүснэгтийн нэр
Нэг зүйлийг тодруулъя. Class-ын нэр Note (том үсгээр), гэхдээ өгөгдлийн сангийн хүснэгтийн нэр нь note (жижиг үсгээр) болно.
SQLModel класын нэрийг автоматаар жижиг үсэг болгодог. Note -> note. BookAuthor -> bookauthor.
Хэрэв өөр нэр хүсвэл зааж болно:
python
class Note(SQLModel, table=True):
__tablename__ = "notes"
...Бид үүнийг ашиглахгүй — автомат нэр хангалттай. Гэхдээ алдааны мессежид note гэсэн жижиг үсэгтэй нэр харагдах болно, тэр нь таны Note class гэдгийг мэдэж байх нь зүйтэй.
Class байгаа боловч өгөгдлийн сан байхгүй. Одоо холболт үүсгэе.
db_test.py-г солино:
python
# db_test.py
from sqlmodel import SQLModel, Field, create_engine
class Note(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
text: str
priority: str = "энгийн"
done: bool = False
engine = create_engine("sqlite:///notes.db", echo=True)
print("Engine үүслээ")Ажиллуулна:
python db_test.pyГаралт:
Engine үүслээФайлын жагсаалтаа хараарай. notes.db файл хараахан үүсээгүй байх магадлалтай (эсвэл хоосон файл үүссэн байж болно).
Учир нь create_engine нь зөвхөн холболтын тохиргоо үүсгэдэг — бодит холболт хараахан хийгдээгүй. Тэр нь залгуур бэлдсэн ч цахилгаан асаагаагүйтэй адил.
Кодын задаргаа
python
engine = create_engine("sqlite:///notes.db", echo=True)engine бол өгөгдлийн сантай холбогдох гол объект. Түүнийг программдаа нэг удаа үүсгэдэг бөгөөд бүх ажилд ашигладаг.
"sqlite:///notes.db" — энэ бол холболтын мөр (connection string). Түүнийг задалж харъя.
sqlite: — ямар төрлийн өгөгдлийн сан. Хэрэв PostgreSQL байсан бол postgresql: байх байсан.
///notes.db — гурван зураас, дараа нь файлын нэр. Гурван зураас нь "одоогийн хавтасны харьцангуй зам" гэсэн утгатай. Тиймээс notes.db файл нь таны төслийн хавтаст үүснэ.
Холболтын мөрийн бүтцийг цээжлэх шаардлагагүй — та түүнийг нэг удаа бичээд мартана. Гэхдээ sqlite:/// гэсэн хэсэг нь өгөгдлийн сангийн төрлийг заадгийг мэдэж байх нь чухал: PostgreSQL руу шилжихэд зөвхөн энэ мөр өөрчлөгдөнө.
echo=True — энэ бол сургалтад маш ашигтай тохиргоо. Тэр нь SQLModel-ийн үүсгэсэн бүх SQL-ыг терминал дээр хэвлэдэг. Бүлэг 10-т сурсан SQL мэдлэг тань энд шууд ашигтай болно — та юу болж байгааг өөрийн нүдээр харна.
Production-д echo=True ашиглахгүй — тэр нь log-ыг дүүргэдэг, гүйцэтгэлийг удаашруулдаг. Гэхдээ сурч байхад асар их тусалдаг тул бид түүнийг энэ бүлэгт ашиглана.
Одоо жинхэнэ мөч. db_test.py-г солино:
python
# db_test.py
from sqlmodel import SQLModel, Field, create_engine
class Note(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
text: str
priority: str = "энгийн"
done: bool = False
engine = create_engine("sqlite:///notes.db", echo=True)
SQLModel.metadata.create_all(engine)
print("Хүснэгт үүслээ")Ажиллуулна:
python db_test.pyГаралт:
2026-07-16 10:23:45,123 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2026-07-16 10:23:45,124 INFO sqlalchemy.engine.Engine PRAGMA main.table_info("note")
2026-07-16 10:23:45,124 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-07-16 10:23:45,125 INFO sqlalchemy.engine.Engine
CREATE TABLE note (
id INTEGER NOT NULL,
text VARCHAR NOT NULL,
priority VARCHAR NOT NULL,
done BOOLEAN NOT NULL,
PRIMARY KEY (id)
)
2026-07-16 10:23:45,126 INFO sqlalchemy.engine.Engine [no key 0.00012s] ()
2026-07-16 10:23:45,127 INFO sqlalchemy.engine.Engine COMMIT
Хүснэгт үүслээЭнэ гаралтыг сайтар хараарай — энд чухал зүйл байна.
CREATE TABLE-ыг таних
Дунд нь энэ хэсэг байна:
sql
CREATE TABLE note (
id INTEGER NOT NULL,
text VARCHAR NOT NULL,
priority VARCHAR NOT NULL,
done BOOLEAN NOT NULL,
PRIMARY KEY (id)
)Энэ бол SQL. Та түүнийг бичээгүй — SQLModel таны Python class-аас автоматаар үүсгэсэн.
Бүлэг 10-ын Хичээл 3-т сурсан мэдлэгээ ашиглан уншъя:
CREATE TABLE note — note гэсэн хүснэгт үүсгэ. (Class нь Note, хүснэгт нь note — жижиг үсгээр.)
id INTEGER NOT NULL — id багана, бүхэл тоо.
text VARCHAR NOT NULL — text багана, текст. (VARCHAR нь TEXT-тэй ижил утгатай — өөр нэршил.)
priority VARCHAR NOT NULL — текст.
done BOOLEAN NOT NULL — үнэн/худал.
PRIMARY KEY (id) — id бол үндсэн түлхүүр.
Python class болон SQL-ыг зэрэгцүүлэн харъя:
Python | SQL |
|---|---|
|
|
|
|
|
|
|
|
Мөр мөрөөр таарч байна. Таны type hint нь SQL болж хувирсан. Өмнөх хичээлд ярьсан "type hint гурав дахь удаа" гэдэг нь яг энэ.
Анзаараарай: default утга ("энгийн", False) нь SQL-д ороогүй, оронд нь NOT NULL гэж бичигдсэн. SQLModel нь default-ыг Python талд зохицуулдаг — объект үүсгэхэд утга оноодог, тиймээс өгөгдлийн санд үргэлж утга ирдэг. Энэ нь өөр аргаар ижил үр дүнд хүрч байна.
Файлын жагсаалтаа хараарай
Одоо VS Code-ийн зүүн талын файлын жагсаалт дээр шинэ файл гарч ирсэн байх ёстой:
notes.dbТаны өгөгдлийн сан бодитоор оршиж байна. Энэ бол файл — та түүнийг харж, хуулж, зөөж, устгаж чадна. Дотор нь note гэсэн хүснэгт байгаа, одоохондоо хоосон.
Хэрэв файл харагдахгүй бол Explorer-ийн refresh товчийг дарна уу.
Энэ бол Бүлэг 10-ын Хичээл 2-т ярьсан мөч: өгөгдлийн сан бол хийсвэр зүйл биш, харин таны харж чадах файл.
create_all юу хийдэг вэ
python
SQLModel.metadata.create_all(engine)Энэ мөр гурван зүйл хийнэ.
Нэгд, table=True бүхий бүх class-ыг олно. SQLModel.metadata нь тэдгээрийн бүртгэл — та class зарлахад автоматаар бүртгэгддэг. Хэрэв танд арван table model байсан бол create_all арвуулангийнх нь хүснэгтийг үүсгэнэ, нэг мөрөөр.
Хоёрт, аль хүснэгт аль хэдийн байгааг шалгана. Гаралтын эхэнд PRAGMA main.table_info("note") гэсэн мөр байсныг анзаараарай — тэр нь "note хүснэгт байна уу?" гэж асууж байна.
Гуравт, байхгүй хүснэгтүүдийг үүсгэнэ. Байгаа хүснэгтэд гар хүрэхгүй.
Тиймээс create_all-ыг олон удаа дуудаж болно — аюулгүй. Дараагийн алхамд үүнийг батлана.
Скриптийг дахин ажиллуулна:
python db_test.pyГаралт:
2026-07-16 10:25:12,456 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2026-07-16 10:25:12,457 INFO sqlalchemy.engine.Engine PRAGMA main.table_info("note")
2026-07-16 10:25:12,457 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-07-16 10:25:12,458 INFO sqlalchemy.engine.Engine COMMIT
Хүснэгт үүслээCREATE TABLE мөр байхгүй.
PRAGMA main.table_info("note") гэсэн шалгалт хийгдсэн, хүснэгт аль хэдийн байгааг олсон, тиймээс дахин үүсгээгүй.
Энэ бол чухал зан төлөв: create_all нь давтагдахад аюулгүй. Тиймээс түүнийг программ эхлэх бүрд дуудаж болно — Бүлэг 12-т бид яг тэгнэ.
Одоо нэг маш чухал зүйлийг харуулъя. Энэ нь ирээдүйд таныг төөрөгдүүлэхээс сэргийлнэ.
Model-д шинэ талбар нэмье:
python
class Note(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
text: str
priority: str = "энгийн"
done: bool = False
tags: str = "" # шинэ талбарХадгалаад ажиллуулна:
python db_test.pyГаралт:
... PRAGMA main.table_info("note")
... COMMIT
Хүснэгт үүслээCREATE TABLE байхгүй. Хүснэгт өөрчлөгдөөгүй.
Энэ бол чухал хязгаарлалт: create_all нь байгаа хүснэгтийг өөрчилдөггүй. Тэр зөвхөн байхгүй хүснэгтийг үүсгэдэг.
Тиймээс таны Python model-д tags талбар байгаа боловч өгөгдлийн санд тэр багана байхгүй. Хэрэв та tags-тай тэмдэглэл хадгалахыг оролдвол:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) table note has no column named tagsБүлэг 10-ын Хичээл 3-т үзсэн алдааны төрөл — одоо та түүнийг уншиж чадна.
Яагаад ингэдэг вэ
Учир нь хүснэгтийг өөрчлөх нь аюултай. Хэрэв хүснэгтэд сая мөр байгаа бол, шинэ багана нэмэхэд тэдгээрт ямар утга өгөх вэ? Багана устгахад тэр өгөгдлийг яах вэ? Төрөл өөрчлөхөд байгаа утгуудыг яаж хөрвүүлэх вэ?
Эдгээр асуултууд нарийн бөгөөд автоматаар шийдэх боломжгүй. Тиймээс create_all нь юу ч эвдэхгүй байхыг сонгодог: байгаа хүснэгтэд гар хүрэхгүй.
Бодит шийдэл нь migration — байгаа өгөгдлийг хадгалж, бүтцийг алхам алхмаар өөрчлөх систем. Python-д тэр нь Alembic гэсэн хэрэгсэл бөгөөд өөрөө том сэдэв юм. Бид Бүлэг 15-т түүнийг цаашдын зам болгон нэрлэнэ.
Сургалтын үеийн шийдэл
Хөгжүүлж байх үед, өгөгдөл нь чухал биш үед хамгийн энгийн шийдэл нь: .db файлыг устгаж, дахин үүсгэх.
Файлын жагсаалт дээр notes.db-г устгана (баруун товшоод Delete). Дараа нь скриптээ дахин ажиллуулна:
python db_test.pyГаралт:
CREATE TABLE note (
id INTEGER NOT NULL,
text VARCHAR NOT NULL,
priority VARCHAR NOT NULL,
done BOOLEAN NOT NULL,
tags VARCHAR NOT NULL,
PRIMARY KEY (id)
)tags VARCHAR NOT NULL — шинэ багана орсон. Хүснэгт шинээр үүссэн.
Бүх хуучин өгөгдөл алга болсон — гэхдээ одоохондоо өгөгдөл байгаагүй тул асуудалгүй.
Дүрэм: сургалтын үед model өөрчлөх бүрд .db файлыг устгаж, дахин үүсгэ. Бодит production-д хэзээ ч тэгэхгүй — тэнд migration хэрэгтэй. Гэхдээ сурч байхад энэ нь хамгийн энгийн, тодорхой арга юм.
Энэ дүрмийг санаж байгаарай — Бүлэг 12 болон capstone-д model өөрчлөхөд хэрэг болно.
Одоо нэг чухал ялгааг тодруулъя.
python
class NoteCreate(SQLModel): # table=True БАЙХГҮЙ
text: str
done: bool = False
class Note(SQLModel, table=True): # table=True БАЙНА
id: int | None = Field(default=None, primary_key=True)
text: str
done: bool = FalseNoteCreate нь зөвхөн Pydantic model — validation хийнэ, /docs-д гарна, гэхдээ өгөгдлийн санд хүснэгт үүсгэхгүй. create_all түүнийг тоохгүй.
Note нь хүснэгт ч мөн — create_all түүнд зориулж CREATE TABLE ажиллуулна.
Яагаад энэ хоёр хэрэгтэй вэ? Учир нь Бүлэг 6-т сурсан оролт-гаралтын model загвар энд ч хэрэгтэй хэвээр байна. Хэрэглэгч id илгээх ёсгүй тул оролтын model-д id байх ёсгүй; хүснэгтэд байх ёстой.
Бүлэг 12-т бид гурван model-ын загварыг ашиглана: оролтын model (NoteCreate, table биш), хүснэгтийн model (Note, table=True), гаралтын model (NotePublic, table биш). Одоохондоо ялгааг мэдэж байхад хангалттай.
echo=True нь SQL харуулдаг тул сурахад ашигтай. Гэхдээ гаралт их байдаг тул заримдаа хэрэггүй.
Унтраахын тулд зүгээр л хасна:
python
engine = create_engine("sqlite:///notes.db")Эсвэл тодорхой болгож:
python
engine = create_engine("sqlite:///notes.db", echo=False)Бид энэ бүлгийн үлдсэн хичээлүүдэд echo=True-г үлдээнэ — SQL харах нь сурахад тустай. Бүлэг 12-т API-д залгахдаа түүнийг унтраана.
Бүлэг 10-ын Хичээл 2-т бид .gitignore-д *.db нэмэхийг зөвлөсөн. Одоо түүнийг шалгах цаг:
.env
venv/
__pycache__/
*.pyc
*.db*.db мөр байгаа эсэхийг шалгаарай. Хэрэв байхгүй бол нэмнэ.
Яагаад чухал вэ? Учир нь notes.db нь өгөгдөл, код биш. Тэр нь хөгжүүлэгч бүрд өөр байх ёстой. Мөн бодит хэрэглэгчийн мэдээлэл санамсаргүй git-д орох эрсдэлээс сэргийлнэ.
table=True мартах
python
class Note(SQLModel): # table=True байхгүй
id: int | None = Field(default=None, primary_key=True)
text: strcreate_all ажиллана, гэхдээ юу ч үүсгэхгүй — алдаа гарахгүй, зүгээр л хоосон өгөгдлийн сан. Дараа нь тэмдэглэл хадгалах гэвэл:
sqlalchemy.exc.OperationalError: no such table: noteЗасвар: table=True нэмэх.
primary_key мартах
python
class Note(SQLModel, table=True):
id: int | None = Field(default=None) # primary_key байхгүй
text: strГаралт:
sqlalchemy.exc.ArgumentError: Mapper ... could not assemble any primary key columns for mapped table 'note'Хүснэгт бүрд үндсэн түлхүүр заавал байх ёстой — мөрүүдийг ялгах арга. primary_key=True нэмнэ.
create_all дуудахгүй
python
engine = create_engine("sqlite:///notes.db")
# create_all байхгүйДараа нь хадгалах гэвэл no such table: note. Class зарлах нь хүснэгт үүсгэдэггүй; create_all заавал.
Model өөрчилсний дараа .db устгахгүй
Дээр дэлгэрэнгүй үзсэн. Шинэ багана нэмээд хуучин .db файлаа үлдээвэл table note has no column named ... гарна.
Засвар: .db файлыг устгаж, дахин ажиллуулах.
Файлын зам буруу
python
engine = create_engine("sqlite://notes.db") # хоёр зураас — бурууГурван зураас (sqlite:///) байх ёстой. Хоёр зураастай бол SQLite түүнийг өөрөөр тайлбарлаж, хачирхалтай алдаа өгнө.
Хүсвэл model-д нэмэлт талбар нэмээд (жишээ нь created_by: str = "тодорхойгүй"), .db файлаа устгаад, дахин ажиллуулж, CREATE TABLE гаралтад шинэ багана орсныг ажиглаарай. Python-ы өөрчлөлт SQL болж хувирахыг хэд хэдэн удаа харах нь холбоог бататгана.
Сонирхвол echo=True-г False болгож, гаралт хэр цэвэрхэн болохыг хараарай. Дараа нь буцааж True болгоно — дараагийн хичээлд SQL харах нь тустай.
Хэрэв DB Browser for SQLite суулгасан бол notes.db файлыг нээж, note хүснэгтийг харна уу. Багануудыг өөрийн нүдээр харах нь ойлголтыг тодруулна.
class Note(SQLModel, table=True) — table=True нь "энэ class бол хүснэгт" гэсэн тэмдэг. Байхгүй бол зөвхөн Pydantic model.
Class-ын нэр автоматаар жижиг үсэг болно: Note -> note хүснэгт.
create_engine("sqlite:///notes.db") — холболтын тохиргоо, программд нэг удаа үүсгэнэ. Гурван зураас заавал.
echo=True нь үүсгэсэн SQL-ыг терминалд хэвлэнэ — сурахад ашигтай, production-д биш.
SQLModel.metadata.create_all(engine) — бүх table=True class-д хүснэгт үүсгэнэ. Байгаа хүснэгтэд гар хүрэхгүй тул давтагдахад аюулгүй.
Таны type hint нь CREATE TABLE SQL болж хувирна — text: str -> text VARCHAR NOT NULL.
create_all нь байгаа хүснэгтийг ӨӨРЧИЛДӨГГҮЙ. Талбар нэмбэл багана нэмэгдэхгүй.
Сургалтын үеийн дүрэм: model өөрчлөх бүрд .db файлыг устгаж, дахин үүсгэ. Production-д migration (Alembic) хэрэгтэй.
Хүснэгт бүрд primary_key=True заавал.
.gitignore-д *.db байх ёстой — өгөгдлийн сан бол өгөгдөл, код биш.
Таны өгөгдлийн сан бодитоор оршиж байна — файлын жагсаалт дээр notes.db харагдаж байна. Гэхдээ тэр хоосон: хүснэгт байгаа, мөр байхгүй.
Дараагийн хичээлд бид түүнийг дүүргэнэ. Session гэсэн шинэ ойлголт орж ирнэ — өгөгдлийн сантай хийх нэг удаагийн яриа. Бид тэмдэглэл нэмж, буцаан уншиж, commit яагаад чухал болохыг зориудаар мартаж үзэж ойлгоно. Тэнд echo=True тань ажиллаж, таны Python код INSERT болон SELECT болж хувирахыг өөрийн нүдээр харна.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.