Ачааллж байна...
Бүлэг 10-т бид онолын суурь тавьсан: өгөгдлийн сан яагаад хэрэгтэй, SQLite гэж юу болох, SQL хэрхэн уншихыг сурсан. Одоо гараа ажиллуулна.
Энэ бүлэг SQLModel-д зориулагдана. Тэр бол Python class-ыг өгөгдлийн сангийн хүснэгт болгож хувиргадаг сан юм. Энэ хичээл голдуу ойлголтын тухай — SQLModel яагаад оршдог, ямар асуудлыг шийддэг, таны аль хэдийн мэддэг зүйлстэй хэрхэн холбогдохыг ойлгоно. Код бага бичнэ; дараагийн гурван хичээлд бид түүнийг бүрэн ашиглана.
Бүлэг 10-ын туршилтад бид түүхий sqlite3 модулиар ажилласан. Тэр код ийм харагдаж байсан:
python
cursor.execute("INSERT INTO notes (text, done) VALUES (?, ?)", ("Сүү авах", 0))
cursor.execute("SELECT * FROM notes")
rows = cursor.fetchall()
for row in rows:
print(row)Гаралт:
(1, 'Сүү авах', 0)
(2, 'Номоо буцаах', 1)Энэ ажилладаг. Гэхдээ дөрвөн ноцтой асуудалтай.
Асуудал 1: SQL нь зүгээр л текст
python
cursor.execute("SELCT * FROM notes")Үсгийн алдаа (SELCT). Editor тань юу ч хэлэхгүй — түүний хувьд энэ бол зүгээр л текст. Алдаа зөвхөн ажиллах үед илэрнэ.
Курс 2-ын Бүлэг 4-т сурсан зарчмыг санаарай: алдааг эрт барих нь илүү дээр. Type hint нь editor-т алдаа харуулдаг тул ажиллахаас өмнө засдаг. SQL нь текст болохоор энэ давуу талыг алддаг.
Багааны нэр буруу бичих, хүснэгтийн нэр андуурах, хаалт мартах — бүгд ажиллах үеийн алдаа болно.
Асуудал 2: мөр нь tuple
python
row = (1, 'Сүү авах', 0)Мөр бүр tuple хэлбэрээр ирнэ. Утга авахын тулд индексээр хандана:
python
note_id = row[0]
text = row[1]
done = row[2]row[1] гэж юу гэсэн үг вэ? Кодыг уншиж байгаа хүн (эсвэл хэдэн сарын дараах та) мэдэхгүй. Багааны дараалал өөрчлөгдвөл бүх код эвдэрнэ, гэхдээ ямар ч алдаа гарахгүй — зөвхөн буруу утга ирнэ.
Курс 1-ээс хойш бид note["text"] эсвэл note.text гэж бичиж ирсэн — нэрээр. row[1] бол ухарч байгаа хэрэг.
Асуудал 3: төрөл байхгүй
python
done = row[2] # 0 эсвэл 1SQLite-д BOOLEAN байхгүй тул done нь 0 эсвэл 1 гэсэн тоо болж ирнэ. Python-д түүнийг True/False болгож хөрвүүлэх ажил таных.
Огноо, бутархай тоо, бусад төрөлд ч ижил асуудал. Хөрвүүлэлт бүрийг гараар хийх ёстой, мартвал нарийн алдаа гарна.
Асуудал 4: Pydantic-тай холбогдохгүй
Хамгийн ноцтой нь энэ. Таны API-д аль хэдийн model байгаа:
python
class NotePublic(BaseModel):
id: int
text: str
done: boolОдоо өгөгдлийн сангаас tuple ирлээ. Хоёрын хооронд гүүр барих ёстой:
python
row = (1, 'Сүү авах', 0)
note = NotePublic(id=row[0], text=row[1], done=bool(row[2]))Мөр бүрд, endpoint бүрд ийм хөрвүүлэлт. Талбар нэмэхэд бүх хөрвүүлэлтийг засах ёстой. Уйтгартай, алдаатай, давтагдмал.
Хамгийн муу нь: та нэг зүйлийг гурван удаа тодорхойлж байна. Нэг удаа CREATE TABLE SQL-д. Нэг удаа Pydantic model-д. Нэг удаа хөрвүүлэлтийн кодонд. Гурав нь зөрчилдвөл — нарийн алдаа.
Эдгээр асуудлууд шинэ биш. Программистууд хэдэн арван жил тэдгээртэй тэмцсэн бөгөөд шийдэл нь ORM гэж нэрлэгддэг.
ORM гэдэг нь Object-Relational Mapper — "объект-хүснэгт холбогч". Түүний ажил нь хоёр ертөнцийг холбох: Python-ы объектын ертөнц болон өгөгдлийн сангийн хүснэгтийн ертөнц.
ORM-той ажиллахад:
Та class бичнэ, CREATE TABLE SQL биш.
Та объект үүсгэнэ, INSERT SQL биш.
Та объект хүлээж авна, tuple биш.
Төрлийн хөрвүүлэлт автоматаар болно.
Өөрөөр хэлбэл: та Python-оор бодож, Python-оор бичнэ. ORM нь SQL-ыг таны өмнөөс үүсгэж, өгөгдлийн сантай ярина.
Python-д хамгийн алдартай ORM бол SQLAlchemy — олон жилийн түүхтэй, маш хүчирхэг, маш өргөн ашиглагддаг. Гэхдээ тэр нь бас нэлээд төвөгтэй, сурахад цаг шаарддаг.
Энд FastAPI-ийн зохиогч Sebastián Ramírez нэг асуудлыг анзаарсан.
FastAPI хэрэглэгчид хоёр зүйл бичих ёстой байсан. Нэг нь Pydantic model (API-ийн validation, /docs):
python
class Note(BaseModel):
id: int
text: str
done: boolНөгөө нь SQLAlchemy model (өгөгдлийн сан):
python
class NoteDB(Base):
__tablename__ = "notes"
id = Column(Integer, primary_key=True)
text = Column(String)
done = Column(Boolean)Бараг ижил мэдээлэл, хоёр удаа бичигдсэн. Хоёр өөр синтаксаар. Талбар нэмэхэд хоёр газар засах ёстой. Хоёрын хооронд хөрвүүлэлт бичих ёстой.
SQLModel бол тэр давхардлыг арилгах шийдэл юм. Нэг class бичээд, тэр нь хоёуланг нь хийнэ:
python
class Note(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
text: str
done: bool = FalseЭнэ class нь:
Pydantic model — validation хийнэ, /docs-д гарна, response_model-д ашиглагдана.
Өгөгдлийн сангийн хүснэгт — CREATE TABLE үүсгэнэ, мөр хадгална, хайлт хийнэ.
Нэг тодорхойлолт, хоёр ажил.
Түүнчлэн SQLModel нь SQLAlchemy дээр баригдсан — өөрөөр хэлбэл дотроо тэр хүчирхэг ORM-ыг ашигладаг, зөвхөн илүү энгийн, FastAPI-д тохирсон гадаргуутай.
Одоо энэ курсын хамгийн чухал давтагдлыг харъя.
Курс 2, Бүлэг 4: та type hint сурсан. Тэр үед тэдгээр нь editor-т тусалдаг — автомат гүйцээлт, алдаа харуулах. Python өөрөө тэднийг албадан хэрэгжүүлдэггүй байсан.
Курс 3, Бүлэг 2 болон 4: ижил type hint нь FastAPI-д validation болов. book_id: int гэж бичихэд буруу утга 422 болов. Pydantic model-ын талбарууд шалгагдав.
Одоо, Бүлэг 11: ижил type hint нь өгөгдлийн сангийн schema болно. text: str гэж бичихэд text TEXT NOT NULL гэсэн багана үүснэ.
Нэг мэдээлэл, гурван ашиг. Та text: str гэж нэг удаа бичихэд:
Editor тань note.text дээр текстийн method санал болгоно.
API нь текст бус утгыг татгалзана.
Өгөгдлийн сан нь текст багана үүсгэнэ.
Курс 2-ын Бүлэг 4-т би "type hint-ийг сурах нь чухал, учир нь орчин үеийн framework-ууд тэдгээр дээр баригдсан" гэж хэлж байсан. Одоо та тэр өгүүлбэрийн бүрэн утгыг харж байна.
Барилгын зургийг төсөөлье. Нэг зураг дээр өрөөний хэмжээ, хаалганы байрлал, цонхны тоо бичигдсэн.
Тэр нэг зургийг олон хүн уншина. Барилгачин ханыг хаана босгохоо мэднэ. Цахилгаанчин утас хаагуур татахаа мэднэ. Худалдагч талбайг мэдэж үнэ тогтооно.
Гурван өөр мэргэжилтэн, нэг зураг. Хэрэв тус бүр өөрийн зурагтай байсан бол — гурав нь зөрчилдөж, хана буруу газар босч, утас өөр газраар татагдана.
SQLModel class бол тэр нэг зураг юм. Pydantic (API), SQLAlchemy (өгөгдлийн сан), таны editor — гурвуулаа нэг эх сурвалжаас уншина. Зөрчилдөх боломжгүй.
Одоо гол ойлголтыг тодорхой болгоё. SQLModel-д:
Class нь хүснэгт. class Note(SQLModel, table=True) -> note гэсэн хүснэгт.
Class-ын талбар нь багана. text: str -> text гэсэн TEXT багана.
Объект нь мөр. Note(text="Сүү авах") -> хүснэгтийн нэг мөр.
Гурван түвшний тохирол:
Python | Өгөгдлийн сан |
|---|---|
Class | Хүснэгт (table) |
Талбар (field) | Багана (column) |
Объект (instance) | Мөр (row) |
Бүлэг 10-ын Хичээл 1-т би хүснэгтийг "dictionary-үүдийн жагсаалт шиг" гэж тайлбарласан. Одоо түүнийг илүү нарийн хэлж болно: хүснэгт бол нэг class-ын объектуудын цуглуулга.
Курс 2-ын Бүлэг 7-9-т та class, объект, dataclass сурсан. Тэр мэдлэг энд шууд ажиллана — зөвхөн одоо объектууд санах ойд биш, дискэн дээр амьдрах болно.
Одоо гурван хэлбэрийг зэрэгцүүлж харъя. Гурвуулаа ижил зүйлийг тодорхойлж байна.
SQL:
sql
CREATE TABLE note (
id INTEGER PRIMARY KEY,
text TEXT NOT NULL,
done INTEGER DEFAULT 0
)Pydantic:
python
class Note(BaseModel):
id: int
text: str
done: bool = FalseSQLModel:
python
class Note(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
text: str
done: bool = FalseГурвуулаа: id (бүхэл тоо, үндсэн түлхүүр), text (текст, заавал), done (үнэн/худал, default худал).
SQLModel хувилбар нь Pydantic-тай бараг ижил. Хоёр ялгаа:
(SQLModel, table=True) — BaseModel-ээс өвлөхийн оронд SQLModel-ээс өвлөж байна, мөн table=True гэсэн тэмдэг. Тэр тэмдэг нь "энэ class бол зөвхөн model биш, хүснэгт ч мөн" гэж хэлж байна.
Field(default=None, primary_key=True) — id талбарт нэмэлт мэдээлэл. primary_key=True нь "энэ бол үндсэн түлхүүр" гэсэн үг. default=None болон int | None нь яагаад хэрэгтэйг доор тайлбарлана.
Бусад талбарууд (text: str, done: bool = False) нь яг ижил — ямар ч нэмэлт зүйлгүй. Энгийн талбарууд ердийн Pydantic шиг бичигдэнэ.
Энэ нь эхэндээ хачирхалтай харагддаг тул тайлбарлая. id бол заавал байх ёстой мэт санагдана — тэгвэл яагаад None байж болох вэ?
Хариу нь цаг хугацаа.
Та шинэ тэмдэглэл үүсгэхэд:
python
note = Note(text="Сүү авах")Энэ мөчид тэмдэглэл хараахан өгөгдлийн санд ороогүй. Тиймээс түүнд id байхгүй — id-г өгөгдлийн сан хадгалахдаа өгдөг (Бүлэг 10-ын PRIMARY KEY автомат нэмэгдэлт).
Тиймээс note.id нь энэ мөчид None.
Дараа нь та түүнийг хадгална. Өгөгдлийн сан id өгнө. Одоо note.id нь 1 (эсвэл дараагийн дугаар).
Тэгэхээр id нь амьдралынхаа зарим хэсэгт None, зарим хэсэгт тоо байдаг. int | None нь яг тэр байдлыг илэрхийлж байна.
default=None нь "объект үүсгэхэд id өгөх шаардлагагүй" гэсэн үг — Бүлэг 4-ийн default утгын мэдлэг.
Энэ загварыг та SQLModel-ийн бүх жишээнд харна. Тэр нь стандарт, зөв бичлэг юм.
Field бол SQLModel (болон Pydantic)-ийн хэрэгсэл: талбарт нэмэлт мэдээлэл хавсаргах.
Ердийн талбарт Field хэрэггүй:
python
text: str
done: bool = FalseГэхдээ нэмэлт зүйл хэлэх шаардлагатай бол Field ашиглана:
python
id: int | None = Field(default=None, primary_key=True)primary_key=True — энэ бол Field-гүйгээр илэрхийлж болохгүй мэдээлэл. Type hint нь төрлийг хэлдэг; Field нь өгөгдлийн сангийн онцлогийг хэлдэг.
Бид энэ курсэд Field-ыг голдуу primary_key болон index-д ашиглана. Түүнд өөр олон боломж байдаг (утгын хязгаар, тайлбар, нэрийн өөрчлөлт) боловч тэдгээр нь энэ курсын хүрээнээс гадуур.
Шударга хүрээ тогтооё. SQLModel нь энэ курсэд үзэхээс илүү зүйл хийж чадна.
Relationship — хүснэгт хоорондын холбоо (тэмдэглэл нь хэрэглэгчид харьяалагдах гэх мэт). SQLModel үүнийг дэмждэг бөгөөд бид capstone-д хялбар хэлбэрээр (гадаад түлхүүр) хүрнэ, гэхдээ гүнзгий Relationship объект, автомат ачаалалт зэргийг үзэхгүй.
Migration — хүснэгтийн бүтэц өөрчлөгдөхөд байгаа өгөгдлийг хадгалж шилжүүлэх. Энэ нь Alembic гэсэн тусдаа хэрэгсэл шаарддаг бөгөөд өөрөө том сэдэв. Бид Бүлэг 15-т түүнийг цаашдын зам болгон нэрлэнэ.
Async өгөгдлийн сан — async горимд ажиллах. Боломжтой, гэхдээ илүү төвөгтэй бөгөөд энэ курсын хэрэгцээнд шаардлагагүй.
Гүнзгий асуулт — join, aggregate, subquery. SQLModel эдгээрийг дэмждэг (SQLAlchemy-ээс өвлөсөн) боловч бид энгийн CRUD-д анхаарлаа төвлөрүүлнэ.
Эдгээрийг мэдэж байх нь чухал: та SQLModel-ыг бүрэн эзэмшихгүй, харин ажиллуулж чадах хэмжээнд сурна. Тэр нь бодит API бүтээхэд хангалттай бөгөөд цаашид сурах суурь болно.
Одоо гараа ажиллуулъя. Терминал дээр ((venv) идэвхтэй эсэхээ шалгаад):
pip install sqlmodelГаралт (ойролцоогоор):
Successfully installed sqlmodel-0.0.39 sqlalchemy-2.0.x ...Хэдэн package суусныг анзаараарай. sqlmodel өөрөө, мөн sqlalchemy (доор нь ажилладаг ORM). Pydantic аль хэдийн суусан (FastAPI-тай хамт) тул дахин суухгүй.
Хэрэв ModuleNotFoundError гарвал — venv идэвхтэй эсэхээ шалгаарай.
Батлах
python -c "import sqlmodel; print(sqlmodel.__version__)"Гаралт:
0.0.39(Таны хувилбар өөр байж болно — SQLModel байнга шинэчлэгддэг. 0.0. гэж эхэлж байвал зөв.)
Хувилбарын дугаар 0.0.x байгааг анзаараарай. Энэ нь "бэлэн биш" гэсэн үг мэт санагдаж болно, гэхдээ SQLModel нь бодитоор ажиллаж байгаа, өргөн ашиглагддаг сан юм. Дугаарын систем нь зохиогчийн сонголт бөгөөд тогтвортой байдлыг илэрхийлдэггүй.
Одоо SQLModel class хэрхэн харагддагийг бүтэн жишээгээр харъя. Кодыг ойлгох гэж, ажиллуулах гэж биш — дараагийн хичээлд бид түүнийг алхам алхмаар барина.
python
# жишээ (одоо ажиллуулах шаардлагагүй)
from sqlmodel import SQLModel, Field, Session, create_engine, select
class Note(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
text: str
done: bool = False
engine = create_engine("sqlite:///notes.db")
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
note = Note(text="Сүү авах")
session.add(note)
session.commit()
with Session(engine) as session:
notes = session.exec(select(Note)).all()
for n in notes:
print(n.id, n.text, n.done)Хорин мөр орчим. Энд юу байгааг товч тоймлоё — тус бүрийг дараагийн хичээлүүдэд гүнзгий үзнэ.
class Note(SQLModel, table=True) — хүснэгтийн тодорхойлолт (Хичээл 2).
create_engine("sqlite:///notes.db") — өгөгдлийн сантай холбогдох (Хичээл 2).
SQLModel.metadata.create_all(engine) — хүснэгтүүдийг үүсгэх (Хичээл 2).
Session(engine) — өгөгдлийн сантай нэг удаагийн яриа (Хичээл 3).
session.add(note) болон session.commit() — нэмж хадгалах (Хичээл 3).
select(Note) — унших асуулт (Хичээл 3).
Одоо түүхий SQL-ын хувилбартай харьцуулъя:
python
cursor.execute("INSERT INTO notes (text, done) VALUES (?, ?)", ("Сүү авах", 0))болон
python
note = Note(text="Сүү авах")
session.add(note)Хоёр дахь нь Python. Class-аас объект үүсгэж, түүнийг нэмж байна — Курс 2-оос танил үйлдэл. SQL хаана ч байхгүй.
Мөн уншихдаа:
python
for n in notes:
print(n.id, n.text, n.done)n.text — цэгээр, row[1] биш. n.done нь жинхэнэ True/False, 0/1 биш. Объект, tuple биш.
Хичээлийн эхэнд ярьсан дөрвөн асуудал бүгд шийдэгдсэн.
Түүхий sqlite3-ийн дөрвөн асуудал: SQL нь текст (editor тусалдаггүй), мөр нь tuple (row[1]), төрөл хөрвүүлэхгүй (0/1), Pydantic-тай холбогдохгүй (гар аргын хөрвүүлэлт).
ORM (Object-Relational Mapper) нь Python объект болон өгөгдлийн сангийн хүснэгтийг холбоно — та class бичнэ, SQL биш.
SQLModel нь FastAPI-ийн зохиогчийн бүтээл: нэг class = Pydantic model + өгөгдлийн сангийн хүснэгт. Доор нь SQLAlchemy ажилладаг.
Өмнө нь хоёр model (Pydantic + SQLAlchemy) бичих шаардлагатай байсныг нэг болгосон.
Type hint гурав дахь удаагаа ажиллана: editor-ын тусламж -> API validation -> өгөгдлийн сангийн schema.
Тохирол: class = хүснэгт, талбар = багана, объект = мөр.
Бичлэг: class Note(SQLModel, table=True) — table=True нь "энэ бол хүснэгт" гэсэн тэмдэг.
id: int | None = Field(default=None, primary_key=True) — id нь хадгалахаас өмнө None, дараа нь тоо болдог тул int | None.
Field нь талбарт нэмэлт мэдээлэл (primary_key, index) хавсаргана; ердийн талбарт хэрэггүй.
Заахгүй зүйлс: Relationship-ийн гүн, migration (Alembic), async, join.
Суулгах: pip install sqlmodel.
Одоо та SQLModel яагаад оршдог, юуг шийддэгийг ойлголоо. Дараагийн хичээлд бид эхний жинхэнэ хүснэгтээ үүсгэнэ: model бичиж, engine тохируулж, create_all дуудаж, дараа нь .db файл файлын жагсаалт дээр гарч ирэхийг өөрийн нүдээр харна. Тэр бол өгөгдлийн сан хийсвэр ойлголтоос бодит зүйл болж хувирах мөч юм.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.