Ачааллж байна...
Энэ сургалтыг "далд байсан SQL-ээ ил гаргая" гэсэн санаагаар эхэлсэн: FastAPI сургалтад SQLModel таны оронд SQL бичдэг орчуулагч байсан бөгөөд та тэр яриаг хэзээ ч сонсож байгаагүй. Өнгөрсөн арван гурван бүлэгт та тэр хэлийг өөрөө эзэмшсэн. Одоо орчуулагч руугаа буцаж очих цаг ирлээ — гэхдээ энэ удаад та түүний хэлж байгаа үг бүрийг ойлгоно. Энэ бүлэг ORM — SQLModel, түүний цаадах SQLAlchemy — -ийг гүнзгий харна.
Энэ хичээлд Бүлэг 5-ын FOREIGN KEY-ийг Python-оор илэрхийлж сурна. Өгөгдлийн санд зээлийн мөр member_id = 1 гэсэн тоо агуулдаг; Python-д бол loan.member гэж бичихэд Бат гэсэн бүтэн объект гарч ирдэг байвал илүү байгалийн. SQLModel-ийн Relationship энэ боломжийг олгодог: хүснэгтүүдийн хоорондын заалтуудыг объектуудын хоорондын холбоос болгодог. book.loans, loan.member, book.authors — Бүлэг 7-ийн JOIN-уудыг бичихгүйгээр холбоотой өгөгдөл рүү "алхах."
Гэвч энэ хичээлийн гол зорилго нь тухтай бичлэг сурах биш. Бүлэг 1-ийн "Туршаад үзээрэй"-ээс хойш хэрэглэж ирсэн echo=True-ийг асааж, loan.member гэх мэт гэм зэмгүй мэт харагдах Python мөр бүр сервер рүү ямар SQL илгээж байгааг харна. Тэнд бид гайхмаар нэг зүйлийг олно: зарим мөр нэг ч query илгээдэггүй, зарим мөр бидний бичээгүй хэд хэдэн query илгээдэг. Энэ ажиглалт дараагийн хичээлийн ORM-ийн хамгийн алдартай асуудлын үр болно.
Бүлэг 5-д FOREIGN KEY-ийг "өөр дэвтрийн бичилт рүү заасан заалт" гэж зүйрлэсэн: зээлийн дэвтэрт "Гишүүн: №1" гэж бичигдсэн, Батын тухай мэдэхийг хүсвэл гишүүдийн дэвтрийг нээж №1-ийг олох хэрэгтэй. Бүлэг 7-ийн JOIN нь хоёр дэвтрийг зэрэгцүүлэн тавьж, бүх заалтыг нэг дор дагаж уншдаг байсан.
ORM-ийн Relationship бол туслах ажилтантай ажиллахтай адил. Та зээлийн хуудсыг барьж байгаад "энэ хэний зээл вэ?" гэж асуухад туслах гишүүдийн дэвтэр рүү гүйж очоод Батын хуудсыг авчирч гарт тань өгнө. Та дэвтэр нээх, дугаар хайхыг огт харахгүй — зүгээр л асуухад хариу гарт ирнэ. Тухтай. Гэвч туслах асуулт бүрт тусдаа гүйдэг. Хэрэв та зуун зээлийн хуудсыг нэг нэгээр барьж "энэ хэнийх вэ" гэж асуувал туслах зуун удаа гүйнэ — та нэг ч удаа дэвтэр нээгээгүй ч. Энэ хичээлд туслахын гүйлт бүрийг echo гаралтаас харна.
Бүлэг 11-ийн pg-practice хавтсанд, DATABASE_URL нь library өгөгдлийн сан руу заасан PowerShell цонхонд ажиллана. Бүлэг 10-т бүтээсэн library бүх хүснэгттэйгээ бэлэн. library_models.py:
python
from datetime import date
from sqlmodel import Field, Relationship, SQLModel
class Member(SQLModel, table=True):
__tablename__ = "members"
id: int | None = Field(default=None, primary_key=True)
name: str
email: str | None = None
city: str | None = None
loans: list["Loan"] = Relationship(back_populates="member")
class Book(SQLModel, table=True):
__tablename__ = "books"
id: int | None = Field(default=None, primary_key=True)
title: str
year: int | None = None
copies: int = 1
loans: list["Loan"] = Relationship(back_populates="book")
class Loan(SQLModel, table=True):
__tablename__ = "loans"
id: int | None = Field(default=None, primary_key=True)
book_id: int = Field(foreign_key="books.id")
member_id: int = Field(foreign_key="members.id")
loaned_on: date
due_on: date
returned_on: date | None = None
book: Book = Relationship(back_populates="loans")
member: Member = Relationship(back_populates="loans")Энэ кодыг хэсэг бүрээр нь Бүлэг 5-ын ойлголтуудтай тааруулъя.
Энгийн талбарууд. id, name, title, loaned_on гэх мэт — эдгээр нь хүснэгтийн баганууд, FastAPI сургалтаас танил. __tablename__ нь хүснэгтийн нэрийг ил тод заадаг, учир нь бидний хүснэгтүүд олон тоогоор (members, books) нэрлэгдсэн бол SQLModel анхдагчаар ангийн нэрийг жижиг үсгээр (member, book) хэрэглэдэг. Бүлэг 10-ын DATE багануудыг Python-ы date төрлөөр тайлбарласан — Бүлэг 11-д psycopg тэдгээрийг хооронд нь автоматаар хөрвүүлдгийг харсан. Бид хүснэгтийн зарим баганыг (genre, isbn, joined_on гэх мэт) тайлбарлаагүй — уншихад хэрэгтэй багануудаа л тайлбарлахад хангалттай.
FOREIGN KEY талбар. book_id: int = Field(foreign_key="books.id") — Бүлэг 5-ийн FOREIGN KEY (book_id) REFERENCES books (id)-ийн Python хувилбар. "books.id" гэдэг нь "хүснэгт.багана" — ангийн нэр биш, хүснэгтийн нэр. Энэ талбар өгөгдлийн сангийн жинхэнэ багана бөгөөд тоо агуулна.
Relationship атрибутууд. book: Book = Relationship(...) ба loans: list["Loan"] = Relationship(...) — эдгээр нь шинэ. Тэд өгөгдлийн сангийн багана биш: хүснэгтэд “book” эсвэл “loans” гэсэн багана байхгүй. Тэд Python-ы талын тухтай байдал — FOREIGN KEY-ийг дагаж бүтэн объектыг авчрах заавар. Зээл талд book: Book — нэг объект, учир нь Бүлэг 5-ын нэг-олон харилцааны хичээлийн "нэг зээл нэг номтой." Ном талд loans: list["Loan"] — жагсаалт, учир нь "нэг ном олон удаа зээлэгддэг." Python-ы төрлийн тайлбар нэг-олон харилцааны хэлбэрийг шууд харуулж байна. "Loan" хашилтанд бичигдсэн нь Loan анги хараахан тодорхойлогдоогүй байхад түүн рүү заах Python-ы арга.
back_populates. Хоёр тал бие биенээ заадаг: Loan-ий book нь Book-ийн loans руу, Book-ийн loans нь Loan-ий book руу. Энэ нь SQLAlchemy-д "эдгээр хоёр атрибут нэг харилцааны хоёр тал" гэж хэлдэг. Ямар FOREIGN KEY-ээр холбогдохыг бид хэлээгүйг анзаараарай — SQLAlchemy book_id талбарын foreign_key="books.id"-ээс өөрөө олсон.
walk_relationships.py:
python
import os
from sqlmodel import Session, create_engine
from library_models import Loan
engine = create_engine(os.environ["DATABASE_URL"], echo=True)
with Session(engine) as session:
loan = session.get(Loan, 4)
print("--- зээл уншигдлаа ---")
print(loan.loaned_on, loan.member_id)
print("--- гишүүн рүү алхаж байна ---")
print(loan.member.name)
print("--- ном руу алхаж байна ---")
print(loan.book.title)Энэ код юу хийж байгааг уншъя. session.get(Loan, 4) нь 4 дугаартай зээлийг PRIMARY KEY-ээр нь уншина — FastAPI сургалтын танил хэлбэр. Дараа нь гурван зүйлийг хэвлэнэ: зээлийн өөрийн хоёр талбар, гишүүний нэр, номын нэр. Хэвлэлүүдийн хооронд тусгаарлагч мөрүүд байгаа нь echo гаралтад аль SQL аль Python мөрөөс үүссэнийг харуулах зорилготой. Ажиллуулна (SQLAlchemy-ийн эхний дотоод query-нүүдийг алгасан, гол хэсэг):
INFO sqlalchemy.engine.Engine BEGIN (implicit)
INFO sqlalchemy.engine.Engine SELECT loans.id AS loans_id, loans.book_id AS loans_book_id, loans.member_id AS loans_member_id, loans.loaned_on AS loans_loaned_on, loans.due_on AS loans_due_on, loans.returned_on AS loans_returned_on
FROM loans
WHERE loans.id = %(pk_1)s
INFO sqlalchemy.engine.Engine [generated in 0.00016s] {'pk_1': 4}
--- зээл уншигдлаа ---
2026-09-20 1
--- гишүүн рүү алхаж байна ---
INFO sqlalchemy.engine.Engine SELECT members.id AS members_id, members.name AS members_name, members.email AS members_email, members.city AS members_city
FROM members
WHERE members.id = %(pk_1)s
INFO sqlalchemy.engine.Engine [generated in 0.00014s] {'pk_1': 1}
Бат
--- ном руу алхаж байна ---
INFO sqlalchemy.engine.Engine SELECT books.id AS books_id, books.title AS books_title, books.year AS books_year, books.copies AS books_copies
FROM books
WHERE books.id = %(pk_1)s
INFO sqlalchemy.engine.Engine [generated in 0.00012s] {'pk_1': 2}
Тунгалаг Тамир
INFO sqlalchemy.engine.Engine ROLLBACKЭнэ гаралтыг мөр мөрөөр уншъя — энэ бол хичээлийн гол хэсэг. session.get(Loan, 4) нэг SELECT илгээсэн: “loans хүснэгтээс id нь өгсөн утгатай мөрийн бүх баганыг ав.” Бүлэг 11-ийн параметр (%(pk_1)s ба {'pk_1': 4}) — SQLModel утга бүрийг параметр болгодог. Баганын нэр бүрийн хажууд AS loans_id гэх мэт alias — Бүлэг 7-ийн баганын alias, SQLAlchemy нэр мөргөлдөхөөс сэргийлж нэмдэг.
Зээлийн талбаруудыг хэвлэхэд (2026-09-20 1) нэг ч query явсангүй — тэдгээр нь эхний SELECT-ээр аль хэдийн ирсэн. Харин loan.member.name гэх мөчид шинэ SELECT гарч ирсэн: “members хүснэгтээс id нь 1 мөрийг ав.” Бид энэ query-г бичээгүй — Python-д зөвхөн цэг тавьж атрибут дуудсан. SQLAlchemy loan.member руу анх хандахад гишүүн хараахан уншигдаагүйг мэдэж, member_id-ийн утгаар (1) гишүүнийг тэр даруй уншсан. loan.book.title ч мөн адил гурав дахь SELECT илгээсэн. Нийт гурван query.
Энэ зан чанарыг lazy loading — "залхуу ачаалал" гэдэг: холбоотой объект хэрэгтэй болох мөчид л уншигддаг, урьдчилан биш. Зүйрлэлийн туслах таныг асуух хүртэл гүйдэггүй. Давуу тал: хэрэв та гишүүн рүү огт хандахгүй бол түүнийг уншихад цаг зарцуулахгүй. Сул тал: та хандах бүрт тусдаа query явна. Бүлэг 7-ийн JOIN-ийг санаарай — тэр нэг query-ээр зээл, гишүүн, номыг хамт авчирдаг байсан. Энд ижил мэдээлэл гурван тусдаа query-ээр ирсэн. Ганц зээлийн хувьд энэ ялгаа хамаагүй. Олон зээлийн хувьд яах вэ — үүнийг Жишээ 2 харуулна.
Жишээ 1: нэг-олон талаас — гишүүний бүх зээл
Одоо эсрэг чиглэлд, "нэг" талаас "олон" руу алхъя. walk_relationships.py-ийн with блокийг солино:
python
with Session(engine) as session:
bat = session.get(Member, 1)
print("--- гишүүн уншигдлаа ---")
for loan in bat.loans:
print(loan.loaned_on, loan.returned_on)(import мөрийг from library_models import Loan, Member болгоно.) Echo гаралтын гол хэсэг:
SELECT members.id, members.name, ... FROM members WHERE members.id = %(pk_1)s
[...] {'pk_1': 1}
--- гишүүн уншигдлаа ---
SELECT loans.id, loans.book_id, loans.member_id, loans.loaned_on, loans.due_on, loans.returned_on
FROM loans
WHERE %(param_1)s = loans.member_id
[...] {'param_1': 1}
2026-09-03 2026-09-15
2026-09-10 2026-09-30
2026-09-20 None(alias-уудыг товчлох үүднээс хассан.) Энэ query юу гэж хэлж байна вэ гэвэл: “loans хүснэгтээс member_id нь 1 байгаа бүх мөрийг ав.” Энэ бол Бүлэг 5-ын нэг-олон харилцааны хичээлийн "Чиглэл 1" — SELECT ... FROM works WHERE author_id = 3 — яг ижил хэлбэр. bat.loans руу хандахад SQLAlchemy нэг query-ээр Батын бүх гурван зээлийг жагсаалт болгон авчирсан. for loan in bat.loans давталт дотор нэмэлт query явсангүй — жагсаалт аль хэдийн бэлэн. Нийт хоёр query.
Жишээ 2: хоёр давхар алхах — query-ний тоо өсөх
Батын зээл бүрийн номын нэрийг хэвлэе:
python
with Session(engine) as session:
bat = session.get(Member, 1)
for loan in bat.loans:
print(loan.loaned_on, loan.book.title)Echo гаралтын SQL мөрүүдийг тоолъё:
SELECT ... FROM members WHERE members.id = ... {'pk_1': 1}
SELECT ... FROM loans WHERE ... = loans.member_id {'param_1': 1}
SELECT ... FROM books WHERE books.id = ... {'pk_1': 4}
2026-09-03 Clean Code
SELECT ... FROM books WHERE books.id = ... {'pk_1': 5}
2026-09-10 Python Crash Course
SELECT ... FROM books WHERE books.id = ... {'pk_1': 2}
2026-09-20 Тунгалаг ТамирТав query. Гишүүнд нэг, зээлүүдэд нэг, дараа нь давталтын алхам бүрт loan.book руу хандах үед тусдаа нэг — гурван зээл, гурван номын query. Python кодонд энэ бол гурван мөрийн энгийн давталт; сервер дээр бол таван тусдаа хүсэлт. Бүлэг 7-ийн JOIN хичээлийн гурван хүснэгтийн query нэг хүсэлтээр ижил мэдээллийг авчирдаг байсан.
Энэ ажиглалтыг тэмдэглээд үлдээе: давталт дотор relationship руу хандах бүр нэмэлт query болж магадгүй. Гурван зээлд энэ нь мэдэгдэхгүй. Харин Батын оронд бүх гишүүнийг, тус бүрийн зээлүүдийг, зээл бүрийн номыг харуулдаг хуудас бол яах вэ? Дараагийн хичээл бүхэлдээ энэ асуултад зориулагдсан — түүний нэр N+1 асуудал.
Жишээ 3: identity map — ижил объект хоёр удаа уншигдахгүй
Жишээ 2-ын гаралтыг харсан хүн нэг асуулт асууж магадгүй: Clean Code-ийг гурван өөр гишүүн зээлсэн — хэрэв бүх зээлийг давтвал Clean Code гурван удаа уншигдах уу? Туршъя:
python
with Session(engine) as session:
first = session.get(Book, 4)
second = session.get(Book, 4)
print(first is second)(import-д Book нэмнэ.) Echo гаралтад books-ийн SELECT нэг л удаа гарч, үр дүн нь True. Энэ код юу хийж байна вэ гэвэл: “4-р номыг хоёр удаа уншиж, хоёр хувьсагч ижил объект руу заах эсэхийг шалга.” Session нь өөрийн дотор "одоогоор уншсан объектуудын бүртгэл" хадгалдаг — үүнийг identity map гэдэг. Хоёр дахь session.get(Book, 4) бүртгэлээс 4-р номыг олж, сервер рүү асуулгүйгээр ижил объектыг буцаасан. Бүлэг 4-ийн PRIMARY KEY хичээлийн "мөр бүр нэг хэн болох" санаа Python-д: нэг Session дотор нэг мөр — нэг объект. Энэ нь Жишээ 2-ын query-ний тоог бага зэрэг багасгаж болох ч асуудлыг бүрэн шийддэггүй: өөр өөр 100 ном 100 query хэвээр.
Жишээ 4: олон-олон — link_model-ээр зохиогчид
Бүлэг 5-ын олон-олон харилцааны холбогч хүснэгт book_authors-ийг SQLModel-д илэрхийлье. library_models.py-д нэмнэ (Book ангийн өмнө):
python
class BookAuthor(SQLModel, table=True):
__tablename__ = "book_authors"
book_id: int = Field(foreign_key="books.id", primary_key=True)
author_id: int = Field(foreign_key="authors.id", primary_key=True)
class Author(SQLModel, table=True):
__tablename__ = "authors"
id: int | None = Field(default=None, primary_key=True)
name: str
books: list["Book"] = Relationship(back_populates="authors", link_model=BookAuthor)Мөн Book ангид нэг мөр нэмнэ:
python
authors: list["Author"] = Relationship(back_populates="books", link_model=BookAuthor)BookAuthor нь Бүлэг 5-ын холбогч хүснэгтийн Python хувилбар: хоёр FOREIGN KEY, хоёулаа primary_key=True — нийлмэл PRIMARY KEY. link_model=BookAuthor нь Relationship-д "энэ олон-олон харилцаа энэ холбогч хүснэгтээр дамждаг" гэж хэлж байна. Одоо:
python
with Session(engine) as session:
book = session.get(Book, 6)
print(book.title, [author.name for author in book.authors])Echo гаралтын хоёр дахь query:
SELECT authors.id, authors.name
FROM authors, book_authors
WHERE %(param_1)s = book_authors.book_id AND authors.id = book_authors.author_id
[...] {'param_1': 6}
The Pragmatic Programmer ['Andrew Hunt', 'David Thomas']Энэ query юу гэж хэлж байна вэ гэвэл: “authors ба book_authors хүснэгтүүдээс book_id нь 6 бөгөөд зохиогчийн id нь холбоосын author_id-тай таарах мөрүүдийн зохиогчийн id, нэрийг ав.” Бүлэг 7-ийн JOIN хичээлийн "хуучин бичлэг" — FROM-д таслалаар хоёр хүснэгт, WHERE-д тааруулах дүрэм — SQLAlchemy өөрийн хүслээр ингэж бичжээ. Бүлэг 7-ийн сүүлийн хичээлийн Жишээ 3-т бид ижил асуултыг INNER JOIN-оор бичсэн. Холбогч хүснэгт Python кодонд огт харагдахгүй — book.authors шууд зохиогчдын жагсаалт өгнө — гэвч echo гаралт гүүр ар талд ажиллаж байгааг харуулна.
Жишээ 5: relationship-ээр өгөгдөл үүсгэх
Relationship уншихад төдийгүй бичихэд ч тустай. Ану анхны номоо — Clean Architecture — зээллээ:
python
from datetime import date
with Session(engine) as session:
anu = session.get(Member, 5)
book = session.get(Book, 7)
new_loan = Loan(member=anu, book=book, loaned_on=date(2026, 10, 10), due_on=date(2026, 10, 24))
session.add(new_loan)
session.commit()
print("Шинэ зээл:", new_loan.id, new_loan.member_id, new_loan.book_id)Echo гаралтын INSERT:
INSERT INTO loans (book_id, member_id, loaned_on, due_on, returned_on) VALUES (%(book_id)s, %(member_id)s, %(loaned_on)s, %(due_on)s, %(returned_on)s) RETURNING loans.id
[...] {'book_id': 7, 'member_id': 5, 'loaned_on': datetime.date(2026, 10, 10), 'due_on': datetime.date(2026, 10, 24), 'returned_on': None}
COMMIT
Шинэ зээл: 9 5 7Бид Loan(...)-д member_id=5 эсвэл book_id=7 гэж бичээгүй — бүтэн объектуудыг (member=anu, book=book) өгсөн. SQLAlchemy INSERT бүтээхдээ тэдгээрийн id-г FOREIGN KEY баганууд руу автоматаар хуулсан. INSERT-ийн төгсгөлийн RETURNING loans.id нь Бүлэг 11-ийн хичээлийн арга — шинэ id-г буцааж авч new_loan.id-д оноосон. Параметрүүд бүгд dict-ээр — SQL injection-оос хамгаалагдсан. Мөн back_populates-ийн ачаар anu.loans жагсаалт санах ойд шинэ зээлийг аль хэдийн агуулж байна.
Туршилтын зээлийг устгая — library өгөгдлийг цэвэр байлгах:
python
with Session(engine) as session:
loan = session.get(Loan, 9)
session.delete(loan)
session.commit()(Таны шинэ зээлийн дугаар 9-өөс өөр байвал түүнийг ашигла.)
Нэг чухал ялгааг тодорхой болгоё. Relationship нь Python-ы тухтай байдал, өгөгдлийн сангийн хууль биш. FOREIGN KEY-ийн жинхэнэ хамгаалалт — өнчин мөр хориглох, ON DELETE — Бүлэг 10-ын library_pg.sql-ээр PostgreSQL-д аль хэдийн тавигдсан. Field(foreign_key="books.id") нь SQLAlchemy-д "эдгээр хүснэгт хэрхэн холбогддог" гэж л хэлж байна; энэ кодоор бид хүснэгт үүсгээгүй тул энэ мөр өгөгдлийн санд юу ч өөрчлөөгүй. Хэрэв та хүснэгтүүдийг migration-оор үүсгэж байгаа бол FOREIGN KEY нь migration-д бичигдэх ёстой — Бүлэг 13-ын дүрэм: бүтцийг migration удирддаг. Загвар ба migration хоёулаа нэг харилцааг өөр өөр зориулалтаар тайлбарладаг.
Хэрэв сонирхож байвал book.loans-ийг ашиглаж Clean Code-ийг хэн хэн зээлснийг хэвлэж, echo гаралтаас хэдэн query явсныг тоолоорой — ажиллуулахаасаа өмнө таамаглаарай. Мөн Author-оос author.books руу, тэндээс ном бүрийн book.loans руу хоёр давхар алхаж, Robert C. Martin-ий номнууд нийт хэдэн удаа зээлэгдсэнийг тоолж, query-ний тоог Бүлэг 8-ын Тайлан 1-ийн нэг query-тэй харьцуулаарай. Эдгээр нь зөвхөн санал бөгөөд дараагийн хичээлүүд тэдгээрийг хийсэн гэж тооцохгүй.
Session хаагдсаны дараа relationship руу хандах. Объектыг with Session(...) блок дотор уншаад, блокийн гадна loan.member-д хандвал:
sqlalchemy.orm.exc.DetachedInstanceError: Parent instance <Loan at 0x...> is not bound to a Session; lazy load operation of attribute 'member' cannot proceedЭнэ мессеж юу гэж хэлж байна вэ гэвэл: "эцэг Loan объект Session-тэй холбоогүй; member атрибутын залхуу ачаалал үргэлжлэх боломжгүй." Lazy loading query илгээх ёстой, гэвч Session хаагдсан тул холболт байхгүй — Бүлэг 11-ийн "хаагдсан холболт" асуудлын ORM хувилбар. Холбоотой өгөгдлийг Session дотор уншиж дуусга. FastAPI-д энэ алдаа endpoint-ийн хариуг Session-ий гадна бүтээх үед түгээмэл гардаг.
foreign_key-д ангийн нэр бичих. Field(foreign_key="member.id") гэж хүснэгтийн нэрийн (members) оронд ангийн нэрийг жижиг үсгээр бичвэл sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'loans.member_id' could not find table 'member' with which to generate a foreign key to target column 'id' гарна. Энэ мессеж юу гэж хэлж байна вэ гэвэл: “loans.member_id баганын гадаад түлхүүр ‘member’ хүснэгтийг олсонгүй.” foreign_key-д үргэлж хүснэгт.багана бичнэ.
back_populates-ийн нэр зөрөх. Loan-д back_populates="loan" гэж нэг тоогоор бичвэл (Member-д атрибут нь loans) SQLAlchemy загваруудыг уншихдаа Member-д loan гэсэн атрибут олохгүй гэсэн утгатай алдаа өгнө. Хоёр тал бие биенийхээ атрибутын яг нэрийг заах ёстой.
Relationship-ийг багана гэж бодох. select(Loan).where(Loan.member == "Бат") гэх мэт relationship-ээр текст харьцуулах нь ажиллахгүй — relationship бол объект, багана биш. Нэрээр шүүх бол Бүлэг 7-ийн JOIN хэрэгтэй: select(Loan).join(Member).where(Member.name == "Бат"). SQLModel-ийн join() нь FOREIGN KEY-ээс ON нөхцлийг өөрөө олдог.
Давталт дотор relationship руу хандахдаа query-ний тоог мартах. Жишээ 2-т харсанчлан алдаа гарахгүй, үр дүн зөв — гэвч query-ний тоо мөрийн тоотой хамт өсдөг. Энэ бол дараагийн хичээлийн бүхэл сэдэв. Одоохондоо дадал: relationship-тэй давталт бичсэн бүрдээ echo-г асааж, query-ний тоог нэг удаа хараарай.
Энэ хичээлд та Бүлэг 5-ын FOREIGN KEY-ийг SQLModel-ийн Relationship-ээр Python-д илэрхийллээ. "Олон" талд Field(foreign_key="хүснэгт.id") жинхэнэ баганыг, Relationship(back_populates=...) нэг объектыг; "нэг" талд list["..."] төрөлтэй Relationship жагсаалтыг тайлбарладаг — төрлийн тайлбар нэг-олон харилцааны хэлбэрийг шууд харуулна. Олон-олон харилцаа link_model-оор холбогч хүснэгтээр дамждаг. Relationship атрибутууд өгөгдлийн сангийн багана биш, тухтай байдал юм. Echo гаралтаас харахад relationship руу анх хандах мөчид SQLAlchemy тусдаа SELECT илгээдэг — lazy loading; loan.member ба loan.book гурван тусдаа query болсон, давталт дотор loan.book руу хандах нь мөр бүрт нэг query нэмсэн. Identity map ижил мөрийг нэг Session-д дахин уншихгүй. Relationship-ээр объект холбож INSERT хийхэд FOREIGN KEY-ийн утгууд автоматаар бөглөгдөнө. Session хаагдсаны дараа lazy loading DetachedInstanceError өгдөг.
Дараагийн хичээлд Жишээ 2-ын ажиглалтыг бүрэн хэмжээнд нь харна. Бүх гишүүнийг, тэдний зээлүүдийг, зээл бүрийн номыг харуулдаг хуудас бичиж, echo гаралтад query-ний урсгал хэрхэн "тэсэрч" байгааг харна — N+1 асуудал. Дараа нь eager loading гэдэг аргаар SQLAlchemy-д холбоотой өгөгдлийг урьдчилан, цөөн query-ээр уншуулж, ялгааг query-ний тоо болон хугацаагаар хэмжинэ.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.