فصل ۱۰: کار با QuerySet و ORM جنگو — راهنمای جامع و پیشرفته

۱. ORM چیست و چرا استفاده میکنیم؟
ORM یا Object Relational Mapping یعنی این که به جای نوشتن دستورات SQL خام، ما از اشیاء پایتون برای کار با دادهها استفاده کنیم.
در Django:
- هر Model معادل یک جدول در دیتابیسه.
- هر شیء مدل معادل یک سطر (row) در جدول.
- ORM جنگو این ارتباط رو ایجاد میکنه و طوری طراحی شده که بدون نیاز به دانستن SQL بتونی دادهها رو ایجاد، خواندن، بروزرسانی و حذف (CRUD) کنی.
✅ مزایا:
- پورتابل بودن (روی SQLite، PostgreSQL، MySQL و غیره یکسان کار میکنه)
- ایمنتر بودن (جلوگیری از SQL Injection)
- راحتتر بودن و کمتر بودن خطا
۲. مدل نمونه برای کار
ما در این فصل با یک مثال فروشگاه آنلاین کار میکنیم. در models.py:
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=100, verbose_name="نام دسته")
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=200, verbose_name="نام محصول")
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="قیمت")
stock = models.PositiveIntegerField(verbose_name="موجودی")
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="products", verbose_name="دستهبندی")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="تاریخ ایجاد")
def __str__(self):
return self.name
حالا با:
python manage.py makemigrations
python manage.py migrate
دو جدول category و product ساخته میشن.
۳. ایجاد دادهها (INSERT)
با ORM میشه خیلی ساده رکورد جدید ساخت.
۳.۱. ساخت یک آبجکت و ذخیره
cat = Category(name="موبایل")
cat.save()
۳.۲. استفاده از create()
Category.objects.create(name="لپتاپ")
⚡ نکته: متد
create()هم آبجکت رو میسازه و هم بلافاصلهsave()میکنه.
۴. خواندن دادهها (SELECT)
۴.۱. همه رکوردها
categories = Category.objects.all()
خروجی → یک QuerySet که مثل لیست پایتون قابل پیمایشه.
۴.۲. یک رکورد خاص
cat = Category.objects.get(id=1)
اگر چنین رکوردی نبود:
DoesNotExistException داده میشه.
۴.۳. فیلتر کردن
mobiles = Product.objects.filter(category__name="موبایل")
cheap_products = Product.objects.filter(price__lt=2000000)
۴.۴. اولین یا آخرین رکورد
first_cat = Category.objects.first()
last_cat = Category.objects.last()
۴.۵. وجود داشتن داده
is_exist = Product.objects.filter(stock=0).exists()
۵. استفاده از Lookup ها
ORM جنگو lookupهای قدرتمندی داره که ما میتونیم در فیلترها ازشون استفاده کنیم:
| Lookup | توضیح | مثال |
|---|---|---|
exact | برابری دقیق | filter(name__exact="iPhone") |
iexact | برابری بدون حساسیت به حروف | filter(name__iexact="iphone") |
contains | شامل بودن متن | filter(name__contains="Pro") |
icontains | شامل بودن متن، بدون حساسیت به حروف | filter(name__icontains="pro") |
lt / lte | کوچکتر از / کوچکتر یا مساوی | filter(price__lt=1000000) |
gt / gte | بزرگتر از / بزرگتر مساوی | filter(price__gte=5000000) |
in | داخل لیست | filter(id__in=[1,2,3]) |
startswith | شروع شدن با | filter(name__startswith="Samsung") |
endswith | پایان یافتن با | filter(name__endswith="Ultra") |
۶. مرتبسازی (ORDER BY)
products = Product.objects.all().order_by('price') # صعودی
products = Product.objects.all().order_by('-price') # نزولی
۷. محدود کردن تعداد نتایج (LIMIT)
top3 = Product.objects.all().order_by('-price')[:3]
۸. بروزرسانی دادهها (UPDATE)
Product.objects.filter(id=1).update(price=2500000)
یا:
p = Product.objects.get(id=1)
p.stock = 20
p.save()
۹. حذف دادهها (DELETE)
Product.objects.get(id=2).delete()
یا چندتایی:
Product.objects.filter(stock=0).delete()
۱۰. استفاده از Q برای شرطهای پیچیده
from django.db.models import Q
# محصولاتی که یا قیمت کمتر از 2 میلیون یا موجودی صفر دارن
products = Product.objects.filter(Q(price__lt=2000000) | Q(stock=0))
۱۱. استفاده از F برای ارجاع به فیلدها
from django.db.models import F
# قیمت رو ۱۰٪ افزایش بده
Product.objects.update(price=F('price') * 1.1)
۱۲. توابع تجمعی (Aggregate Functions)
from django.db.models import Count, Avg, Max, Min, Sum
# آمار محصولات
Product.objects.aggregate(
total_products=Count('id'),
avg_price=Avg('price'),
max_price=Max('price')
)
۱۳. گروهبندی (annotate)
مثال: تعداد محصولات هر دسته
Category.objects.annotate(
product_count=Count('products')
)
۱۴. انتخاب فقط برخی فیلدها
# خروجی: لیست دیکشنری
Product.objects.values('name', 'price')
# خروجی: لیست Tuple
Product.objects.values_list('name', 'price')
۱۵. بهینهسازی کوئریها
۱۵.۱. استفاده از select_related
برای رابطه ForeignKey — یک JOIN میزنه:
products = Product.objects.select_related('category').all()
۱۵.۲. استفاده از prefetch_related
برای روابط Many-to-Many یا reverse ForeignKey:
categories = Category.objects.prefetch_related('products').all()
۱۶. Raw SQL وقتی ORM کافی نیست
Product.objects.raw("SELECT * FROM myapp_product WHERE price > %s", [2000000])
۱۷. نکات حرفهای و Best Practices
- همیشه از
QuerySetها استفاده کن، نه لیستهای تبدیلشده — چون QuerySet تنبل (lazy) هست و فقط وقتی لازم باشه از دیتابیس میخونه. - از
exists()برای چک کردن وجود داده استفاده کنید چون سبکتر ازcount()یاlen()هست. - از Lookups مناسب استفاده کن تا کوئری دقیقتر و سریعتر باشه.
- بهینهسازی صبح اول پروژه خیلی مهمه، مخصوصاً وقتی دادهها زیاد شدن.
۱۸. مثال عملی — صفحه محصولات فروشگاه
views.py
from django.shortcuts import render
from .models import Product
def product_list(request):
products = Product.objects.filter(stock__gt=0).order_by('price')
return render(request, 'product_list.html', {'products': products})
product_list.html
<h1>لیست محصولات</h1>
<ul>
{% for product in products %}
<li>{{ product.name }} - {{ product.price }} تومان</li>
{% empty %}
<li>هیچ محصولی موجود نیست</li>
{% endfor %}
</ul>
۱۹. خطاهای رایج
| خطا | علت | راهحل |
|---|---|---|
DoesNotExist | استفاده از get برای رکوردی که وجود نداره | استفاده از filter().first() |
MultipleObjectsReturned | get و بیش از یک نتیجه | استفاده از filter() |
| افت سرعت | کوئری اضافی یا N+1 Query | select_related / prefetch_related |
۲۰. جمعبندی
در این فصل یاد گرفتیم:
- CRUD کامل با ORM
- فیلترها و Lookups
- مرتبسازی، گروهبندی و Aggregate
- شرطهای پیچیده با Q
- بهینهسازی QuerySetها
- مثال عملی فروشگاه آنلاین