Django cookbook · Intermediate
Run background work with Django's tasks framework
Sending emails or generating reports inside requests makes them slow and fragile.
Approach
Define tasks with @task (Django 6's built-in tasks framework) and enqueue them after the transaction commits.
Configure a real backend (such as the database backend from the django-tasks package, or a Celery/RQ setup)
in production; the built-in ImmediateBackend runs tasks inline for development and tests.
Solution
# reports/tasks.py
from django.core.mail import send_mail
from django.tasks import task
@task(queue_name="emails")
def send_welcome_email(user_id: int) -> None:
from django.contrib.auth import get_user_model
user = get_user_model().objects.get(id=user_id) # pass IDs, load fresh data in the task
send_mail("Welcome!", "Thanks for signing up.", None, [user.email])
# accounts/views.py
from functools import partial
from django.db import transaction
def complete_signup(user):
transaction.on_commit(partial(send_welcome_email.enqueue, user_id=user.id))
# settings.py
TASKS = {
"default": {"BACKEND": "django.tasks.backends.immediate.ImmediateBackend", "QUEUES": ["default", "emails"]},
}
Pitfalls
- Passing model instances to tasks; they're serialized and stale when the task runs. Pass IDs.
- Enqueuing inside a transaction that later rolls back, so the task runs for data that doesn't exist.
- Non-idempotent tasks; workers may retry, so make them safe to run twice.
Variations
- Celery remains common for complex workflows (chains, retries with backoff, beat schedules).
- Use
.using(priority=...)or separate queues for slow and fast work.
Further reading: Django tasks framework
Reference: https://docs.djangoproject.com/en/6.0/topics/tasks/
More Django recipes
- Add a required column without downtime
- Add full-text search with PostgreSQL
- Add health checks and structured logging
- Avoid N+1 queries with select_related and prefetch_related
- Cache expensive queries and invalidate on change
- Call external APIs concurrently in async views
- Configure settings from environment variables
- Enforce rules with database constraints