IlmHamroh
JavaScript Full-stack/27-qism. Ma'lumotlar bazasi chuqur: Postgres, ORM, Mongo, Redis22/65-dars16 daqiqa
Mundarija (28)

Prisma relations: 1:1, 1:N, N:M, self-relation va onDelete

Qisqacha: Prisma'da bog'lanish ikki tomonda yoziladi: tashqi kalit turgan modelda egasi Foydalanuvchi @relation(fields: [egasiId], references: [id]), qarshi tomonda esa ro'yxat vazifalar Vazifa[]. 1:1 — tashqi kalit @unique; N:M — yo yashirin (Prisma o'zi _AToB jadvalini yaratadi), yo aniq ko'prik model (kanondagi VazifaTegi). onDelete bazadagi referential action'ga aylanadi (Cascade, Restrict, SetNull). include relation'larni JOIN bilan emas, alohida IN (…) so'rovlar bilan yuklaydi.

Bu darsda

  • @relation(fields, references) ning ikki tomonini o'qib, qaysi model tashqi kalitni ushlashini aniqlaysiz.
  • 1:1, 1:N va N:M ni Prisma sxemasida yozasiz va Prisma ularni qanday SQL'ga aylantirishini ko'rasiz.
  • Yashirin va aniq N:M farqini bilib, kanonda nega aniq varianti tanlanganini tushuntirasiz.
  • Self-relation (kichik vazifalar) va tarkibiy tashqi kalitni o'qiy olasiz.
  • onDelete va relationMode qanday ishlashini bilasiz.

Oldin bilishingiz kerak: Prisma: modellar va atributlar, Bog'lanishlar: 1:1, 1:N, N:M, Foreign key va referential actions, N+1 muammosi.

1. Nega bu kerak?

Bog'lanishlar darsida bazada bog'lanish nima ekanini o'rgandik: tashqi kalit (foreign key) ustuni va REFERENCES. Bazaga bu yetadi. Lekin Prisma'dan "Azizni vazifalari va ularning izohlari bilan ber" deb so'rash uchun, Prisma bog'lanishlarni bilishi kerak: qaysi ustun qaysi jadvalga ishora qiladi, bog'lanish birga-bir mi yoki birga-ko'p.

Shuning uchun Prisma sxemasida bog'lanish alohida yoziladi — relation maydonlari bilan. Ular bazada ustun emas: foydalanuvchi.vazifalar degan ustun yo'q. Bu Prisma'ning o'zi uchun xarita.

O'xshatish: «Bahor» oshxonasining buyurtma daftari. Har buyurtmada mijozning raqami yozilgan (tashqi kalit). Lekin ofitsiant "Jasur akaning hamma buyurtmalari" ni topishi uchun mijoz kartasiga "buyurtmalari — daftarning shu sahifalarida" degan eslatma ham kerak. Relation maydoni — o'sha eslatma.

Diqqat: Kanon — holat 20 sxemasi va uning Prisma tajribasi, Prisma 7.10.1, PostgreSQL 18.6. Kanonda yo'q 1:1 va yashirin N:M ni biz alohida namuna sxemada sinadik («Bahor» oshxonasi: mijoz, profil, buyurtma, taom) — Prisma 7.10.1, PostgreSQL 17.6, "biz ishga tushirdik".

2. 1:N — eng ko'p uchraydigani

2.1 Ikki tomon

Bir foydalanuvchida ko'p vazifa. Kanondan (qisqartirilgan):

ts
model Foydalanuvchi {
  id        BigInt   @id @default(autoincrement())
  vazifalar Vazifa[]
  @@map("foydalanuvchilar")
}

model Vazifa {
  id      BigInt        @id @default(autoincrement())
  egasiId BigInt        @map("egasi_id")
  egasi   Foydalanuvchi @relation(fields: [egasiId],
            references: [id], onDelete: Cascade)
  @@map("vazifalar")
}

(Uzun atribut o'qish uchun ikki qatorga bo'lindi.) O'qiymiz:

  • Vazifa.egasiId — oddiy skalyar maydon, bazadagi egasi_id ustuni.
  • Vazifa.egasi — relation maydoni. @relation(fields: [egasiId], references: [id]) — "shu modeldagi egasiId Foydalanuvchi.id ga ishora qiladi". fields turgan tomon — tashqi kalitni ushlaydigan tomon.
  • Foydalanuvchi.vazifalar Vazifa[] — qarshi tomon: ro'yxat, unda @relation argumentlari yo'q.

Ikkala tomon ham majburiy: bittasini yozsangiz, prisma validate qarshi tomonni so'raydi (prisma format uni o'zi qo'shib beradi).

flowchart LR
  F["Foydalanuvchi.vazifalar: Vazifa[]"] -.->|"qarshi tomon"| V
  V["Vazifa.egasi: @relation"] -->|"egasi_id REFERENCES id"| F2[(foydalanuvchilar)]

Diagrammada: tashqi kalit faqat vazifalar jadvalida (egasi_id), Foydalanuvchi tomonidagi ro'yxat esa faqat Prisma xaritasi — bazada hech qanday ustun qo'shmaydi.

2.2 Ixtiyoriy relation

Izohning muallifi o'chirilgan bo'lishi mumkin (26-qismda ON DELETE SET NULL). Kanonda:

ts
muallifId BigInt?        @map("muallif_id")
muallif   Foydalanuvchi? @relation(fields: [muallifId],
            references: [id], onUpdate: NoAction)

Ikkalasi ham ? bilan: tashqi kalit NULL bo'lishi mumkin, demak relation ham. onDelete yozilmagan — Prisma'ning sukuti ixtiyoriy relation uchun SetNull, bazadagi bilan bir xil.

Tekshirib ko'ring: Vazifa da egasiId BigInt (majburiy), lekin egasi Foydalanuvchi? (ixtiyoriy) deb yozsangiz nima bo'ladi?

Javob

prisma validate xato beradi: relation maydonining ixtiyoriyligi fields dagi skalyar maydonlarga mos bo'lishi kerak. Skalyar majburiy bo'lsa, relation ham majburiy; skalyar ? bo'lsa — relation ham ?.

3. 1:1

1:1 — 1:N ning bitta farqli holati: tashqi kalit noyob. «Bahor» namunasida har mijozning bittagina profili bor:

ts
model Mijoz {
  id     Int     @id @default(autoincrement())
  ism    String
  profil Profil?
}

model Profil {
  id      Int    @id @default(autoincrement())
  telefon String
  mijozId Int    @unique
  mijoz   Mijoz  @relation(fields: [mijozId], references: [id],
            onDelete: Cascade)
}

mijozId @unique — shu bitta atribut 1:N ni 1:1 ga aylantiradi. Qarshi tomonda ro'yxat emas, Profil? — "profil bor yoki yo'q". Prisma yasagan SQL'dan (biz ishga tushirdik, migrate diff):

sql
CREATE UNIQUE INDEX "Profil_mijozId_key" ON "Profil"("mijozId");
ALTER TABLE "Profil" ADD CONSTRAINT "Profil_mijozId_fkey"
  FOREIGN KEY ("mijozId") REFERENCES "Mijoz"("id")
  ON DELETE CASCADE ON UPDATE CASCADE;

Noyob indeks + tashqi kalit — Bog'lanishlar darsidagi 1:1 ning aynan o'zi. E'tibor bering: @@map yozmaganimiz uchun jadval nomlari model nomi bilan — "Mijoz", "Profil" (katta harf, qo'shtirnoqda).

4. N:M — ikki yo'l

4.1 Yashirin (implicit)

Buyurtmada ko'p taom, taom ko'p buyurtmada. Prisma'da eng qisqa yozuv — ikkala tomonda ro'yxat:

ts
model Buyurtma {
  id      Int    @id @default(autoincrement())
  taomlar Taom[]
}

model Taom {
  id          Int        @id @default(autoincrement())
  nom         String
  buyurtmalar Buyurtma[]
}

Ko'prik jadvalni Prisma o'zi yaratadi (biz ishga tushirdik):

sql
CREATE TABLE "_BuyurtmaToTaom" (
    "A" INTEGER NOT NULL,
    "B" INTEGER NOT NULL,
    CONSTRAINT "_BuyurtmaToTaom_AB_pkey" PRIMARY KEY ("A","B")
);
CREATE INDEX "_BuyurtmaToTaom_B_index" ON "_BuyurtmaToTaom"("B");

