Communitygithub.com

django-celery

Patrones de tareas asíncronas con Django + Celery: configuración, diseño de tareas, programación con Beat, reintentos, flujos Canvas, monitoreo y pruebas. Útil para añadir trabajos en segundo plano, tareas programadas o procesamiento asíncrono a una aplicación Django.

¿Qué es django-celery?

Este skill proporciona un conjunto integral de patrones de producción para el procesamiento de tareas en segundo plano en Django utilizando Celery con Redis o RabbitMQ. Cubre desde la configuración inicial del proyecto y la definición de tareas básicas hasta patrones avanzados como tareas con reintentos, idempotencia, límites de tiempo suaves, y el uso de Canvas para encadenar y agrupar tareas. También incluye guías detalladas para la programación periódica con Celery Beat (tanto con código como con base de datos), gestión de errores con colas de mensajes muertos, estrategias de prueba (unitarias y de integración), y herramientas de monitoreo como Flower. Es ideal para desarrolladores que necesitan añadir trabajos en segundo plano, tareas programadas o procesamiento asíncrono a una aplicación Django, ofreciendo prácticas recomendadas para evitar antipatrones comunes y garantizar la robustez en producción.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/django-celery

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Patrones de Tareas Asíncronas con Django + Celery

Patrones de nivel de producción para el procesamiento de tareas en segundo plano en Django usando Celery con Redis o RabbitMQ.

Cuándo Activar

  • Añadir trabajos en segundo plano o procesamiento asíncrono a una aplicación Django
  • Implementar tareas periódicas o programadas
  • Descargar operaciones lentas (correo electrónico, generación de PDF, llamadas a API) del ciclo de solicitud
  • Configurar Celery Beat para programación tipo cron
  • Depurar fallos de tareas, reintentos o acumulaciones en colas
  • Escribir pruebas para tareas de Celery

Configuración del Proyecto

Instalación

pip install 'celery[redis]' django-celery-results django-celery-beat

celery.py — Punto de entrada de la aplicación

# config/celery.py
import os
from celery import Celery

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.development')

app = Celery('myproject')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()  # Descubre tasks.py en cada INSTALLED_APP

@app.task(bind=True, ignore_result=True)
def debug_task(self):
    print(f'Request: {self.request!r}')
# config/__init__.py
from .celery import app as celery_app

__all__ = ('celery_app',)

Configuración de Django

# config/settings/base.py

# Broker (Redis recomendado para producción)
CELERY_BROKER_URL = env('CELERY_BROKER_URL', default='redis://localhost:6379/0')
CELERY_RESULT_BACKEND = env('CELERY_RESULT_BACKEND', default='django-db')

# Serialización
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'

# Comportamiento de tareas
CELERY_TASK_TRACK_STARTED = True
CELERY_TASK_TIME_LIMIT = 30 * 60        # Límite duro: 30 min
CELERY_TASK_SOFT_TIME_LIMIT = 25 * 60   # Límite suave: envía SoftTimeLimitExceeded
CELERY_WORKER_PREFETCH_MULTIPLIER = 1   # Evita que el worker acumule tareas largas
CELERY_TASK_ACKS_LATE = True            # Reencola si el worker falla

# Persistencia de resultados
CELERY_RESULT_EXPIRES = 60 * 60 * 24   # Conserva resultados 24 horas

# Programador Beat (para tareas periódicas)
CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler'

# Aplicaciones instaladas
INSTALLED_APPS += [
    'django_celery_results',
    'django_celery_beat',
]

Ejecutar Workers

# Iniciar worker (desarrollo)
celery -A config worker --loglevel=info

# Iniciar programador Beat (tareas periódicas)
celery -A config beat --loglevel=info --scheduler django_celery_beat.schedulers:DatabaseScheduler

# Worker + Beat combinados (solo desarrollo, nunca en producción)
celery -A config worker --beat --loglevel=info

# Producción: múltiples workers con concurrencia
celery -A config worker --loglevel=warning --concurrency=4 -Q default,high_priority

Patrones de Diseño de Tareas

Tarea Básica

# apps/notifications/tasks.py
from celery import shared_task
import logging

logger = logging.getLogger(__name__)

@shared_task(name='notifications.send_welcome_email')
def send_welcome_email(user_id: int) -> None:
    """Envía correo de bienvenida a un usuario recién registrado."""
    from apps.users.models import User
    from apps.notifications.services import EmailService

    try:
        user = User.objects.get(pk=user_id)
    except User.DoesNotExist:
        logger.warning('send_welcome_email: usuario %s no encontrado', user_id)
        return  # Idempotente — no lanzar excepción, la tarea ya es imposible de completar

    EmailService.send_welcome(user)
    logger.info('Correo de bienvenida enviado al usuario %s', user_id)

Tarea con Reintentos

