تا وقتی لازم نباشد، هیچ کوئریای اجرا نمیشود
وقتی مینویسید qs = Order.objects.filter(status="paid") هیچ SQLی اجرا نمیشود؛ فقط یک «توصیف» از کوئری ساخته میشود. میتوانید ده بار filter و order_by و exclude را زنجیر کنید و باز هم هیچ اتفاقی در پایگاه داده نمیافتد. کوئری فقط در این لحظهها اجرا میشود (ارزیابی):
- پیمایش با
forیا در قالب با{% for %} list(qs)،len(qs)،bool(qs)وif qs:- برش با گام (
qs[::2]) یا ایندکس تکی (qs[0]) repr(qs)— مثلاً وقتی در shell فقط نامش را مینویسید
کش نتیجه
بعد از اولین ارزیابی، نتیجه روی همان شیء QuerySet کش میشود. این رفتار هم کمک است هم دام:
orders = Order.objects.filter(status="paid")
for o in orders: ... # کوئری ۱
for o in orders: ... # از کش؛ بدون کوئری
Order.objects.filter(status="paid")[0] # کوئری
Order.objects.filter(status="paid")[0] # باز کوئری؛ هر بار QuerySet تازه
# خوب: وقتی فقط وجود/تعداد مهم است
if orders.exists(): ... # SELECT 1 ... LIMIT 1
total = orders.count() # SELECT COUNT(*)
# اما اگر قرار است بعداً پیمایش کنید، همان len() بهتر است
orders = list(orders)
if orders:
print(len(orders))
filter، exclude و lookupها
شکل کلی یک شرط field__lookup=value است و با __ میتوانید روی روابط هم جلو بروید:
| lookup | مثال | SQL تقریبی |
|---|---|---|
| exact / iexact | code__iexact="ksh-12" | = / ILIKE |
| contains / icontains | name__icontains="افشان" | LIKE '%…%' |
| in | status__in=["paid", "shipped"] | IN (…) |
| gt, gte, lt, lte | total__gte=50_000_000 | >= |
| range | created_at__date__range=(d1, d2) | BETWEEN |
| isnull | delivery_date__isnull=True | IS NULL |
| startswith | customer__user__mobile__startswith="0912" | LIKE '0912%' |
| date / year / month | created_at__year=2025 | استخراج بخش تاریخ |
qs = (Order.objects
.filter(customer__city="کاشان", total__gte=100_000_000)
.exclude(status="cancelled")
.order_by("-created_at"))
print(qs.query) # SQL تولیدشده برای دیباگ
جستوجوی فارسی
متن فارسی دو «ی» و دو «ک» دارد: فارسی (ی، ک) و عربی (ي، ك). اگر داده از منابع مختلف (اکسل قدیمی، کیبورد عربی) آمده باشد، icontains="کاشی" رکوردی را که با «كاشي» ذخیره شده پیدا نمیکند. راه درست: هم هنگام ذخیره و هم روی عبارت جستوجو، این حروف را یکسان کنید (فصل ۵).
نکتههایی که کمتر کسی میداند
.first()اگر ترتیبی تعریف نشده باشد خودشORDER BY pkاضافه میکند؛ روی جدول بزرگ بدون ایندکس مناسب ممکن است کند باشد.- زنجیر کردن دو
filter()روی رابطهی چندتایی با یکfilter()با دو شرط فرق دارد: اولی ممکن است دو قلم متفاوت را مطابق کند، دومی شرط هر دو را روی یک قلم میخواهد. - دادن یک QuerySet به
__in(filter(customer__in=Customer.objects.filter(...))) یک زیرکوئری SQL میسازد، نه دو کوئری جدا؛ نیازی بهvalues_listو list کردن نیست. created_at__date=...تاریخ را در منطقهی زمانی فعال (تهران) حساب میکند نه UTC؛ سفارش ساعت ۱ بامداد تهران در همان روز تهران شمرده میشود.- روی SQLite جستوجوی
iexactوicontainsفقط برای حروف ASCII غیرحساس به بزرگی است؛ برای فارسی مهم نیست، اما در متنهای لاتین نتیجه با PostgreSQL فرق میکند.