Django Signals vs Celery Tasks 2026: Quando Usare Quale e Domande per Colloqui
Scopri le differenze tra Django Signals e Celery Tasks, quando utilizzare ciascuno strumento e le domande più frequenti nei colloqui tecnici 2026.

Django offre agli sviluppatori numerosi strumenti per gestire la logica basata su eventi e l'elaborazione asincrona. Due dei meccanismi più importanti sono Django Signals e Celery Tasks. Sebbene a prima vista possano sembrare soluzioni simili, differiscono fondamentalmente nella loro architettura e nei casi d'uso.
La differenza fondamentale: Django Signals sono sincroni ed eseguiti nello stesso processo, mentre Celery Tasks vengono eseguiti in modo asincrono in processi worker separati.
Cosa Sono i Django Signals?
I Django Signals implementano il pattern Observer e permettono un accoppiamento lasco tra diverse parti di un'applicazione. Consentono di attivare determinate azioni quando si verificano eventi definiti, senza che il mittente debba conoscere il destinatario.
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from .models import UserProfile
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)Questo esempio mostra un caso d'uso classico: la creazione automatica di un oggetto UserProfile quando viene registrato un nuovo utente.
Signals Integrati in Django
Django fornisce diversi signals predefiniti:
# Model Signals
from django.db.models.signals import (
pre_save,
post_save,
pre_delete,
post_delete,
m2m_changed
)
# Request/Response Signals
from django.core.signals import (
request_started,
request_finished
)
# Database Signals
from django.db.backends.signals import connection_createdDefinire Signals Personalizzati
Per casi d'uso specifici è possibile creare signals personalizzati:
from django.dispatch import Signal
# Definire il signal
order_completed = Signal()
# Inviare il signal
class Order:
def complete(self):
self.status = "completed"
self.save()
order_completed.send(sender=self.__class__, order=self)
# Ricevere il signal
@receiver(order_completed)
def send_order_confirmation(sender, order, **kwargs):
send_email_notification(order.customer.email, order)Cosa Sono i Celery Tasks?
Celery è una coda di task distribuita che esegue operazioni asincrone in processi worker separati. I task vengono inseriti in una coda messaggi (Redis, RabbitMQ) ed elaborati dai worker.
from celery import shared_task
from django.core.mail import send_mail
@shared_task
def send_welcome_email(user_id):
from django.contrib.auth.models import User
user = User.objects.get(id=user_id)
send_mail(
subject="Benvenuto!",
message=f"Ciao {user.username}, benvenuto sulla nostra piattaforma.",
from_email="noreply@example.com",
recipient_list=[user.email]
)Configurazione di Celery in Django
# celery.py
import os
from celery import Celery
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
app = Celery("myproject")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()
# settings.py
CELERY_BROKER_URL = "redis://localhost:6379/0"
CELERY_RESULT_BACKEND = "redis://localhost:6379/0"
CELERY_ACCEPT_CONTENT = ["json"]
CELERY_TASK_SERIALIZER = "json"Funzionalità Avanzate di Celery
Celery offre numerose funzionalità avanzate:
from celery import shared_task
from celery.exceptions import MaxRetriesExceededError
@shared_task(bind=True, max_retries=3, default_retry_delay=60)
def process_payment(self, order_id):
try:
order = Order.objects.get(id=order_id)
result = payment_gateway.charge(order.amount)
order.payment_status = "completed"
order.save()
return result
except PaymentError as exc:
try:
self.retry(exc=exc)
except MaxRetriesExceededError:
order.payment_status = "failed"
order.save()
raise
# Task con pianificazione
@shared_task
def daily_report():
generate_and_send_report()
# In celery.py
app.conf.beat_schedule = {
"daily-report": {
"task": "myapp.tasks.daily_report",
"schedule": crontab(hour=8, minute=0),
},
}Quando Utilizzare Django Signals?
I Django Signals sono ideali per:
1. Effetti collaterali sincroni all'interno del ciclo di richiesta
@receiver(post_save, sender=Order)
def update_inventory(sender, instance, created, **kwargs):
if created:
for item in instance.items.all():
item.product.stock -= item.quantity
item.product.save()2. Accoppiamento lasco tra app Django
# Nell'app Payments
@receiver(post_save, sender=Payment)
def notify_order_app(sender, instance, **kwargs):
if instance.status == "successful":
payment_received.send(sender=sender, payment=instance)3. Consistenza dei dati nelle modifiche ai Model
@receiver(pre_save, sender=Article)
def generate_slug(sender, instance, **kwargs):
if not instance.slug:
instance.slug = slugify(instance.title)Quando Utilizzare Celery Tasks?
Celery è la scelta giusta per:
1. Operazioni di lunga durata
@shared_task
def generate_pdf_report(report_id):
report = Report.objects.get(id=report_id)
pdf = generate_complex_pdf(report.data) # Può richiedere minuti
report.pdf_file.save(f"report_{report_id}.pdf", ContentFile(pdf))
report.status = "completed"
report.save()2. Chiamate API esterne
@shared_task(bind=True, max_retries=5)
def sync_with_external_crm(self, customer_id):
try:
customer = Customer.objects.get(id=customer_id)
crm_api.update_contact(customer.to_dict())
except CRMAPIError as exc:
self.retry(exc=exc, countdown=2**self.request.retries)3. Elaborazione massiva
from celery import group
@shared_task
def process_single_image(image_id):
image = Image.objects.get(id=image_id)
image.create_thumbnails()
image.optimize()
image.processed = True
image.save()
def process_all_images(image_ids):
job = group(process_single_image.s(id) for id in image_ids)
result = job.apply_async()
return result4. Attività pianificate e ricorrenti
@shared_task
def cleanup_expired_sessions():
from django.contrib.sessions.models import Session
from django.utils import timezone
Session.objects.filter(expire_date__lt=timezone.now()).delete()Combinare Signals e Celery
Una best practice comune è utilizzare i Signals come trigger per i Celery Tasks:
@receiver(post_save, sender=Order)
def handle_new_order(sender, instance, created, **kwargs):
if created:
# Sincrono: aggiornare dati critici
instance.order_number = generate_order_number()
instance.save(update_fields=["order_number"])
# Asincrono: delegare operazioni dispendiose
send_order_confirmation_email.delay(instance.id)
notify_warehouse.delay(instance.id)
sync_with_erp.delay(instance.id)Pronto a superare i tuoi colloqui su Django?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Tabella Comparativa: Signals vs Celery
| Aspetto | Django Signals | Celery Tasks |
|---|---|---|
| Esecuzione | Sincrona | Asincrona |
| Processo | Stesso processo | Worker separato |
| Infrastruttura | Nessuna aggiuntiva | Richiede message broker |
| Gestione errori | Propagazione exception | Meccanismo retry |
| Scalabilità | Limitata | Scalabile orizzontalmente |
| Debug | Semplice | Più complesso |
| Latenza | Aumenta tempo risposta | Nessun impatto |
Errori Comuni e Anti-Pattern
Signals
# SBAGLIATO: Operazioni lente nei Signals
@receiver(post_save, sender=User)
def bad_signal_handler(sender, instance, **kwargs):
send_email(instance.email) # Blocca la richiesta
call_external_api(instance) # Può fallire
# CORRETTO: Usare Celery per operazioni pesanti
@receiver(post_save, sender=User)
def good_signal_handler(sender, instance, created, **kwargs):
if created:
send_welcome_email.delay(instance.id)Celery
# SBAGLIATO: Passare istanze Model come parametri
@shared_task
def bad_task(user):
user.send_notification() # L'oggetto potrebbe essere obsoleto
# CORRETTO: Passare solo ID
@shared_task
def good_task(user_id):
user = User.objects.get(id=user_id)
user.send_notification()Domande e Risposte per Colloqui
Domanda 1: Qual è la differenza principale tra Django Signals e Celery Tasks?
I Django Signals sono sincroni e vengono eseguiti nello stesso processo del codice chiamante. I Celery Tasks vengono eseguiti in modo asincrono in processi worker separati. I Signals sono adatti per reazioni rapide e interne agli eventi, mentre Celery è progettato per attività di background scalabili e dispendiose in termini di tempo.
Domanda 2: Perché nei signal handler si dovrebbero passare ID invece di oggetti?
Quando si combinano Django Signals con Celery e si passano istanze Model, possono verificarsi problemi: l'oggetto deve essere serializzato, il che può fallire con oggetti complessi. Inoltre, l'oggetto potrebbe essere obsoleto al momento dell'esecuzione del task. Passare ID e ricaricare dal database garantisce dati aggiornati.
Domanda 3: Come si prevengono i loop infiniti con i Signals?
@receiver(post_save, sender=Profile)
def update_profile(sender, instance, **kwargs):
if not getattr(instance, "_skip_signal", False):
instance._skip_signal = True
instance.last_updated = timezone.now()
instance.save(update_fields=["last_updated"])In alternativa, si può utilizzare update_fields nel metodo save() oppure usare update() invece di save().
Domanda 4: Come si implementa la logica di retry nei Celery Tasks?
Celery offre funzionalità di retry integrate:
@shared_task(bind=True, max_retries=3, autoretry_for=(ConnectionError,))
def resilient_task(self, data):
try:
process(data)
except TemporaryError as exc:
raise self.retry(exc=exc, countdown=60)Domanda 5: Quando è opportuno definire Signals personalizzati invece di usare quelli integrati?
I Signals personalizzati sono utili quando si devono segnalare eventi specifici del dominio che non sono direttamente correlati alle operazioni sui Model. Esempi: processi aziendali come "ordine completato" o "pagamento ricevuto", che devono essere gestiti da più componenti indipendenti.
Considerazioni sulle Performance
Performance dei Signals
# Stabilire la connessione una sola volta
from django.apps import AppConfig
class MyAppConfig(AppConfig):
def ready(self):
import myapp.signals # noqa: F401Performance di Celery
# Task routing per migliori performance
app.conf.task_routes = {
"myapp.tasks.quick_*": {"queue": "fast"},
"myapp.tasks.slow_*": {"queue": "slow"},
}
# Regolare il prefetch multiplier
app.conf.worker_prefetch_multiplier = 1 # Per task lentiConclusione
Django Signals e Celery Tasks sono strumenti complementari con ambiti di applicazione differenti. I Signals sono eccellenti per scenari di gestione eventi sincroni e interni al processo con accoppiamento lasco. Celery rappresenta la soluzione ottimale per l'elaborazione asincrona e scalabile in background.
L'architettura migliore combina entrambi gli approcci: Signals come trigger leggeri che, quando necessario, avviano Celery Tasks per operazioni pesanti. Questa combinazione garantisce applicazioni reattive e un'elaborazione in background robusta.
Sapresti trovare il bug in Django?
Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Scritto da
Anthony Fillion-MailletFondatore di SharpSkill
Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.
Aggiornato il 2 settembre 2026
Tag
Condividi
Articoli correlati

Django e Celery: Elaborazione Asincrona dei Task e Domande da Colloquio 2026
Guida completa a Django e Celery: configurazione, task asincroni, code, Celery Beat, monitoraggio e domande frequenti nei colloqui tecnici 2026.

Django async view e ASGI nel 2026: performance e domande da colloquio
Un'analisi approfondita delle async view di Django e di ASGI nel 2026: come funzionano internamente, quale server usare in produzione, l'ORM asincrono e la trappola SynchronousOnlyOperation, oltre alle domande da colloquio.

Django 5.2: Custom Middleware e Gestione dei Segnali per Colloqui Tecnici
Padroneggiare middleware e segnali in Django 5.2: pipeline middleware, middleware asincrono, pre_save/post_save signals e pattern comuni nei colloqui tecnici.