@shared_task(
    bind=True,
    name='integrations.sync_to_crm',
    max_retries=5,
    default_retry_delay=60,       # segundos antes del primer reintento
    autoretry_for=(ConnectionError, TimeoutError),
    retry_backoff=True,           # backoff exponencial
    retry_backoff_max=600,        # límite máximo de 10 minutos
    retry_jitter=True,            # aleatorizar para evitar avalancha de peticiones
)
def sync_contact_to_crm(self, contact_id: int) -> dict:
    """Sincroniza contacto con CRM externo con reintentos ante fallos transitorios."""
    from apps.crm.services import CRMClient

    try:
        result = CRMClient().sync(contact_id)
        return result
    except CRMClient.RateLimitError as exc:
        # Retardo específico desde la cabecera de la respuesta
        raise self.retry(exc=exc, countdown=int(exc.retry_after))

Patrón de Tarea Idempotente

Diseña tareas para que puedan ejecutarse de forma segura múltiples veces con las mismas entradas:

@shared_task(name='orders.mark_shipped')
def mark_order_shipped(order_id: int, tracking_number: str) -> None:
    """Marca un pedido como enviado — seguro de ejecutar múltiples veces."""
    from apps.orders.models import Order

    updated = Order.objects.filter(
        pk=order_id,
        status=Order.Status.PROCESSING,    # Guardia: solo actualizar si no está ya enviado
    ).update(
        status=Order.Status.SHIPPED,
        tracking_number=tracking_number,
    )

    if not updated:
        logger.info('mark_order_shipped: pedido %s ya enviado o no encontrado', order_id)

Tarea con Límite de Tiempo Suave

from celery.exceptions import SoftTimeLimitExceeded

@shared_task(
    bind=True,
    name='reports.generate_pdf',
    soft_time_limit=120,
    time_limit=150,
)
def generate_pdf_report(self, report_id: int) -> str:
    """Genera un informe PDF con manejo de tiempo de espera controlado."""
    from apps.reports.services import PDFGenerator

    try:
        path = PDFGenerator.build(report_id)
        return path
    except SoftTimeLimitExceeded:
        # Limpiar archivos parciales antes de la terminación forzosa
        PDFGenerator.cleanup(report_id)
        raise

Llamar Tareas

from datetime import timedelta
from django.utils import timezone

# Disparar y olvidar (asíncrono)
send_welcome_email.delay(user.pk)

# Programar en el futuro
send_reminder.apply_async(args=[user.pk], countdown=3600)  # Dentro de 1 hora
send_reminder.apply_async(args=[user.pk], eta=timezone.now() + timedelta(days=1))

# Aplicar con enrutamiento de cola
sync_contact_to_crm.apply_async(args=[contact.pk], queue='high_priority')

# Ejecutar de forma síncrona (pruebas / depuración)
result = generate_pdf_report.apply(args=[report.pk])

Programación Beat (Tareas Periódicas)

Programación Definida en Código

# config/settings/base.py
from celery.schedules import crontab

CELERY_BEAT_SCHEDULE = {
    'cleanup-expired-sessions': {
        'task': 'users.cleanup_expired_sessions',
        'schedule': crontab(hour=2, minute=0),   # 2am diariamente
    },
    'sync-inventory': {
        'task': 'products.sync_inventory',
        'schedule': 60.0,                         # cada 60 segundos
    },
    'weekly-digest': {
        'task': 'notifications.send_weekly_digest',
        'schedule': crontab(day_of_week='monday', hour=8, minute=0),
    },
}

Programación Definida en Base de Datos (mediante django-celery-beat)

# Gestionar tareas periódicas desde el administrador de Django o código
from django_celery_beat.models import PeriodicTask, CrontabSchedule
import json

schedule, _ = CrontabSchedule.objects.get_or_create(
    hour='*/6', minute='0',
    timezone='UTC',
)

PeriodicTask.objects.update_or_create(
    name='Sincronizar inventario cada 6 horas',
    defaults={
        'crontab': schedule,
        'task': 'products.sync_inventory',
        'args': json.dumps([]),
        'enabled': True,
    }
)

Canvas: Encadenamiento y Agrupación de Tareas

from celery import chain, group, chord

# Chain: ejecutar tareas secuencialmente, pasando resultados
pipeline = chain(
    fetch_data.s(source_id),
    transform_data.s(),          # recibe el resultado de fetch_data como primer argumento
    load_to_warehouse.s(),
)
pipeline.delay()

# Group: ejecutar tareas en paralelo
parallel = group(
    send_welcome_email.s(user_id)
    for user_id in new_user_ids
)
parallel.delay()

# Chord: tareas paralelas + callback cuando todas completen
result = chord(
    group(process_chunk.s(chunk) for chunk in data_chunks),
    aggregate_results.s(),       # se llama con la lista de resultados de los chunks
)
result.delay()

Manejo de Errores y Cola de Mensajes Muertos

# apps/core/tasks.py
from celery.signals import task_failure

