Advanced ORM | Django'ning Foydali Expression'lari haqida

Django'dagi ba'zi foydali expression'larni haqida va ularni qanday qo'llashni misollar bilan ko'rib chiqamiz.
7th March 2025
Django ORM juda qulay va kuchli vosita hisoblanadi, lekin ko'p hollarda murakkab operatsiyalarni amalga oshirishda resurslar va vaqt sarfi ortishi mumkin. Ushbu maqolada Django ORM ning ba'zi foydali expressions'laridan bazilari (OuterRef, Subquery, Aggregate, Value, ExpressionWrapper, F, Window, RowRange, ValueRange, When, Case, Coalesce, etc...) yordamida jarayonlarni qanday tezlashtirish va optimallashtirish mumkinligini ko'rib chiqamiz.
Maqolada quyidagilarni ko'rib chiqamiz:
- Oddiy hollarda expressions'lardan foydalanmasdan ishlash.
- Har bir expression'ga alohida misollar va ularning SQL query ko'rinishi.
Kirish
Expressions - bu Django ORM da ma'lumotlarni dinamik tarzda qayta ishlash va manipulyatsiya qilish imkonini beruvchi kuchli vosita (methodlar to'plami). Ular ko'p hollarda oddiy filter va annotatsiya operatsiyalariga qaraganda samaraliroq bo'ladi.
1. Oddiy Expressions:
1.1. F
F
ma'lumotlarni ustunlar bo'yicha o'zaro solishtirish yoki hisoblash uchun ishlatiladi. Ya'ni, tasavvur qiling, sizdagi A nomli modeldagi biror field'dagi mavjud qiymatini 10% oshirishingiz kerak.
Eski usulda, siz bu ishni quyidagi ko'rinishda bajararsiz:
Misol (Expressions siz):
from myapp.models import Product
products = Product.objects.all()
for product in products:
product.price = product.price * 1.1
product.save()
Misol (Expressions bilan):
Narxni 10% oshirish:
from django.db.models import F
from myapp.models import Product
Product.objects.update(price=F('price') * 1.1)
Expression'dagi SQL Query:
UPDATE myapp_product SET price = price * 1.1;
1.2. Cast
Django’da Cast
— bu ma’lumotlar bazasidagi maydon qiymatlarini bir turdan boshqa turga o‘zgartirish uchun ishlatiladigan foydali Expression
. U django.db.models.functions
modulida mavjud bo‘lib, so‘rovlar yoki annotatsiyalar jarayonida ma’lumotlar turini dinamik tarzda o‘zgartirish imkonini beradi. Bu, ayniqsa, ma’lumotlar bazasida saqlangan qiymatlarni Python’da boshqa turda ishlatish kerak bo‘lganda qo‘l keladi.
Cast
nima uchun kerak?
Ma’lumotlar bazasida maydonlar ma’lum bir turda saqlanadi (masalan, CharField
matn sifatida, IntegerField
butun son sifatida). Ammo ba’zida bu qiymatlarni boshqa turda olish yoki ulardan foydalanish zarur bo‘ladi. Masalan, matn sifatida saqlangan raqamni butun songa aylantirish yoki aksincha. Cast
aynan shu muammoni ma’lumotlar bazasi darajasida hal qiladi va kodni optimallashtiradi.
Misol (Cast
siz):
Agar sizda price
nomli maydon CharField
sifatida saqlangan bo‘lsa va uni son sifatida qo‘shishni xohlasangiz, avval uni Python’da o‘zgartirish kerak bo‘lardi:
from myapp.models import Product
products = Product.objects.all()
total = 0
for product in products:
total += int(product.price) # Har bir qiymatni alohida o‘zgartirish
print(total)
Bu yondashuv ko‘p ma’lumotlar bilan ishlaganda samarasiz, chunki har bir qator uchun qo‘shimcha hisoblash talab qilinadi.
Misol (Cast
bilan):
Cast
yordamida bu jarayonni ma’lumotlar bazasi darajasida optimallashtirish mumkin:
from django.db.models import CharField, IntegerField
from django.db.models.functions import Cast
from myapp.models import Product
products = Product.objects.annotate(
price_as_int=Cast('price', IntegerField())
)
total = products.aggregate(total_sum=models.Sum('price_as_int'))['total_sum']
print(total)
Bu yerda price
matnli maydon IntegerField
ga o‘zgartiriladi va keyin aggregate
yordamida yig‘indi hisoblanadi.
SQL Query (Orqa fonda):
Yuqoridagi kodning SQL ekvivalenti quyidagicha bo‘ladi:
SELECT SUM(CAST(price AS INTEGER)) AS total_sum
FROM myapp_product;
Yana bir misol (Float ga o‘zgartirish):
Agar price
maydonini o‘nlik son (FloatField
) sifatida ishlatmoqchi bo‘lsangiz:
from django.db.models import FloatField
from django.db.models.functions import Cast
products = Product.objects.annotate(
price_as_float=Cast('price', FloatField())
)
for product in products:
print(product.price_as_float * 1.2) # Masalan, 20% qo‘shish
Qo‘llab-quvvatlanadigan turlar:
Cast
quyidagi Django model maydon turlariga o‘zgartirishni qo‘llab-quvvatlaydi:
IntegerField
FloatField
CharField
BooleanField
DateField
va boshqalar.
Afzalliklari:
- Optimallashtirish: Ma’lumotlar bazasi darajasida tur o‘zgartirish Python’dagi qo‘lda hisoblashdan ko‘ra tezroq.
- Moslashuvchanlik: Turli ma’lumot turlarini bir xil so‘rovda ishlatish imkoniyati.
Kamchiliklari:
- Xatolik ehtimoli: Agar maydon qiymati o‘zgartiriladigan turga mos kelmasa (masalan, “abc” ni butun songa o‘zgartirish), xatolik yuzaga kelishi mumkin.
- Ma’lumotlar bazasiga bog‘liqlik: Ba’zi ma’lumotlar bazalari (masalan, SQLite) tur o‘zgartirishda cheklovlarga ega bo‘lishi mumkin.
1.3. Coalesce
va Uning Qo‘llanilishi
Django’da Coalesce
— bu ma’lumotlar bazasida bir nechta qiymatlar orasidan birinchi NULL
bo‘lmagan qiymatni qaytarish uchun ishlatiladigan foydali funksiya. U django.db.models.functions
modulida joylashgan bo‘lib, so‘rovlar yoki annotatsiyalar jarayonida NULL
qiymatlarni boshqarishda juda qulaydir. Bu funksiya SQL’dagi COALESCE
operatori bilan bir xil ishlaydi va bir nechta argument qabul qiladi.
Coalesce
nima uchun kerak?
Ma’lumotlar bazasida ba’zi maydonlar NULL
qiymatga ega bo‘lishi mumkin. Agar siz ushbu maydonlardan foydalanayotganda standart qiymat qo‘ymoqchi bo‘lsangiz yoki bir nechta maydon orasidan birinchi mavjud qiymatni tanlamoqchi bo‘lsangiz, Coalesce
bu jarayonni soddalashtiradi. U birinchi NULL
bo‘lmagan qiymatni qaytaradi, agar barcha qiymatlar NULL
bo‘lsa, oxirgi standart qiymatni qaytarish uchun ishlatilishi mumkin.
Misol (Coalesce
siz):
Agar sizda mahsulotning narxi (price
) yoki chegirmali narxi (discounted_price
) bo‘lsa va birinchi mavjud qiymatni olishni xohlasangiz, odatda Python’da shartli tekshiruv qilish kerak bo‘lardi:
from myapp.models import Product
products = Product.objects.all()
for product in products:
final_price = product.price if product.price is not None else product.discounted_price
print(final_price)
Bu yondashuv har bir qator uchun qo‘shimcha tekshirish talab qiladi va ko‘p ma’lumotlar bilan samarasiz bo‘lishi mumkin.
Misol (Coalesce
bilan):
Coalesce
yordamida bu jarayonni ma’lumotlar bazasi darajasida hal qilish mumkin:
from django.db.models.functions import Coalesce
from myapp.models import Product
products = Product.objects.annotate(
final_price=Coalesce('price', 'discounted_price', 0)
)
for product in products:
print(product.final_price)
Bu yerda final_price
birinchi navbatda price
ni, agar u NULL
bo‘lsa discounted_price
ni, agar ikkalasi ham NULL
bo‘lsa 0 ni qaytaradi.
SQL Query (Orqa fonda):
Yuqoridagi kodning SQL ekvivalenti quyidagicha bo‘ladi:
SELECT COALESCE(price, discounted_price, 0) AS final_price
FROM myapp_product;
Yana bir misol (Matn bilan):
Agar sizda foydalanuvchining ismi (first_name
) yoki taxallusi (nickname
) bo‘lsa va birinchi mavjud qiymatni olishni xohlasangiz:
from django.db.models.functions import Coalesce
from myapp.models import User
users = User.objects.annotate(
display_name=Coalesce('first_name', 'nickname', 'Noma’lum')
)
for user in users:
print(user.display_name)
Bu yerda display_name
birinchi navbatda first_name
ni, keyin nickname
ni, agar ikkalasi ham NULL
bo‘lsa “Noma’lum” ni qaytaradi.
Afzalliklari:
- Soddalik:
NULL
qiymatlarni boshqarishni ma’lumotlar bazasi darajasida hal qiladi. - Tezlik: Python’da qo‘lda tekshirish o‘rniga ma’lumotlar bazasi optimallashtirishidan foydalanadi.
Kamchiliklari:
- Moslashuvchanlik chegarasi: Faqat birinchi
NULL
bo‘lmagan qiymatni qaytaradi, murakkab shartli logikani qo‘llab-quvvatlamaydi. - Ma’lumotlar turiga e’tibor: Argumentlar bir xil turda bo‘lishi kerak, aks holda xatolik yuzaga kelishi mumkin.
2. O'rta Darajali Expressions:
2.1. ExpressionWrapper
ExpressionWrapper
murakkab matematik yoki mantiqiy ifodalarni model ustunlariga qo'llash imkonini beradi. Masalan, QQS hisoblash:
Oddiy Misol:
from django.db.models import ExpressionWrapper, DecimalField, F
from myapp.models import Product
products = Product.objects.annotate(
tax=ExpressionWrapper(F('price') * 0.15, output_field=DecimalField())
)
for product in products:
print(product.name, product.tax)
Bu yerda ExpressionWrapper
yordamida F('price') * 0.15
ifodasi DecimalField
turiga o'giriladi. Ushbu yondashuv orqali modeldagi qiymatlari bo'yicha avtomatik hisob-kitoblar amalga oshiriladi.
Misol (Expressions siz):
from myapp.models import Product
products = Product.objects.all()
for product in products:
product.tax = product.price * 0.1
product.save()
Misol (Expressions bilan):
10% QQS hisoblash:
from django.db.models import ExpressionWrapper, DecimalField
from myapp.models import Product
products = Product.objects.annotate(
tax=ExpressionWrapper(F('price') * 0.1, output_field=DecimalField())
)
for product in products:
print(product.name, product.tax)
SQL Query (Expressions bilan):
SELECT name, (price * 0.1) AS tax FROM myapp_product;
2.2. Case
va When
Shartli qiymatlarni generatsiya qilish uchun ishlatiladi.
Case
va When
kombinatsiyasi murakkab shartlarni amalga oshirish uchun juda mos keladi. Masalan, bir nechta ustunlarni solishtirish yoki shartlarga asoslangan turli hisoblashlarni bajarish uchun quyidagi kabi yondashuvdan foydalanishingiz mumkin:
Misol:
Narxni bir nechta shartlarga qarab hisoblash:
from django.db.models import Case, When, F, DecimalField
from myapp.models import Product
products = Product.objects.annotate(
adjusted_price=Case(
When(category='electronics', price__gt=500, then=F('price') * 0.85),
When(category='clothing', price__lte=500, then=F('price') * 0.75),
When(category='furniture', then=F('price') * 0.90),
default=F('price'),
output_field=DecimalField()
)
)
for product in products:
print(product.name, product.adjusted_price)
SQL Query (Expressions bilan):
SELECT name,
CASE
WHEN category = 'electronics' AND price > 500 THEN price * 0.85
WHEN category = 'clothing' AND price <= 500 THEN price * 0.75
WHEN category = 'furniture' THEN price * 0.90
ELSE price
END AS adjusted_price
FROM myapp_product;
Bu yondashuv orqali ko'p turli shartlarni bitta SQL so'rovda amalga oshirish mumkin, bu esa dastur samaradorligini oshiradi.
Misol (Expressions siz):
from myapp.models import Product
products = Product.objects.all()
for product in products:
if product.category == 'electronics':
product.discount = product.price * 0.9
elif product.category == 'clothing':
product.discount = product.price * 0.8
else:
product.discount = product.price
product.save()
Misol (Expressions bilan):
Narxni toifaga qarab belgilash:
from django.db.models import Case, When
from myapp.models import Product
products = Product.objects.annotate(
discount=Case(
When(category='electronics', then=F('price') * 0.9),
When(category='clothing', then=F('price') * 0.8),
default=F('price'),
output_field=DecimalField()
)
)
3. Murakkab Expressions:
3.1. Subquery
va OuterRef
Subquery
va OuterRef
katta ma'lumotlar bilan ishlashda juda foydali bo'lib, ular yordamida ma'lumotlarni optimallashtirilgan tarzda olish mumkin. Ayniqsa, bir nechta jadval bilan ishlaganda SQL so'rovlari sonini kamaytirishga yordam beradi. Ya'ni, quyidagi misolni ko'ramiz:
So'nggi buyurtma sanasini olish:
Misol (Expressions siz):
from myapp.models import Product, Order
products = Product.objects.all()
for product in products:
latest_order = Order.objects.filter(product_id=product.id).order_by('-order_date').first()
product.last_order_date = latest_order.order_date if latest_order else None
product.save()
Misol (Expressions bilan):
from django.db.models import Subquery, OuterRef
from myapp.models import Product, Order
latest_order = Order.objects.filter(product_id=OuterRef('pk')).order_by('-order_date')
products = Product.objects.annotate(
last_order_date=Subquery(latest_order.values('order_date')[:1])
)
SQL Query (Expressions bilan):
SELECT name,
(SELECT order_date
FROM myapp_order
WHERE product_id = myapp_product.id
ORDER BY order_date DESC LIMIT 1) AS last_order_date
FROM myapp_product;
3.2. ContentType
, GenericForeignKey
va GenericRelation
Django’da ContentType
, GenericForeignKey
va GenericRelation
moslashuvchan va umumiy model aloqalarini yaratish uchun ishlatiladi. Bu vositalar yordamida bir modelni turli xil boshqa modellarga bog‘lash mumkin, bu esa ma’lumotlarni dinamik tarzda boshqarish imkonini beradi. Ushbu yondashuv ayniqsa, loyihada oldindan aniq model tuzilmasini bilish qiyin bo‘lgan holatlarda foydalidir.
ContentType
nima?
ContentType
— Django’ning django.contrib.contenttypes
modulida mavjud bo‘lib, har bir modelni identifikatsiya qilish uchun ishlatiladi. U avtomatik ravishda har bir model uchun yozuv yaratadi va modelning nomi va ilova (app) nomi bilan bog‘lanadi.
GenericForeignKey
nima?
GenericForeignKey
— bu oddiy ForeignKey
dan farqli o‘laroq, ma’lum bir modelga emas, balki har qanday modelga bog‘lanish imkonini beruvchi maydon. U ikkita komponentdan iborat:
content_type
— bog‘lanadigan modelni aniqlash uchunContentType
obyekti.object_id
— bog‘lanadigan obyektning ID’si.
GenericRelation
nima?
GenericRelation
— bu teskari aloqani ta’minlaydi. Agar bir model GenericForeignKey
orqali boshqa modellarga bog‘lansa, GenericRelation
yordamida ushbu bog‘lanishlarni qaytarib olish mumkin.
Misol (Oddiy yondashuvsiz):
Agar bizda Comment
modeli bo‘lsa va u Product
yoki Order
kabi turli modellarga izoh qoldirishi kerak bo‘lsa, har bir model uchun alohida ForeignKey
yaratish kerak bo‘lardi. Bu esa kodni murakkablashtiradi va moslashuvchanlikni pasaytiradi.
Misol (GenericForeignKey
bilan):
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class Comment(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
text = models.TextField()
class Product(models.Model):
name = models.CharField(max_length=100)
class Order(models.Model):
order_date = models.DateTimeField(auto_now_add=True)
Bu yerda Comment
modeli Product
yoki Order
ga bog‘lanishi mumkin. Masalan:
product = Product.objects.create(name="Telefon")
comment = Comment.objects.create(
content_type=ContentType.objects.get_for_model(Product),
object_id=product.pk,
text="Bu juda yaxshi mahsulot!"
)
print(comment.content_object) # Telefon
Misol (GenericRelation
bilan):
Agar Product
modelida unga bog‘langan barcha izohlarni olishni istasak:
from django.contrib.contenttypes.fields import GenericRelation
class Product(models.Model):
name = models.CharField(max_length=100)
comments = GenericRelation(Comment)
Endi foydalanish:
product = Product.objects.get(name="Telefon")
comments = product.comments.all() # Ushbu mahsulotga tegishli barcha izohlar
SQL Query (Orqa fonda):
GenericForeignKey
bilan yaratilgan so‘rov quyidagicha ko‘rinadi:
SELECT *
FROM myapp_comment
WHERE content_type_id = <Product ContentType ID>
AND object_id = <Product ID>;
Afzalliklari:
- Moslashuvchanlik: Har qanday modelga bog‘lanish imkoniyati.
- Kodni qayta ishlatish: Har bir model uchun alohida aloqa yaratish zarurati yo‘q.
Kamchiliklari:
- So‘rovlar murakkablashishi: Oddiy
ForeignKey
ga qaraganda so‘rovlar optimallashtirishni talab qilishi mumkin. - Ma’lumotlar yaxlitligi:
object_id
noto‘g‘ri modelga ishora qilishi mumkin, agar ehtiyotkorlik bilan boshqarilmasa.
Xulosa
Yuqoridagi misollardan ko'rinib turibdiki, Django ORM da expressions'larni ishlatish:
- Kodni soddalashtiradi.
- SQL darajasida optimallikni oshiradi.
- Ko'p vaqt va resurslarni tejaydi.
Har bir expressionni to'g'ri ishlatish orqali siz Django ORMning to'liq qudratidan foydalanishingiz mumkin. Ushbu maqolada ko'rib chiqilgan oddiy sodda misollar orqali siz bu expression'larni qanday qo'llashni qisman o'rgandingiz. Endi esa ularni o'z loyihalaringizda qo'llab, dasturlaringizni yanada samaraliroq qilishingiz mumkin.
p.s) Postni oxirigacha yetib keldganingiz uchun rahmat! Xatoliklar va takliflar bo'lsa, izoh qoldiring. Happy coding! 😊
Foydali manbalar: