Гузариш ба мундариҷа
Мавзӯи 4 аз 6

Моделҳо ва дархостҳо

Чаро моделҳо лозиманд

Рӯйхати дар хотирабуда баъди анҷоми Python нест мешавад. Пойгоҳи додаҳо сабтҳоро байни иҷроҳо нигоҳ медорад ва худаш метавонад сатрҳоро ёбад, ҷудо ва ҳисоб кунад. Django ORM имкон медиҳад, ки ҷадвалро бо синфи Python тавсиф карда, дархостро бо занҷири методҳо созем.

Модел ҷадвал, майдон сутун ва намунаи модел як сатри ҷудоро тавсиф мекунад. Сатр калиди асосии pk дорад: Django бо нобаёнӣ барои он майдони id месозад. Менеҷери objects нуқтаи оғози дархостҳост. Он рӯйхати ҳамаи сабтҳо нест.

Пойгоҳи хурди китобҳо

Барои мисолҳо SQLite-и дар хотирабударо истифода мебарем: баъди анҷоми раванд нест мешавад. Дар масъалаҳо пойгоҳ дар файли муваққатӣ ҷойгир буда, дар ҳар иҷро аз нав сохта мешавад. Мисолҳоро бо тартиб дар як файли нав иҷро кунед.

CharField матни кӯтоҳ, IntegerField ададҳои бутун ва BooleanField қимати мантиқиро нигоҳ медоранд. default ҳангоми надодани қимат қимати пешфарз медиҳад. app_label моделро дар файли таълимӣ ба барнома мепайвандад. schema_editor дар ин ҷо ҷадвалро бевосита месозад. Дар лоиҳаи одӣ тағйироти моделҳоро бо migration сабт мекунанд: аввал makemigrations, баъд migrate.

import django
from django.conf import settings

settings.configure(
    INSTALLED_APPS=[], USE_I18N=False,
    DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
)
django.setup()

from django.db import models, connection

class Book(models.Model):
    title = models.CharField(max_length=80)
    shelf = models.CharField(max_length=20)
    pages = models.IntegerField(default=0)
    available = models.BooleanField(default=True)
    published = models.DateField()

    class Meta:
        app_label = "learn"

with connection.schema_editor() as editor:
    editor.create_model(Book)

from datetime import date
b = Book.objects.create(title="Атлас", shelf="A", pages=90, published=date(2024, 1, 2))
print(b.pk, b.available)
print(Book.objects.count())

Натиҷа:

1 True
1

Сохтан, тағйир додан ва хондани як сабт

create фавран INSERT иҷро карда, объекти сабтшударо бармегардонад. Таъиноти одии b.pages = ... танҳо объекти Python-ро дигар мекунад; save() тағйиротро ба пойгоҳ мефиристад. get маҳз як сабтро интизор аст: ҳангоми набудан Book.DoesNotExist ва ҳангоми чандто будан Book.MultipleObjectsReturned ба вуҷуд меояд.

Тағйир додани объект дар Python ва сабт кардани он қулай аст, вале барои ҳисобкунакҳои ҳамзамон одатан ифодаи F-ро дар пойгоҳ истифода мебаранд, то тағйироти дархости дигар гум нашавад. Дар ин ҷо мисоли пайдарпай кофӣ аст.

b.pages = 100
print(Book.objects.get(pk=b.pk).pages)
b.save()
print(Book.objects.get(pk=b.pk).pages)
Book.objects.create(title="Алифбо", shelf="A", pages=40, published=date(2025, 5, 6))
Book.objects.create(title="Харита", shelf="B", pages=10, available=False, published=date(2024, 8, 9))
print(Book.objects.count())

Натиҷа:

90
100
3

QuerySet, шартҳо ва тартиб

filter QuerySet, яъне тавсифи маҷмӯи сабтҳоро бармегардонад. Одатан SQL танҳо ҳангоми паймоиш, list(...) ё амали дигари ниёзманд ба натиҷа иҷро мешавад. Аз ин рӯ аввал тамоми дархостро сохта, баъд маълумотро хондан қулай аст. exclude сабтҳои мувофиқро хориҷ мекунад.

Ду зерхат майдонро аз lookup ҷудо мекунад: gte «калон ё баробар», gt қатъиян калонтар, in узвият дар маҷмӯъ ва year соли санаро мефаҳмонад. Якчанд аргументи filter бо «ВА» мепайванданд. icontains дар SQLite бо нобаёнӣ муқоисаи пурраи беэътино ба регистри кириллиро намедиҳад — имкониятҳои пойгоҳи худро ба назар гиред.