@task_failure.connect
def on_task_failure(sender, task_id, exception, args, kwargs, traceback, einfo, **kw):
    """Registra todos los fallos de tareas en Sentry / sistema de alertas."""
    import sentry_sdk
    with sentry_sdk.new_scope() as scope:
        scope.set_context('celery', {
            'task': sender.name,
            'task_id': task_id,
            'args': args,
            'kwargs': kwargs,
        })
        sentry_sdk.capture_exception(exception)
# Enrutar tareas fallidas a la cola de mensajes muertos después de máximos reintentos
@shared_task(
    bind=True,
    max_retries=3,
    name='payments.charge_card',
)
def charge_card(self, order_id: int) -> None:
    from apps.payments.models import Order, FailedCharge

    try:
        _do_charge(order_id)
    except Exception as exc:
        if self.request.retries >= self.max_retries:
            # Persistir en tabla de mensajes muertos para revisión manual
            FailedCharge.objects.create(
                order_id=order_id,
                error=str(exc),
                task_id=self.request.id,
            )
            return  # No lanzar — la tarea ha fallado permanentemente
        raise self.retry(exc=exc)

Pruebas de Tareas Celery

Pruebas Unitarias (Sin Broker)

# tests/test_tasks.py
import pytest
from unittest.mock import patch, MagicMock
from apps.notifications.tasks import send_welcome_email

class TestSendWelcomeEmail:

    @pytest.mark.django_db
    def test_envia_correo_a_usuario_existente(self, user):
        with patch('apps.notifications.services.EmailService') as mock_email:
            send_welcome_email(user.pk)
            mock_email.send_welcome.assert_called_once_with(user)

    @pytest.mark.django_db
    def test_ignora_usuario_faltante_sin_excepciones(self):
        """No debe lanzar excepción si el usuario se elimina entre el encolado y la ejecución."""
        send_welcome_email(99999)  # Usuario inexistente — no debe lanzar

Pruebas de Integración con CELERY_TASK_ALWAYS_EAGER

# config/settings/test.py
CELERY_TASK_ALWAYS_EAGER = True      # Ejecutar tareas de forma síncrona en pruebas
CELERY_TASK_EAGER_PROPAGATES = True  # Relanzar excepciones de las tareas

# tests/test_integration.py
@pytest.mark.django_db
def test_registro_desencadena_correo_de_bienvenida(client):
    with patch('apps.notifications.services.EmailService') as mock_email:
        response = client.post('/api/users/', {
            'email': '[email protected]',
            'password': 'strongpass123',
        })

    assert response.status_code == 201
    mock_email.send_welcome.assert_called_once()

Pruebas de Reintentos

@pytest.mark.django_db
def test_tarea_reintenta_en_error_de_conexion():
    with patch('apps.crm.services.CRMClient.sync') as mock_sync:
        mock_sync.side_effect = ConnectionError('timeout')

        with pytest.raises(ConnectionError):
            sync_contact_to_crm.apply(args=[1], throw=True)

        assert mock_sync.call_count == 1  # Solo primer intento cuando es eager

Monitoreo

# Inspeccionar workers y colas activos
celery -A config inspect active
celery -A config inspect stats
celery -A config inspect reserved

# Comprobar longitudes de colas (Redis)
redis-cli llen celery

# Flower: monitor web en tiempo real
pip install flower
celery -A config flower --port=5555

Antipatrones

# MALO: Pasar instancias de modelos — pueden estar obsoletas al momento de la ejecución
send_welcome_email.delay(user)        # Nunca pasar objetos ORM
send_welcome_email.delay(user.pk)     # Siempre pasar PKs

# MALO: Llamar tareas de forma síncrona en vistas de producción
result = generate_report.apply()      # Bloquea el hilo de la solicitud

# MALO: Tarea no idempotente sin guardias
@shared_task
def charge_and_fulfill(order_id):
    order.charge()     # ¡Puede cobrar dos veces si la tarea se reintenta!
    order.fulfill()

# BUENO: Idempotente con guardia de estado
@shared_task
def charge_and_fulfill(order_id):
    order = Order.objects.select_for_update().get(pk=order_id)
    if order.status != Order.Status.PENDING:
        return  # Ya procesado
    order.charge()
    order.fulfill()

Lista de Verificación para Producción

VerificaciónConfiguración
Reinicio del worker al fallarUnidad supervisord o systemd
CELERY_TASK_ACKS_LATE = TrueReencolar tareas si el worker falla
CELERY_WORKER_PREFETCH_MULTIPLIER = 1Distribución justa de tareas largas
Colas separadas por prioridad-Q default,high_priority,low_priority
CELERY_TASK_SOFT_TIME_LIMIT establecidoTiempo de espera controlado antes de terminación forzosa
Integración con SentryCapturar todas las señales task_failure
Flower u otro monitorVisibilidad de las profundidades de cola
Beat ejecutándose en un solo nodoEvita ejecución duplicada de tareas programadas

Skills Relacionados

  • django-patterns — ORM, capa de servicios y estructura del proyecto
  • django-tdd — Pruebas de modelos, vistas y servicios de Django
  • python-testing — Configuración de pytest y fixtures

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

Skills relacionados