IlmHamroh
Python kursi/Django10/16-dars15 daqiqa
Mundarija (22)

21.10-dars: ORM: bog'lanishlar

21-QISM — DJANGO · 10-dars


1. Kirish va motivatsiya

Real ma'lumot bog'langan: kurs bir muallifga tegishli, izoh bir kursga, kurs ko'p teglarga ega. Bu aloqalarni qanday ifodalami? Django ORM uch xil bog'lanish beradi: ForeignKey (bir-ko'p), ManyToManyField (ko'p-ko'p), OneToOneField (bir-bir). Bog'lanishlar — real dunyoning tuzilishini bazada aks ettiradi.

Bog'lanishlar — ma'lumot modelining yuragi: kam narsa mustaqil (hammasi bir-biriga bog'liq). To'g'ri bog'lanish — toza baza, oson so'rovlar (kurs.muallif.ism), izchil ma'lumot. Bu darsda bog'lanishlarni e'lon qilishni, oldinga/orqaga so'rovlarni va bog'langan filtrlarni o'rganamiz.

Real vaziyat. Bir jamoa muallif ma'lumotini har kursda takrorlab saqlardi (muallif_ism, muallif_email — har kursda) — muallif o'zgarsa, barcha kursni yangilash kerak, ba'zilari unutildi, ma'lumot nomuvofiq bo'ldi. Ular ForeignKey ishlatdi: muallif alohida jadval, kurs unga havola. Endi muallif bir joyda, kurslar unga bog'langan (kurs.muallif). O'zgarish bir joyda, ma'lumot izchil. Bog'lanishlar takrorni va nomuvofiqlikni yo'qotdi.

Bu darsda bog'lanishlarni — ForeignKey, ManyToManyField va bog'langan so'rovlarni o'rganamiz.

Bu darsda:

  • ForeignKey (bir-ko'p)
  • Oldinga va orqaga bog'lanish
  • related_name (orqaga havola)
  • ManyToManyField (ko'p-ko'p)
  • OneToOneField (bir-bir)
  • Bog'langan filtr (__ bilan join)
  • on_delete (o'chirish xatti-harakati)
  • Amaliy: to'liq bog'langan modellar

ℹ Misollarda Django standalone ORM bilan sinaladi.


2. Nazariya — chuqur tushuntirish

2.1. ForeignKey (bir-ko'p)

ForeignKey — bir-ko'p bog'lanish (bir muallif — ko'p kurs):

python
class Muallif(models.Model):
    ism = models.CharField(max_length=100)

class Kurs(models.Model):
    nom = models.CharField(max_length=100)
    muallif = models.ForeignKey(Muallif, on_delete=models.CASCADE, related_name="kurslar")
Element Ma'no
ForeignKey(Muallif) Muallifga havola
on_delete Muallif o'chirilsa nima bo'ladi
related_name Orqaga havola nomi

ForeignKey — "ko'p" tomonda (har kurs bir muallifga). Bazada muallif_id ustuni (havola). Bir muallif ko'p kursga ega bo'lishi mumkin (bir-ko'p). Bu eng keng tarqalgan bog'lanish.

2.2. Oldinga va orqaga bog'lanish

Bog'langan ma'lumotga ikki yo'nalishda kirish:

python
# oldinga (kurs → muallif):
kurs.muallif.ism             # kursning muallifi

# orqaga (muallif → kurslar):
muallif.kurslar.all()        # muallifning kurslari (related_name)

Oldinga — ForeignKey yo'nalishida (kurs.muallif). Orqaga — teskari (muallif.kurslar) — related_name bilan. Bir bog'lanish, ikki yo'nalish. Bu ma'lumotni ikki tomondan ko'rish imkonini beradi.

related_name — orqaga havola nomi:

python
muallif = models.ForeignKey(Muallif, on_delete=..., related_name="kurslar")

# orqaga:
ali.kurslar.all()            # ali muallifning barcha kurslari
ali.kurslar.filter(narx__gt=100)   # filtrlash ham

related_name — orqaga so'rov uchun nom (muallif.kurslar). Usiz Django kurs_set ishlatadi (chalkash). Aniq nom (kurslar) — o'qiladigan. Orqaga havola ham QuerySet (filtr, count qo'llanadi).

2.4. ManyToManyField (ko'p-ko'p)

ManyToManyField — ko'p-ko'p (bir kurs ko'p teg, bir teg ko'p kurs):

python
class Teg(models.Model):
    nom = models.CharField(max_length=50)
    kurslar = models.ManyToManyField(Kurs, related_name="teglar")

# qo'shish:
teg.kurslar.add(python, go)

# so'rov:
teg.kurslar.all()            # tegning kurslari
kurs.teglar.all()            # kursning teglari

ManyToManyField — har ikki tomon ko'p (kurs↔teg). Django oraliq jadval yaratadi (avtomatik). add/remove bilan bog'lash. Ikki tomondan so'rov (teg.kurslar, kurs.teglar).

2.5. OneToOneField (bir-bir)

OneToOneField — bir-bir (bir foydalanuvchi — bir profil):

python
class Profil(models.Model):
    foydalanuvchi = models.OneToOneField(Foydalanuvchi, on_delete=models.CASCADE)
    bio = models.TextField()

foydalanuvchi.profil         # bitta profil

OneToOneField — bir-bir aloqa (har foydalanuvchi bitta profil). ForeignKeyga o'xshaydi, lekin unique (bir tomon faqat bittaga). Modelni kengaytirish (masalan, Userga qo'shimcha ma'lumot) uchun ishlatiladi.

2.6. Bog'langan filtr (join)

Bog'langan maydon bo'yicha filtr — __ bilan:

python
# muallif ismi bo'yicha kurslar (join):
Kurs.objects.filter(muallif__ism="Ali")

# muallif kurslari narxi bo'yicha:
Muallif.objects.filter(kurslar__narx__gt=100)

__ bog'langan modelga o'tadi (SQL JOIN): muallif__ism — kursning muallifi ismi. Bu bog'langan ma'lumot bo'yicha qidiruv (Python'da join qilmasdan). Chuqur ham mumkin (muallif__mamlakat__nom).

2.7. on_delete (o'chirish xatti-harakati)

on_delete — bog'langan obyekt o'chirilsa nima bo'ladi:

on_delete Xatti-harakat
CASCADE Birga o'chiriladi (kurs → izohlar)
PROTECT O'chirishni taqiqlaydi
SET_NULL NULL qiladi (null=True kerak)
SET_DEFAULT Standartga o'rnatadi
python
muallif = models.ForeignKey(Muallif, on_delete=models.CASCADE)
# muallif o'chirilsa — uning kurslari ham o'chiriladi

on_delete majburiy — bog'langan obyekt o'chirilganda nima bo'lishni belgilaydi. CASCADE (birga o'chirish), PROTECT (himoya), SET_NULL (bo'shatish). To'g'ri tanlov — ma'lumot yaxlitligini saqlaydi.

2.8. Bog'lanishlar — ma'lumot yaxlitligi

Bog'lanish Aloqa Misol
ForeignKey Bir-ko'p Muallif — kurslar
ManyToManyField Ko'p-ko'p Kurs — teglar
OneToOneField Bir-bir Foydalanuvchi — profil

Bog'lanishlar takrorni yo'qotadi (muallif bir joyda, kurslar havola) va yaxlitlikni ta'minlaydi (on_delete, referential integrity). Bu 23-qism (baza dizayni) ning asosi — real dunyo aloqalarini modellash.


3. Tez ma'lumotnoma

python
from django.db import models

# ForeignKey (bir-ko'p):
class Kurs(models.Model):
    muallif = models.ForeignKey(Muallif, on_delete=models.CASCADE, related_name="kurslar")

# ManyToMany (ko'p-ko'p):
class Teg(models.Model):
    kurslar = models.ManyToManyField(Kurs, related_name="teglar")

# OneToOne (bir-bir):
class Profil(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)

# so'rovlar:
kurs.muallif.ism              # oldinga
muallif.kurslar.all()         # orqaga (related_name)
Kurs.objects.filter(muallif__ism="Ali")   # bog'langan filtr (join)
teg.kurslar.add(kurs)         # M2M qo'shish

Bog'lanishlar

ForeignKey — bir-ko'p · ManyToManyField — ko'p-ko'p · OneToOneField — bir-bir
__ — bog'langan filtr (join) · related_name — orqaga havola

4. Batafsil misollar

Misollarda Django standalone ORM bilan sinaladi.

Misol 1 — ForeignKey va oldinga/orqaga

python
"""ForeignKey (bir-ko'p); oldinga (kurs.muallif); orqaga (muallif.kurslar, related_name); bog'langan filtr (join)."""

import warnings
warnings.filterwarnings("ignore")

import sys
import tempfile
import types

import django
from django.conf import settings

settings.configure(
    DEBUG=False,
    DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
    INSTALLED_APPS=["wisar"],
    DEFAULT_AUTO_FIELD="django.db.models.BigAutoField",
)
_app = types.ModuleType("wisar")
_app.__path__ = [tempfile.gettempdir()]
sys.modules["wisar"] = _app
django.setup()

from django.db import connection, models


class Muallif(models.Model):
    ism = models.CharField(max_length=100)

    class Meta:
        app_label = "wisar"

    def __str__(self):
        return self.ism


class Kurs(models.Model):
    nom = models.CharField(max_length=100)
    narx = models.IntegerField()
    muallif = models.ForeignKey(Muallif, on_delete=models.CASCADE, related_name="kurslar")

    class Meta:
        app_label = "wisar"


def main() -> None:
    for m in (Muallif, Kurs):
        with connection.schema_editor() as se:
            se.create_model(m)

    ali = Muallif.objects.create(ism="Ali")
    vali = Muallif.objects.create(ism="Vali")
    Kurs.objects.create(nom="Python", narx=100, muallif=ali)
    Kurs.objects.create(nom="Go", narx=120, muallif=ali)
    Kurs.objects.create(nom="Rust", narx=150, muallif=vali)

    print("=== 1. Oldinga (kurs → muallif) ===")
    py = Kurs.objects.get(nom="Python")
    print(f"  Python kursi muallifi: {py.muallif.ism}")

    print("\n=== 2. Orqaga (muallif → kurslar) ===")
    print(f"  Ali kurslari: {list(ali.kurslar.values_list('nom', flat=True))}")

    print("\n=== 3. Orqaga + filtr ===")
    print(f"  Ali qimmat (>100) kurslari: {list(ali.kurslar.filter(narx__gt=100).values_list('nom', flat=True))}")

    print("\n=== 4. Bog'langan filtr (join) ===")
    print(f"  muallif__ism='Ali': {list(Kurs.objects.filter(muallif__ism='Ali').values_list('nom', flat=True))}")
    print("  ⭐ ForeignKey — oldinga (kurs.muallif), orqaga (muallif.kurslar)")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Oldinga (kurs → muallif) ===
  Python kursi muallifi: Ali

=== 2. Orqaga (muallif → kurslar) ===
  Ali kurslari: ['Python', 'Go']

=== 3. Orqaga + filtr ===
  Ali qimmat (>100) kurslari: ['Go']

=== 4. Bog'langan filtr (join) ===
  muallif__ism='Ali': ['Python', 'Go']
  ⭐ ForeignKey — oldinga (kurs.muallif), orqaga (muallif.kurslar)

Nima ko'rsatdi: 2.1, 2.2, 2.3, 2.6-bo'limlar.

Misol 2 — ManyToManyField

python
"""ManyToManyField (ko'p-ko'p); add bilan bog'lash; ikki tomondan so'rov; oraliq jadval."""

import warnings
warnings.filterwarnings("ignore")

import sys
import tempfile
import types

import django
from django.conf import settings

settings.configure(
    DEBUG=False,
    DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
    INSTALLED_APPS=["wisar"],
    DEFAULT_AUTO_FIELD="django.db.models.BigAutoField",
)
_app = types.ModuleType("wisar")
_app.__path__ = [tempfile.gettempdir()]
sys.modules["wisar"] = _app
django.setup()

from django.db import connection, models


class Kurs(models.Model):
    nom = models.CharField(max_length=100)

    class Meta:
        app_label = "wisar"


class Teg(models.Model):
    nom = models.CharField(max_length=50)
    kurslar = models.ManyToManyField(Kurs, related_name="teglar")

    class Meta:
        app_label = "wisar"


def main() -> None:
    for m in (Kurs, Teg):
        with connection.schema_editor() as se:
            se.create_model(m)

    py = Kurs.objects.create(nom="Python")
    go = Kurs.objects.create(nom="Go")
    rust = Kurs.objects.create(nom="Rust")

    backend = Teg.objects.create(nom="backend")
    tez = Teg.objects.create(nom="tez")
    backend.kurslar.add(py, go, rust)      # 3 kurs
    tez.kurslar.add(go, rust)              # 2 kurs
    py.teglar.add(tez)                     # boshqa tomondan ham

    print("=== 1. Teg → kurslar (oldinga) ===")
    print(f"  'backend' teg kurslari: {sorted(backend.kurslar.values_list('nom', flat=True))}")

    print("\n=== 2. Kurs → teglar (orqaga) ===")
    print(f"  Go teglari: {sorted(go.teglar.values_list('nom', flat=True))}")
    print(f"  Python teglari: {sorted(py.teglar.values_list('nom', flat=True))}")

    print("\n=== 3. Ko'p-ko'p aloqa ===")
    print(f"  'tez' teg kurslari: {sorted(tez.kurslar.values_list('nom', flat=True))}")

    print("\n=== 4. Bog'langan filtr (M2M join) ===")
    natija = Kurs.objects.filter(teglar__nom="tez")
    print(f"  'tez' tegli kurslar: {sorted(natija.values_list('nom', flat=True))}")
    print("  ⭐ ManyToManyField — ko'p-ko'p; add bilan bog'lash; ikki tomondan")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Teg → kurslar (oldinga) ===
  'backend' teg kurslari: ['Go', 'Python', 'Rust']

=== 2. Kurs → teglar (orqaga) ===
  Go teglari: ['backend', 'tez']
  Python teglari: ['backend', 'tez']

=== 3. Ko'p-ko'p aloqa ===
  'tez' teg kurslari: ['Go', 'Python', 'Rust']

=== 4. Bog'langan filtr (M2M join) ===
  'tez' tegli kurslar: ['Go', 'Python', 'Rust']
  ⭐ ManyToManyField — ko'p-ko'p; add bilan bog'lash; ikki tomondan

Nima ko'rsatdi: 2.4-bo'lim.

Misol 3 — OneToOneField va on_delete

python
"""OneToOneField (bir-bir); modelni kengaytirish; on_delete=CASCADE (birga o'chirish); yaxlitlik."""

import warnings
warnings.filterwarnings("ignore")

import sys
import tempfile
import types

import django
from django.conf import settings

settings.configure(
    DEBUG=False,
    DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
    INSTALLED_APPS=["wisar"],
    DEFAULT_AUTO_FIELD="django.db.models.BigAutoField",
)
_app = types.ModuleType("wisar")
_app.__path__ = [tempfile.gettempdir()]
sys.modules["wisar"] = _app
django.setup()

from django.db import connection, models


class Foydalanuvchi(models.Model):
    ism = models.CharField(max_length=100)

    class Meta:
        app_label = "wisar"


class Profil(models.Model):
    foydalanuvchi = models.OneToOneField(Foydalanuvchi, on_delete=models.CASCADE, related_name="profil")
    bio = models.TextField(default="")

    class Meta:
        app_label = "wisar"


class Izoh(models.Model):
    matn = models.CharField(max_length=200)
    muallif = models.ForeignKey(Foydalanuvchi, on_delete=models.CASCADE, related_name="izohlar")

    class Meta:
        app_label = "wisar"


def main() -> None:
    for m in (Foydalanuvchi, Profil, Izoh):
        with connection.schema_editor() as se:
            se.create_model(m)

    ali = Foydalanuvchi.objects.create(ism="Ali")
    Profil.objects.create(foydalanuvchi=ali, bio="Python dasturchi")
    Izoh.objects.create(matn="Zo'r!", muallif=ali)
    Izoh.objects.create(matn="Rahmat", muallif=ali)

    print("=== 1. OneToOne (bir-bir) ===")
    print(f"  Ali profili bio: {ali.profil.bio}")

    print("\n=== 2. Profildan foydalanuvchiga ===")
    profil = Profil.objects.first()
    print(f"  profil egasi: {profil.foydalanuvchi.ism}")

    print("\n=== 3. Foydalanuvchi izohlari (FK orqaga) ===")
    print(f"  Ali izohlari: {list(ali.izohlar.values_list('matn', flat=True))}")

    print("\n=== 4. ⚠️ on_delete=CASCADE ===")
    print(f"  o'chirishdan oldin: profil={Profil.objects.count()}, izoh={Izoh.objects.count()}")
    ali.delete()
    print(f"  Ali o'chirilgach: profil={Profil.objects.count()}, izoh={Izoh.objects.count()} (birga o'chdi)")
    print("  ⭐ OneToOne — bir-bir (kengaytirish); on_delete — yaxlitlik")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. OneToOne (bir-bir) ===
  Ali profili bio: Python dasturchi

=== 2. Profildan foydalanuvchiga ===
  profil egasi: Ali

=== 3. Foydalanuvchi izohlari (FK orqaga) ===
  Ali izohlari: ["Zo'r!", 'Rahmat']

=== 4. ⚠️ on_delete=CASCADE ===
  o'chirishdan oldin: profil=1, izoh=2
  Ali o'chirilgach: profil=0, izoh=0 (birga o'chdi)
  ⭐ OneToOne — bir-bir (kengaytirish); on_delete — yaxlitlik

Nima ko'rsatdi: 2.5, 2.7-bo'limlar.

Misol 4 — Amaliy: to'liq bog'langan modellar

Real ilova modellarini bog'lanishlar bilan quramiz: muallif, kurs (FK), teg (M2M) va bog'langan so'rovlar, agregatlar. Bu — Django ma'lumot modelining namunasi.

python
"""to'liq bog'langan modellar: muallif-kurs (FK), kurs-teg (M2M); bog'langan filtr, annotate; real model."""

import warnings
warnings.filterwarnings("ignore")

import sys
import tempfile
import types

import django
from django.conf import settings

settings.configure(
    DEBUG=False,
    DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
    INSTALLED_APPS=["wisar"],
    DEFAULT_AUTO_FIELD="django.db.models.BigAutoField",
)
_app = types.ModuleType("wisar")
_app.__path__ = [tempfile.gettempdir()]
sys.modules["wisar"] = _app
django.setup()

from django.db import connection, models
from django.db.models import Count


class Muallif(models.Model):
    ism = models.CharField(max_length=100)

    class Meta:
        app_label = "wisar"


class Kurs(models.Model):
    nom = models.CharField(max_length=100)
    narx = models.IntegerField()
    muallif = models.ForeignKey(Muallif, on_delete=models.CASCADE, related_name="kurslar")

    class Meta:
        app_label = "wisar"


class Teg(models.Model):
    nom = models.CharField(max_length=50)
    kurslar = models.ManyToManyField(Kurs, related_name="teglar")

    class Meta:
        app_label = "wisar"


def main() -> None:
    for m in (Muallif, Kurs, Teg):
        with connection.schema_editor() as se:
            se.create_model(m)

    ali = Muallif.objects.create(ism="Ali")
    vali = Muallif.objects.create(ism="Vali")
    py = Kurs.objects.create(nom="Python", narx=100, muallif=ali)
    go = Kurs.objects.create(nom="Go", narx=120, muallif=ali)
    rust = Kurs.objects.create(nom="Rust", narx=150, muallif=vali)
    backend = Teg.objects.create(nom="backend")
    backend.kurslar.add(py, go, rust)

    print("=== 1. FK: muallif kurslari ===")
    print(f"  Ali kurslari: {list(ali.kurslar.values_list('nom', flat=True))}")

    print("\n=== 2. M2M: teg kurslari ===")
    print(f"  'backend' kurslari: {sorted(backend.kurslar.values_list('nom', flat=True))}")

    print("\n=== 3. Bog'langan filtr (chuqur join) ===")
    natija = Kurs.objects.filter(muallif__ism="Ali", teglar__nom="backend")
    print(f"  Ali + backend: {sorted(natija.values_list('nom', flat=True))}")

    print("\n=== 4. Annotate (har muallif kurslari soni) ===")
    stat = Muallif.objects.annotate(soni=Count("kurslar")).order_by("ism")
    print(f"  {list(stat.values_list('ism', 'soni'))}")
    print("  ⭐ FK + M2M + bog'langan filtr + annotate — to'liq model")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. FK: muallif kurslari ===
  Ali kurslari: ['Python', 'Go']

=== 2. M2M: teg kurslari ===
  'backend' kurslari: ['Go', 'Python', 'Rust']

=== 3. Bog'langan filtr (chuqur join) ===
  Ali + backend: ['Go', 'Python']

=== 4. Annotate (har muallif kurslari soni) ===
  [('Ali', 2), ('Vali', 1)]
  ⭐ FK + M2M + bog'langan filtr + annotate — to'liq model

Nima ko'rsatdi: 2.1–2.8-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Ma'lumotni takrorlab saqlash" Bog'lanish (havola)
"ForeignKey — ikki tomonda" "Ko'p" tomonda (kurs)
"Orqaga so'rov yo'q" related_name
"Ko'p-ko'p qo'lda jadval" ManyToManyField
"Bog'langan filtr Python'da" __ (join)
"on_delete ixtiyoriy" Majburiy (FK)
"OneToOne = ForeignKey" Bir-bir (unique)
"Bog'langan ma'lumot alohida so'rov" select_related (optimizatsiya)

6. Keng tarqalgan xatolar va yechimlari

1. Ma'lumotni takrorlash (bog'lanish o'rniga)

python
class Kurs:
    muallif_ism = models.CharField(...)      # ⚠️ takror, nomuvofiq
    muallif = models.ForeignKey(Muallif, ...)  # ✅ havola

2. on_delete unutish

python
muallif = models.ForeignKey(Muallif)         # ❌ on_delete kerak
muallif = models.ForeignKey(Muallif, on_delete=models.CASCADE)   # ✅

3. related_namesiz chalkash orqaga

python
muallif = models.ForeignKey(Muallif, on_delete=...)   # muallif.kurs_set
related_name="kurslar"                        # ✅ muallif.kurslar

4. Bog'langan ma'lumotni Python'da join

python
[k for k in Kurs.objects.all() if k.muallif.ism == "Ali"]   # ⚠️
Kurs.objects.filter(muallif__ism="Ali")       # ✅ join

5. M2M'ga = bilan qo'shish

python
teg.kurslar = [py, go]                        # ❌ ishlamaydi
teg.kurslar.add(py, go)                        # ✅

6. N+1 so'rov (bog'lanishda)

python
for k in Kurs.objects.all(): print(k.muallif.ism)   # ⚠️ har kurs +1 so'rov
Kurs.objects.select_related("muallif")        # ✅ bir so'rov (23.9)

7. OneToOne o'rniga ForeignKey

python
profil = models.ForeignKey(User, ...)         # ⚠️ ko'p profil mumkin
profil = models.OneToOneField(User, ...)      # ✅ bitta

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 21.9-dars (o'tilgan): ORM so'rovlar — bog'langan filtr
  • 21.3-dars (o'tilgan): model — bog'lanish maydonlari
  • 23.2-dars: SQL JOIN — bog'lanish asosi
  • 23.9-dars: N+1 — select_related, prefetch_related
  • 23.12-dars: baza dizayni — aloqalar

8. Eng yaxshi amaliyotlar

  1. Ma'lumotni takrorlamang — bog'lanish (havola) ishlating.

  2. ForeignKeyda on_delete bering (majburiy).

  3. related_name bilan aniq orqaga havola.

  4. Ko'p-ko'p uchun ManyToManyField (qo'lda jadval emas).

  5. Bog'langan filtr uchun __ (Python'da join emas).

  6. Bir-bir uchun OneToOneField (kengaytirish).

  7. N+1 uchun select_related/prefetch_related 23.9-bob.

  8. on_deleteni to'g'ri tanlang (CASCADE/PROTECT/SET_NULL).


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # ForeignKey nima?
2.  # ForeignKey qaysi tomonda?
3.  # oldinga bog'lanish nima?
4.  # orqaga bog'lanish nima?
5.  # related_name nima?
6.  # ManyToManyField nima?
7.  # OneToOneField nima?
8.  # __ (join) nima?
9.  # on_delete nima?
10. # CASCADE nima qiladi?
11. # M2M qanday qo'shiladi?
12. # takror yoki bog'lanish?
Javoblar
  1. Bir-ko'p bog'lanish (havola)
  2. "Ko'p" tomonda (kurs → muallif)
  3. FK yo'nalishi (kurs.muallif)
  4. Teskari (muallif.kurslar)
  5. Orqaga havola nomi
  6. Ko'p-ko'p bog'lanish
  7. Bir-bir bog'lanish (unique)
  8. Bog'langan filtr (SQL JOIN)
  9. Bog'langan obyekt o'chirilganda nima bo'ladi
  10. Birga o'chiradi (bog'langanlar)
  11. .add(...) bilan
  12. Bog'lanish (takror — nomuvofiq)

Vazifa 2: Xatolarni tuzating

python
1.  muallif_ism = models.CharField(...)       # takror

2.  muallif = models.ForeignKey(Muallif)      # on_delete

3.  [k for k in ... if k.muallif.ism=="Ali"]  # join

4.  teg.kurslar = [py, go]                     # M2M

5.  profil = models.ForeignKey(User, ...)      # bir-bir
Javoblar
python
1.  muallif = models.ForeignKey(Muallif, on_delete=models.CASCADE)

2.  muallif = models.ForeignKey(Muallif, on_delete=models.CASCADE)

3.  Kurs.objects.filter(muallif__ism="Ali")

4.  teg.kurslar.add(py, go)

5.  profil = models.OneToOneField(User, on_delete=models.CASCADE)

Vazifa 3: Blog modellari

Blog bog'lanishlari:

  1. Muallif — Maqola (FK)
  2. Maqola — Teg (M2M)
  3. Maqola — Izoh (FK)
  4. Bog'langan so'rovlar test

Vazifa 4: Oldinga/orqaga

Bog'langan so'rovlar:

  1. Oldinga (maqola.muallif)
  2. Orqaga (muallif.maqolalar)
  3. Orqaga + filtr
  4. Har birini test

Vazifa 5: Ko'p-ko'p

M2M tizimi:

  1. Talaba — Kurs (M2M)
  2. add/remove
  3. Ikki tomondan so'rov
  4. Bog'langan filtr

Vazifa 6: on_delete xatti-harakatlari

on_delete variantlari:

  1. CASCADE, PROTECT, SET_NULL
  2. Har biri uchun holat
  3. O'chirishni test
  4. Yaxlitlik tahlili

Vazifa 7: O'ylash

Bog'lanishlar ma'lumotni bir marta saqlash imkonini beradi: muallif bir joyda (alohida jadval), kurslar unga havola (ForeignKey). Bu takrorni (har kursda muallif ismi) va nomuvofiqlikni (muallif o'zgarsa, bir joy unutiladi) yo'qotadi — bu ma'lumot bazasidagi "normalizatsiya" tamoyili. Ma'lumotni bir joyda saqlab, havola bilan bog'lashning afzalligi nima, va bu "takrorni yo'qotish" (DRY) tamoyili ma'lumot modelida qanday muhim?

Javob

Qisqa javob: Bog'lanishlar ma'lumotni bir marta saqlaydi: muallif alohida jadvalda, kurslar unga havola (muallif_id). Bu "normalizatsiya" — takrorlangan ma'lumotni (har kursda muallif ismi) bir joyga yig'ish. Afzalligi: izchillik (muallif bir joyda — o'zgarish bir joyda, hamma kursda aks etadi), kam joy (takror yo'q), yaxlitlik (on_delete bilan bog'langan ma'lumot boshqariladi). Takrorlangan ma'lumot (denormalizatsiya) — nomuvofiqlik xavfi (bir joy yangilanadi, boshqasi unutiladi). "Takrorni yo'qotish" (DRY) ma'lumot modelida ayniqsa muhim, chunki nomuvofiq ma'lumot — ishonchsiz tizim.

1. Normalizatsiya (bir marta saqlash)

Takror (yomon) Bog'lanish (yaxshi)
Har kursda muallif ismi Muallif alohida jadval
O'zgarish ko'p joyda O'zgarish bir joyda
Nomuvofiq xavfi Izchil

2. Afzalliklar

Afzallik Sabab
Izchillik Ma'lumot bir joyda
Kam joy Takror yo'q
Yaxlitlik on_delete, havola
Saqlash O'zgarish bir joyda

3. Nomuvofiqlik xavfi (takror)

Muallif ismi 100 kursda takrorlansa — muallif ismi o'zgarsa, 100 joyni yangilash kerak. Biror joy unutilsa — nomuvofiq (bir kursda eski ism). Bog'lanishda — bir joy (muallif jadvali).

4. "Takrorni yo'qotish" boshqa joyda

Soha DRY
Ma'lumot Normalizatsiya (bog'lanish)
Kod Funksiya (takror yo'q)
URL reverse (21.6)
Konstanta Bir joyda e'lon

5. Muhandislik saboqlari

  1. Ma'lumotni bir marta saqla (bog'lanish)
  2. Takror — nomuvofiqlik xavfi
  3. Normalizatsiya — izchil, yaxlit ma'lumot
  4. DRY — ma'lumot modelida ham (kod kabi)

6. Xulosa

  1. Bog'lanish — ma'lumot bir joyda (havola)
  2. Normalizatsiya — takrorni yo'qotish
  3. Izchillik, yaxlitlik, kam joy
  4. DRY — ma'lumot modelida muhim

Nimani mustahkamlaydi: 2.1–2.8-bo'limlar.


Xulosa

Bu darsda Django ORM bog'lanishlarini o'rgandik.

Eng muhim uch fikr:

  1. ForeignKey (bir-ko'p). ForeignKey — eng keng tarqalgan bog'lanish (bir muallif — ko'p kurs); u "ko'p" tomonda (kurs) e'lon qilinadi, bazada muallif_id havola ustuni yaratadi. Ma'lumotga ikki yo'nalishda kirish: oldinga (kurs.muallif — kursning muallifi), orqaga (muallif.kurslar — related_name bilan). Orqaga havola ham QuerySet (filtr, count qo'llanadi). Bu ma'lumotni takrorlamasdan bog'langan saqlaydi.

  2. ManyToManyField va OneToOneField. ManyToManyField — ko'p-ko'p (kurs↔teg): Django avtomatik oraliq jadval yaratadi, add/remove bilan bog'lanadi, ikki tomondan so'rov (teg.kurslar, kurs.teglar). OneToOneField — bir-bir (foydalanuvchi — profil, unique): modelni kengaytirish uchun. on_delete (ForeignKey/OneToOneda majburiy) — bog'langan obyekt o'chirilganda nima bo'lishni belgilaydi: CASCADE (birga o'chirish), PROTECT (himoya), SET_NULL (bo'shatish).

  3. Bog'langan filtr va yaxlitlik. Bog'langan maydon bo'yicha filtr __ bilan (SQL JOIN): muallif__ism="Ali" — kursning muallifi ismi bo'yicha (Python'da join qilmasdan, chuqur ham mumkin). Bog'lanishlar takrorni yo'qotadi (muallif bir joyda, kurslar havola) va yaxlitlikni ta'minlaydi — bu "normalizatsiya" tamoyili: ma'lumotni bir marta saqlab, havola bilan bog'lash. Takror — nomuvofiqlik xavfi; bog'lanish — izchil, yaxlit ma'lumot (DRY ma'lumot modelida).

Keyingi darsda autentifikatsiya tizimi ni o'rganamiz: Django'ning tayyor User modeli, kirish/chiqish, parol boshqaruvi va ruxsatlar (permissions).

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
21.10-dars: ORM: bog'lanishlar — IlmHamroh