order_by тартибро муайян мекунад: минус маънои камшавиро дорад. Барои қиматҳои баробар майдони дуюм ё pk илова кунед, вагарна тартиби онҳо кафолат надорад. Буриши [:2] ба маҳдудияти SQL табдил меёбад; аввал филтр ва ҷудокунӣ, баъд маҳдудкунӣ иҷро мешаванд.

qs = Book.objects.filter(pages__gte=40).exclude(available=False).order_by("pages", "pk")
print(list(qs.values_list("title", flat=True)))
print(list(Book.objects.filter(published__year=2024).order_by("pk").values_list("pages", flat=True)))
print(list(Book.objects.order_by("-pages", "pk").values_list("pages", flat=True)[:2]))

Натиҷа:

['Алифбо', 'Атлас']
[100, 10]
[100, 40]

Ҷамъбаст ва гурӯҳҳо

aggregate барои тамоми QuerySet як луғати натиҷаҳо бармегардонад. Sum ҷамъ мекунад, Count мешуморад ва Avg миёнаро меёбад. Дар маҷмӯи холӣ Sum одатан None медиҳад; default=0 таъин кардан мумкин аст.

values("shelf") пеш аз annotate гурӯҳбандиро аз рӯйи раф муайян мекунад. annotate ба ҳар гурӯҳ қимати ҳисобшаванда меафзояд. Ин барои ҳисобот муфид аст: ҳамаи китобҳоро бор карда, луғати ҳисобкунакҳоро дастӣ сохтан лозим нест.

from django.db.models import Sum, Count

print(Book.objects.aggregate(pages_total=Sum("pages")))
print(Book.objects.filter(pages__lt=0).aggregate(pages_total=Sum("pages", default=0)))
print(list(Book.objects.values("shelf").annotate(items=Count("id")).order_by("shelf")))

Натиҷа:

{'pages_total': 150}
{'pages_total': 0}
[{'shelf': 'A', 'items': 2}, {'shelf': 'B', 'items': 1}]

ORM дар дохил чӣ гуна кор мекунад

QuerySet ба варақаи фармоиш монанд аст: даъватҳои методҳо шарт, тартиб ва маҳдудиятро пур мекунанд, вале худи китобҳоро намеоранд. Вақте натиҷа лозим мешавад, Django тавсифро ба SQL-и параметрдор табдил дода, ба SQLite мефиристад ва сатрҳои ҷавобро ба объект ё луғат табдил медиҳад. Қиматҳои филтр ҳамчун параметр дода мешаванд; SQL-ро аз сатрҳои корбар сохтан лозим нест.

Баъди паймоиши одӣ QuerySet метавонад натиҷаҳоро дар кэши худ нигоҳ дорад. QuerySet-и нав дархости нав иҷро мекунад. count() ва exists() ҳангоми танҳо лозим будани шумора ё мавҷудият муфиданд. Сохтори ҷадвал ва санҷиши маълумоти корбар вазифаҳои ҷудоанд: save() худкор full_clean()-ро даъват намекунад.

Хатоҳои маъмул ва ислоҳҳо

  1. b.pages += 1 бе b.save() танҳо хотираро дигар мекунад. Объектро сабт карда, барои санҷиш аз нав хонед.
  2. Book.objects.get(shelf="A") ҳангоми ду китоб будан MultipleObjectsReturned медиҳад. Барои чанд сатр filter лозим аст.
  3. filter(pages_gte=40) майдони мавҷуднабударо ҷуста, FieldError медиҳад. pages__gte бо ду зерхат дуруст аст.
  4. order_by("pages")[:2] китобҳои кӯтоҳтаринро интихоб мекунад. Барои дарозтаринҳо order_by("-pages", "pk")[:2] лозим аст.
  5. filter(...)[:2].order_by("pk") ба TypeError меорад: пас аз маҳдудкунӣ тартибро дигар кардан мумкин нест. order_by-ро пеш аз буриш гузоред.

Ёддошт

Амал Метод
Илова / сабт objects.create(...) / obj.save()
Як сатр / маҷмӯъ get(...) / filter(...)
Хориҷ кардани сабтҳо exclude(...)
Муқоиса / сол pages__gte=40 / published__year=2024
Тартиб / маҳдудият order_by("-pages", "pk")[:2]
Ҷамъбаст / гурӯҳҳо aggregate(...) / values(...).annotate(...)
Танҳо мавҷудият exists()