Ustunlar A va B — model nomlarining alifbo tartibida (Buyurtma → A, Taom → B). Ikkala tashqi kalit ON DELETE CASCADE.

4.2 Aniq (explicit) — kanon tanlovi

Kanonda vazifa ↔ teg bog'lanishi — aniq ko'prik model:

ts
model VazifaTegi {
  vazifaId BigInt @map("vazifa_id")
  tegId    BigInt @map("teg_id")
  teg    Teg    @relation(fields: [tegId], references: [id],
           onDelete: Cascade, onUpdate: NoAction)
  vazifa Vazifa @relation(fields: [vazifaId], references: [id],
           onDelete: Cascade, onUpdate: NoAction)
  @@id([vazifaId, tegId])
  @@index([tegId], map: "vazifa_teglari_teg_id_idx")
  @@map("vazifa_teglari")
}

Vazifa va Teg modellarida esa vazifaTeglari VazifaTegi[].

Yashirin Aniq
Ko'prik jadval nomi _AToB (Prisma tanlaydi) o'zingizniki (vazifa_teglari)
Qo'shimcha ustun (vaqt, kim qo'shgan) mumkin emas mumkin
Mavjud bazaga moslash qiyin (nom va A/B qat'iy) oson (@map)
So'rov yozish qisqaroq (taomlar: { connect }) bir qadam uzunroq (vazifaTeglari: { some: { teg } })

Kanon uchun tanlov aniq edi: ko'prik jadval 26-qismdan bor, nomi vazifa_teglari, uni trigger to'ldiradi — yashirin N:M bunga mos kelmaydi. Kanon PrismaOmbori dagi teg filtri shunday ko'rinadi: vazifaTeglari: { some: { teg: { nom } } } — SQL'da EXISTS.

Tekshirib ko'ring: Ertaga "tegni qachon qo'shilgan" vaqti kerak bo'lib qoldi. Yashirin N:M da buni qanday qilasiz?

Javob

Yashirin N:M ga ustun qo'shib bo'lmaydi — Prisma _AToB jadvalini to'liq o'zi boshqaradi. Aniq ko'prik modelga o'tish kerak: BuyurtmaTaomi modeli, unda ikki tashqi kalit va qoshilganVaqt. Bu migratsiya talab qiladi — shuning uchun qo'shimcha ma'lumot bo'lishi mumkin bo'lgan joyda boshidanoq aniq modelni tanlash ma'qul.

5. Self-relation va tarkibiy kalit

5.1 Kichik vazifalar

Vazifaning kichik vazifalari bor — bog'lanish o'sha modelning o'ziga. Kanondan:

ts
otaId BigInt? @map("ota_id")
ota   Vazifa? @relation("KichikVazifalar",
        fields: [otaId, egasiId], references: [id, egasiId],
        onDelete: Cascade, onUpdate: NoAction,
        map: "vazifalar_ota_id_fkey")
kichikVazifalar Vazifa[] @relation("KichikVazifalar")

Uch yangi narsa:

  • Relation nomi ("KichikVazifalar") — bitta modelda bir xil turdagi ikki relation maydoni bor (ota va kichikVazifalar), Prisma qaysi biri qaysiga juft ekanini nom orqali biladi. Ikkala tomonda ham bir xil nom.
  • Tarkibiy tashqi kalit — fields: [otaId, egasiId], references: [id, egasiId]. 26-qismdagi qaror: ota vazifa o'sha egasiniki bo'lishi kerak; buni baza tekshiradi (FOREIGN KEY (ota_id, egasi_id) REFERENCES vazifalar (id, egasi_id)). Prisma'da bunga references tomonda noyob juftlik kerak — Vazifa dagi @@unique([id, egasiId]) aynan shu uchun.
  • map: — tashqi kalit cheklovining bazadagi nomi.

5.2 Kanon tajribasi: 3 daraja

Kanon "kichik vazifasi bor vazifalar, ularning kichiklari va yana bir daraja" ni so'radi. Natija (qisqartirilgan):

text
[{"matn":"Portfolio saytiga blog yozish #portfolio","kichikVazifalar":[{"matn":"Qoralama yozish","kichikVazifalar":[]},{"matn":"Blog uchun mavzu tanlash","kichikVazifalar":[]},{"matn":"Rasmlarni siqish","kichikVazifalar":[]}]},{"matn":"Kurs ishi: ma'lumotlar bazasi bo'limi #o'qish","kichikVazifalar":[{"matn":"Jadvallarni loyihalash","kichikVazifalar":[{"matn":"ER diagramma chizish"},{"matn":"Normalizatsiyani tekshirish"}]},{"matn":"Adabiyotlar ro'yxati","kichikVazifalar":[]}]}, …]

Har daraja — alohida SQL so'rov (kanon logida 3 ta SELECT, ikkinchi va uchinchisi WHERE egasi_id IN ($1,$2,$3) …). Prisma rekursiyani qila olmaydi: chuqurlikni include ichiga include qo'yib o'zingiz belgilaysiz. Cheksiz chuqur daraxt kerak bo'lsa — Rekursiv CTE va raw SQL.

6. include: Prisma relation'larni qanday yuklaydi

6.1 Alohida so'rovlar

Kanon "Aziz, uning ikki vazifasi va izohlari" ni so'radi:

text
Aziz Karimov [
  "JavaScript kursining 26-qismini boshlash #o'qish (3 izoh)",
  "Postgres'ni Docker'da ko'tarish #o'qish #docker (1 izoh)"
]

Bu tajriba holat 20 sxemasida ishlagan: foydalanuvchi ismi hali ism maydonida (SQL'da "foydalanuvchilar"."ism" ustuni). Zero-downtime: expand-contract darsida bu maydon toliqIsm ga aylanadi — o'shanda select dagi nom ham o'zgaradi.

Prisma yuborgan SQL'da nimaga qarash kerak: nechta SELECT bor va oxirgisida IN (qisqartirilgan):

text
SQL: SELECT "public"."foydalanuvchilar"."id", … FROM "public"."foydalanuvchilar" …
SQL: SELECT "public"."vazifalar"."id", "public"."vazifalar"."egasi_id", … FROM "public"."vazifalar" …
SQL: SELECT "public"."izohlar"."id", "public"."izohlar"."matn", "public"."izohlar"."vazifa_id" FROM "public"."izohlar" WHERE "public"."izohlar"."vazifa_id" IN ($1,$2) OFFSET $3

Uch daraja — uch so'rov, JOIN yo'q. Muhimi — har daraja bitta so'rov: ikkala vazifaning izohlari IN ($1,$2) bilan birga olinadi. Bu N+1 emas, "1+1+1": vazifa 2 ta bo'ladimi, 200 ta — izohlar uchun baribir bitta so'rov. Natijani Prisma JS'da yig'adi.

Biz ham namunada sanadik: mijoz + profil + buyurtmalar + taomlar (N:M) — include 4 ta so'rov yubordi (biz ishga tushirdik):

text
Jasur aka | +998 90 000 00 00
[{"nom":"Osh"},{"nom":"Ko'k choy"}]
include so'rovlari: 4

6.2 relationLoadStrategy — flagsiz ishlamaydi

Prisma 7 hujjatida relationLoadStrategy: "join" (bitta SQL'da LATERAL JOIN bilan yuklash) bor, lekin "Preview" deb belgilangan. Kanon uni flagsiz sinadi:

text
Unknown argument `relationLoadStrategy`. Available options are marked with ?.

"Noma'lum argument". Prisma 7.10.1 da bu imkoniyat generator'dagi previewFeatures = ["relationJoins"] bilan yoqiladi. Preview imkoniyat — hali sinovdagi, keyingi versiyada o'zgarishi mumkin bo'lgan qism. Kanon flagni yoqmadi: loyiha barqaror API bilan qoladi. Xulosa: hujjatdagi har imkoniyat sizning versiyangizda va sozlamangizda borligini tekshiring.

Diqqat: 2026-10 holatida prisma.io/docs sukut bo'yicha Prisma 8 (release candidate) hujjatini ochadi — u yerda include boshqacha ishlaydi. Prisma 7 hujjati /docs/orm/v7 bo'limida. Hujjatni o'qishdan oldin versiyani tekshiring.

Maslahat: include hamma ustunlarni oladi. Faqat kerakli maydonlar uchun select ichida relation yozing: select: { ism: true, vazifalar: { select: { matn: true } } }. Bu Prisma Client CRUD darsida.

7. onDelete va onUpdate

7.1 Sukutlar va bazaga ta'siri

onDelete — Prisma bazaga yozadigan referential action (Foreign key va referential actions):

Prisma SQL Ota o'chirilsa
Cascade ON DELETE CASCADE bolalar ham o'chadi
Restrict ON DELETE RESTRICT o'chirish rad etiladi
NoAction ON DELETE NO ACTION rad etiladi (tekshiruv tranzaksiya oxirida ham bo'lishi mumkin)
SetNull ON DELETE SET NULL bolada kalit NULL
SetDefault ON DELETE SET DEFAULT bolada kalit sukut qiymatiga

Prisma sukutlari (onDelete yozilmasa): majburiy relation — Restrict, ixtiyoriy — SetNull; onUpdate — Cascade. Biz namunada tekshirdik: Buyurtma.mijoz da onDelete yozmagan edik, Prisma yasagan SQL'da — ON DELETE RESTRICT ON UPDATE CASCADE.

Buyurtmasi bor mijozni o'chirib ko'rdik (biz ishga tushirdik):

text
P2003 ForeignKeyConstraintViolation

P2003 — tashqi kalit cheklovi buzildi (Postgres'da 23503). Ilovada bu "avval buyurtmalarni hal qiling" degan javobga aylanadi, 500 ga emas.

7.2 Kanondagi onUpdate: NoAction

db pull kanon sxemasida hamma joyga onUpdate: NoAction yozdi — chunki 26-qism SQL'ida ON UPDATE berilmagan, Postgres sukuti esa NO ACTION. Prisma'ning o'z sukuti (Cascade) bilan bazadagi holat farq qilgani uchun introspection aniq yozib qo'ydi. Bu to'g'ri: sxema bazani tasvirlaydi. Bizda birlamchi kalitlar identity va o'zgarmaydi, shuning uchun onUpdate amalda ishlamaydi.

8. relationMode: tashqi kalitsiz relation

Ba'zi bazalar (masalan, tashqi kalitlarni qo'llamaydigan PlanetScale/Vitess) uchun Prisma'da relationMode = "prisma" bor: relation'lar faqat Prisma Client'da "emulyatsiya" qilinadi, bazada tashqi kalit yaratilmaydi. Biz namuna sxemaga uni qo'shdik:

text
Prisma schema warning:
- With `relationMode = "prisma"`, no foreign keys are used, so relation fields will not benefit from the index usually created by the relational database under the hood. … We recommend adding an index manually.

migrate diff dagi FOREIGN KEY lar soni — 0 (sukut rejimda 4 ta edi). Ogohlantirish matnidagi "baza o'zi yaratadigan indeks" Postgres uchun aniq emas — Postgres tashqi kalit ustuniga indeksni o'zi yaratmaydi (Qaysi ustunga va qanday tartibda indeks); lekin xulosa to'g'ri: indeksni qo'lda qo'ying.

Postgres'da relationMode = "prisma" ishlatishga sabab yo'q: tashqi kalit — bazaning eng arzon va ishonchli himoyasi. Prisma'dan tashqari yozuvlar (raw SQL, boshqa servis) uni chetlab o'ta olmaydi. Kanon sukut rejimda (foreignKeys).

9. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
validate: qarshi tomon relation yo'q faqat bir tomon yozilgan prisma format yoki qo'lda Vazifa[]
Ambiguous relation bir modelda bir xil turdagi ikki relation ikkala tomonda relation nomi
tarkibiy FK uchun xato references noyob emas @@unique([id, egasiId])
P2003 o'chirishda Restrict (sukut) onDelete ni ongli tanlash yoki bolalarni avval hal qilish
Unknown argument relationLoadStrategy preview flagi yo'q previewFeatures = ["relationJoins"] yoki undan voz kechish
mavjud ko'prik jadval ishlatilmayapti yashirin N:M _AToB yaratmoqchi aniq ko'prik model + @@map

10. Mashqlar

Mashqlar kurs/mashqlar/27/22-prisma-relations/ da. 2- va 3-mashq Node 24 da tekshirilgan (biz ishga tushirdik).

1-mashq (oson): qaysi tur

  • Tashqi kalitda @unique bor —
  • Ikkala tomonda ro'yxat, ko'prik model yo'q — yashirin
  • Bir modelda ota va kichikVazifalar — -relation
  • Majburiy relation'da onDelete yozilmasa —
  • Bazada tashqi kalit yaratmaydigan rejim — relationMode = ""
Yechim
  • 1:1 — mijozId Int @unique, qarshi tomonda Profil?.
  • N:M — Prisma _BuyurtmaToTaom ni o'zi yaratadi.
  • self — relation nomi ("KichikVazifalar") bilan.
  • Restrict — ixtiyoriy relation'da SetNull.
  • prisma — Postgres'da kerak emas.

2-mashq (o'rta): include qo'lda

Prisma include ni "har daraja — bitta IN so'rov + JS'da yig'ish" bilan bajaradi. Shuni o'zingiz qiling: vazifalar va izohlar massivlari berilgan; izohlarBilan(vazifalar, izohlar) har vazifaga izohlar maydonini qo'shsin. Izohlarni avval Map ga guruhlang (har vazifa uchun izohlarni bir martada topish — filter ni sikl ichida emas).

Ishora: Map — get/set (Map); ?? bilan bo'sh massiv.

Yechim
js
function izohlarBilan(vazifalar, izohlar) {
  const guruh = new Map();
  for (const i of izohlar) {
    const r = guruh.get(i.vazifaId) ?? [];
    r.push(i.matn);
    guruh.set(i.vazifaId, r);
  }
  return vazifalar.map((v) => ({
    ...v, izohlar: guruh.get(v.id) ?? [],
  }));
}

const vazifalar = [
  { id: 1, matn: "JavaScript kursining 26-qismini boshlash" },
  { id: 2, matn: "Postgres'ni Docker'da ko'tarish" },
  { id: 9, matn: "Onamga dori olib borish" },
];
const izohlar = [
  { vazifaId: 1, matn: "Boshladim" },
  { vazifaId: 2, matn: "Volume'ni unutmang" },
  { vazifaId: 1, matn: "JOIN'lar qiyin ekan" },
];
for (const v of izohlarBilan(vazifalar, izohlar)) {
  console.log(v.id, v.izohlar.length, v.izohlar.join(" | "));
}

Konsolda:

text
1 2 Boshladim | JOIN'lar qiyin ekan
2 1 Volume'ni unutmang
9 0

Izohlar bir marta aylanib chiqildi — vazifalar soni oshsa ham ish chiziqli o'sadi. Sikl ichida izohlar.filter(…) yozilsa, bu JS'dagi "N+1" bo'lardi: har vazifa uchun butun ro'yxat qayta aylanadi.

3-mashq (qiyin): kichik vazifalar daraxti

Kanondagi kabi otaId li tekis ro'yxatdan daraxt chizing. daraxtniChiqar(vazifalar) ildiz vazifalarni (otaId === null) va ularning kichiklarini ikki bo'shliq chekinish bilan chiqarsin, istalgan chuqurlikda.

Ishora: avval otaId bo'yicha Map (2-mashqdagidek), keyin rekursiv funksiya (Rekursiya asoslari).

Yechim
js
function daraxtniChiqar(vazifalar) {
  const bolalar = new Map();
  for (const v of vazifalar) {
    const r = bolalar.get(v.otaId) ?? [];
    r.push(v);
    bolalar.set(v.otaId, r);
  }
  function chiz(otaId, chuqurlik) {
    for (const v of bolalar.get(otaId) ?? []) {
      console.log("  ".repeat(chuqurlik) + v.matn);
      chiz(v.id, chuqurlik + 1);
    }
  }
  chiz(null, 0);
}

daraxtniChiqar([
  { id: 20, otaId: null, matn: "Kurs ishi: bazalar bo'limi" },
  { id: 21, otaId: 20, matn: "Jadvallarni loyihalash" },
  { id: 22, otaId: 21, matn: "ER diagramma chizish" },
  { id: 23, otaId: 21, matn: "Normalizatsiyani tekshirish" },
  { id: 24, otaId: 20, matn: "Adabiyotlar ro'yxati" },
  { id: 17, otaId: null, matn: "Portfolio saytiga blog yozish" },
  { id: 44, otaId: 17, matn: "Qoralama yozish" },
]);

Konsolda:

text
Kurs ishi: bazalar bo'limi
  Jadvallarni loyihalash
    ER diagramma chizish
    Normalizatsiyani tekshirish
  Adabiyotlar ro'yxati
Portfolio saytiga blog yozish
  Qoralama yozish

Prisma'da shu daraxt uchun chuqurlikni oldindan bilish kerak edi (include ichida include). Bu yerda ma'lumot bitta so'rov bilan olinib (masalan findMany({ where: { egasiId } })), daraxt JS'da quriladi — chuqurlik cheklanmagan. Juda katta daraxtda esa rekursiv CTE bazaning o'zida qiladi. id lar misol uchun tanlangan.

11. Loyiha qadami: holat 20 — relation'lar

Holat 20 ning uchinchi qadami. Hali commit qilmang — commit PrismaOmbori tayyor bo'lgach.

Oldingi darsda tartiblangan modellarga relation maydonlarini kanondagidek qo'shing (ko'pini db pull allaqachon yozgan — nomlarini o'zgartirasiz):

Model Relation maydonlari
Foydalanuvchi vazifalar Vazifa[], teglar Teg[], izohlar Izoh[]
Vazifa egasi, ota / kichikVazifalar ("KichikVazifalar"), izohlar Izoh[], vazifaTeglari VazifaTegi[]
Teg egasi, vazifaTeglari VazifaTegi[]
VazifaTegi teg, vazifa; @@id([vazifaId, tegId])
Izoh muallif Foydalanuvchi?, vazifa
VazifaTarixi relation yo'q — jadvalda tashqi kalit yo'q (26-qism qarori: tarix vazifa o'chsa ham qoladi)

Tekshiring:

  • Vazifa da @@unique([id, egasiId], map: "vazifalar_id_egasi_id_key") — tarkibiy FK uchun.
  • ota relation'ida map: "vazifalar_ota_id_fkey" va onDelete: Cascade.
  • Hamma relation'larda onUpdate: NoAction (bazadagidek).

Tekshiruv: npx prisma validate, npm run db:generate, npm run tip — exit 0. Kichik skript bilan (oldingi darslardagidek) db.foydalanuvchi.findUnique({ where: { id: 1n }, include: { vazifalar: { include: { izohlar: true } } } }) ni ishga tushirib, SQL logida uchta so'rov va IN (…) ni ko'ring.

12. Real ishda

  • Relation'larni to'g'ri e'lon qilish — Prisma loyihasining asosi: include, nested write (Prisma: nested write va tranzaksiyalar) va filtrlar (some, every, none) shunga tayanadi.
  • Mavjud bazalarda ko'prik jadvallar deyarli doim aniq model bo'ladi — yashirin N:M yangi, kichik loyihalar uchun qulay.
  • Intervyuda: "Prisma'da N:M qanday yoziladi, yashirin va aniq farqi?", "include N+1 ga olib keladimi?", "Prisma'da onDelete sukuti qanday?"

Xulosa

  • Relation maydoni bazada ustun emas — Prisma xaritasi; @relation(fields, references) tashqi kalitni ushlagan tomonda.
  • 1:1 = tashqi kalit @unique; N:M — yashirin (_AToB, A/B) yoki aniq ko'prik model (qo'shimcha ustun, mavjud jadval).
  • Self-relation — relation nomi bilan; tarkibiy FK uchun references tomonda @@unique.
  • include — har daraja bitta IN (…) so'rov ("1+1+1"), JOIN emas; chuqurlikni o'zingiz belgilaysiz.
  • Sukut: majburiy — Restrict (P2003), ixtiyoriy — SetNull; Postgres'da relationMode = "prisma" kerak emas.

Keyingi dars: Prisma Client CRUD — findUnique, findMany, create, update, upsert, delete, select va include, va trigger qiymatlari bilan ishlash.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Prisma relations: 1:1, 1:N, N:M, self-relation va onDelete — IlmHamroh