Django Celery Expert
Instructions
Step 1: Classify the Request
Identify the task category from the request:
- Django integration — transaction safety, ORM patterns, testing, request correlation → read
references/django-integration.md
- Task design — new tasks, calling patterns, chains/groups/chords, idempotency → read
references/task-design-patterns.md
- Configuration — broker setup, result backend, worker settings, queue routing → read
references/configuration-guide.md
- Error handling — retries, backoff, dead letter queues, timeouts → read
references/error-handling.md
- Periodic tasks — Celery Beat, crontab schedules, dynamic schedules, timezone handling → read
references/periodic-tasks.md
- Monitoring — Flower, Prometheus, logging, debugging stuck tasks → read
references/monitoring-observability.md
- Production deployment — scaling, supervision, containers, health checks → read
references/production-deployment.md
If the request spans multiple categories, read all relevant reference files before continuing.
Step 2: Read the Reference File(s)
Read each reference file identified in Step 1. Do not proceed to implementation without reading the relevant reference.
Step 3: Implement
Apply the patterns from the reference file. Before presenting the solution, verify:
- Task arguments are serializable (pass IDs, not model instances)
- Tasks with retries enabled are idempotent
- Errors are logged with context
- Long-running tasks have timeouts configured
Examples
Basic Background Task
Request: "Send welcome emails in the background after user registration"
# tasks.py
from celery import shared_task
from django.core.mail import send_mail
@shared_task(bind=True, max_retries=3)
def send_welcome_email(self, user_id):
from users.models import User
try:
user = User.objects.get(id=user_id)
send_mail(
subject="Welcome!",
message=f"Hi {user.name}, welcome to our platform!",
from_email="noreply@example.com",
recipient_list=[user.email],
)
except User.DoesNotExist:
pass
except Exception as exc:
raise self.retry(exc=exc, countdown=60 * (2 ** self.request.retries))
# views.py — queue only after the transaction commits
from django.db import transaction
def register(request):
user = User.objects.create(...)
transaction.on_commit(lambda: send_welcome_email.delay(user.id))
return redirect("dashboard")
Task with Progress Tracking
Request: "Process a large CSV import with progress updates"
@shared_task(bind=True)
def import_csv(self, file_path, total_rows):
from myapp.models import Record
with open(file_path) as f:
reader = csv.DictReader(f)
for i, row in enumerate(reader):
Record.objects.create(**row)
if i % 100 == 0:
self.update_state(
state="PROGRESS",
meta={"current": i, "total": total_rows},
)
return {"status": "complete", "processed": total_rows}
# Poll progress
result = import_csv.AsyncResult(task_id)
if result.state == "PROGRESS":
progress = result.info.get("current", 0) / result.info.get("total", 1)
Workflow with Chains
Request: "Process an order: validate inventory, charge payment, then send confirmation"
from celery import chain
@shared_task
def validate_inventory(order_id):
order = Order.objects.get(id=order_id)
if not order.items_in_stock():
raise ValueError("Items out of stock")
return order_id
@shared_task
def charge_payment(order_id):
order = Order.objects.get(id=order_id)
order.charge()
return order_id
@shared_task
def send_confirmation(order_id):
Order.objects.get(id=order_id).send_confirmation_email()
def process_order(order_id):
chain(
validate_inventory.s(order_id),
charge_payment.s(),
send_confirmation.s(),
).delay()
1---2name: django-celery-expert3description: Expert Django Celery guidance for asynchronous task processing. Use when designing background tasks, configuring Celery workers, handling task retries and errors, optimizing Celery performance, implementing periodic tasks with Celery Beat, or setting up production monitoring for Celery. Do not use for general Django questions unrelated to Celery, non-Celery task systems (Django Q, Huey, RQ), ML/data pipeline orchestration (Airflow, Prefect), or frontend and API-only concerns. Follows Vinta's Django Celery best practices.4---5
6# Django Celery Expert
7
8## Instructions
9
10### Step 1: Classify the Request
11
12Identify the task category from the request:
13
14- **Django integration** — transaction safety, ORM patterns, testing, request correlation → read `references/django-integration.md`
15- **Task design** — new tasks, calling patterns, chains/groups/chords, idempotency → read `references/task-design-patterns.md`
16- **Configuration** — broker setup, result backend, worker settings, queue routing → read `references/configuration-guide.md`
17- **Error handling** — retries, backoff, dead letter queues, timeouts → read `references/error-handling.md`
18- **Periodic tasks** — Celery Beat, crontab schedules, dynamic schedules, timezone handling → read `references/periodic-tasks.md`
19- **Monitoring** — Flower, Prometheus, logging, debugging stuck tasks → read `references/monitoring-observability.md`
20- **Production deployment** — scaling, supervision, containers, health checks → read `references/production-deployment.md`
21
22If the request spans multiple categories, read all relevant reference files before continuing.
23
24### Step 2: Read the Reference File(s)
25
26Read each reference file identified in Step 1. Do not proceed to implementation without reading the relevant reference.
27
28### Step 3: Implement
29
30Apply the patterns from the reference file. Before presenting the solution, verify:
31
32- Task arguments are serializable (pass IDs, not model instances)
33- Tasks with retries enabled are idempotent
34- Errors are logged with context
35- Long-running tasks have timeouts configured
36
37## Examples
38
39### Basic Background Task
40
41**Request:** "Send welcome emails in the background after user registration"
42
43```python
44# tasks.py
45from celery import shared_task
46from django.core.mail import send_mail
47
48@shared_task(bind=True, max_retries=3)
49def send_welcome_email(self, user_id):
50 from users.models import User
51
52 try:
53 user = User.objects.get(id=user_id)
54 send_mail(
55 subject="Welcome!",
56 message=f"Hi {user.name}, welcome to our platform!",
57 from_email="noreply@example.com",
58 recipient_list=[user.email],
59 )
60 except User.DoesNotExist:
61 pass
62 except Exception as exc:
63 raise self.retry(exc=exc, countdown=60 * (2 ** self.request.retries))
64
65
66# views.py — queue only after the transaction commits
67from django.db import transaction
68
69def register(request):
70 user = User.objects.create(...)
71 transaction.on_commit(lambda: send_welcome_email.delay(user.id))
72 return redirect("dashboard")
73```
74
75### Task with Progress Tracking
76
77**Request:** "Process a large CSV import with progress updates"
78
79```python
80@shared_task(bind=True)
81def import_csv(self, file_path, total_rows):
82 from myapp.models import Record
83
84 with open(file_path) as f:
85 reader = csv.DictReader(f)
86 for i, row in enumerate(reader):
87 Record.objects.create(**row)
88 if i % 100 == 0:
89 self.update_state(
90 state="PROGRESS",
91 meta={"current": i, "total": total_rows},
92 )
93
94 return {"status": "complete", "processed": total_rows}
95
96
97# Poll progress
98result = import_csv.AsyncResult(task_id)
99if result.state == "PROGRESS":
100 progress = result.info.get("current", 0) / result.info.get("total", 1)
101```
102
103### Workflow with Chains
104
105**Request:** "Process an order: validate inventory, charge payment, then send confirmation"
106
107```python
108from celery import chain
109
110@shared_task
111def validate_inventory(order_id):
112 order = Order.objects.get(id=order_id)
113 if not order.items_in_stock():
114 raise ValueError("Items out of stock")
115 return order_id
116
117@shared_task
118def charge_payment(order_id):
119 order = Order.objects.get(id=order_id)
120 order.charge()
121 return order_id
122
123@shared_task
124def send_confirmation(order_id):
125 Order.objects.get(id=order_id).send_confirmation_email()
126
127def process_order(order_id):
128 chain(
129 validate_inventory.s(order_id),
130 charge_payment.s(),
131 send_confirmation.s(),
132 ).delay()
133```