المشكلة: عمليات بطيئة داخل الطلب
بعض العمليات (إرسال بريد، معالجة صورة، استدعاء API خارجي) تأخذ وقتًا، ولا يجب أن ينتظرها المستخدم قبل ما تظهر له الصفحة. تقليديًا، الحل الوحيد بمشاريع Django كان مكتبة خارجية مثل Celery. بدءًا من Django 6، صار هناك حل مدمج بالإطار نفسه: Tasks Framework.
تعريف مهمة بـ @task
# tasks.py
from django.tasks import task
from django.core.mail import send_mail
@task
def send_welcome_email(user_email):
send_mail(
subject="أهلًا بك في The Code Fix",
message="شكرًا لتسجيلك معنا!",
from_email="no-reply@thecodefix.net",
recipient_list=[user_email],
)
جدولتها من View بلا انتظار
# views.py
from .tasks import send_welcome_email
def register(request):
# ... حفظ المستخدم ...
send_welcome_email.enqueue(user_email=user.email)
return redirect("home") # الصفحة تُعاد فورًا، لا تنتظر إرسال البريد
enqueue() لا تُنفّذ المهمة فورًا بالضرورة — تُضيفها لطابور، وتُرجع كائن
TaskResult يحمل معرّفًا فريدًا يمكن استخدامه لاحقًا لمعرفة حالة التنفيذ
ونتيجته.
اختيار الخلفية (Backend) عبر إعداد TASKS
# settings.py — وضع التطوير
TASKS = {
"default": {
"BACKEND": "django.tasks.backends.immediate.ImmediateBackend",
}
}
ImmediateBackend هي الافتراضية — تُنفِّذ المهمة فورًا ضمن نفس دورة
الطلب، بلا طابور حقيقي ولا عامل (worker) منفصل. مثالية للتطوير المحلي
والاختبار فقط، لأنها لا تحقّق الهدف الأصلي (تحرير الطلب من الانتظار).
للإنتاج الفعلي، يلزم خلفية طرف ثالث توفّر طابورًا حقيقيًا وعمّالًا
مستقلّين ينفّذون المهام خارج عملية الخادم نفسها.
فحص نتيجة مهمة لاحقًا
result = send_welcome_email.enqueue(user_email=user.email)
# لاحقًا، بمكان آخر بالكود:
result = send_welcome_email.get_result(result.id)
if result.status == "SUCCESSFUL":
print(result.return_value)
قيد مهم: القيم يجب أن تكون قابلة لتحويلها JSON
# ❌ خطأ: كائن نموذج كامل غير قابل للتسلسل مباشرة
@task
def notify(user): # لا تمرّر كائن Django كاملًا كوسيط
...
# ✅ صحيح: مرّر القيم البسيطة فقط
@task
def notify(user_id):
user = User.objects.get(pk=user_id)
...
معاملات المهمة وقيمتها المُرجَعة يجب أن تكون قابلة للتحويل لـ JSON —
أرقام، نصوص، قوائم، قواميس. كائنات نماذج Django أو datetime مباشرة غير
مدعومة، مرّر المعرّف (id) وأعد جلب الكائن داخل المهمة نفسها.
Django Tasks مقابل Celery — أيهما تختار؟
| المعيار | Tasks Framework (مدمج) | Celery |
|---|---|---|
| التثبيت | مدمج بـ Django 6+، لا حزمة إضافية للتعريف والجدولة | مكتبة منفصلة + broker خارجي (Redis/RabbitMQ) |
| مهام مجدولة دوريًا (cron) | غير مدعومة بالإطار الأساسي حاليًا | مدعومة رسميًا عبر Celery Beat |
| خلفية تنفيذ حقيقية بالإنتاج | تحتاج حزمة طرف ثالث توفّر عمّالًا | ناضجة ومختبرة على نطاق واسع بالإنتاج |
| مناسب لـ | مشاريع صغيرة/متوسطة، مهام بسيطة غير مجدولة | مشاريع تحتاج جدولة دورية أو حمل عمل ثقيل |
💡 لمشروع جديد بمهام بسيطة (إرسال بريد، إشعار) — Tasks Framework المدمج يكفي ويقلّل التبعيات. لمشروع يحتاج مهامًا دورية (تقرير يومي، تنظيف بيانات أسبوعي) أو حمل عمل ثقيل موزّع — Celery ما زال الخيار الأنضج.
جدول سريع
| العنصر | الغرض |
|---|---|
@task | تحويل دالة عادية لمهمة قابلة للجدولة |
.enqueue(**kwargs) | جدولة تنفيذ المهمة، ترجع TaskResult |
TASKS["default"]["BACKEND"] | إعداد خلفية التنفيذ (immediate بالتطوير) |
.get_result(id) | استرجاع حالة/نتيجة مهمة سابقة عبر معرّفها |
أخطاء شائعة
- الاعتماد على
ImmediateBackend(الافتراضية) بالإنتاج، ظانًّا أن المهام تُنفَّذ بالخلفية — فعليًا تُنفَّذ فورًا وبشكل متزامن ضمن نفس الطلب، فلا فائدة حقيقية بلا خلفية إنتاجية حقيقية. - تمرير كائن نموذج Django كاملًا كوسيط لمهمة بدل تمرير معرّفه (
id) — يفشل لأن كائنات النماذج غير قابلة للتحويل لـ JSON مباشرة. - توقّع جدولة مهام دورية (كل يوم الساعة 3 صباحًا) بالإطار المدمج مباشرة — هذا خارج نطاقه حاليًا، ولا يزال يحتاج Celery Beat أو حزمة جدولة منفصلة.
🎯 التالي: الخلاصة الشاملة لمسار Django وخطواتك بعده.