from django.db.models.signals import post_save
from django.dispatch import receiver
from django.core.mail import send_mail
from .models import User, Profile
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
"""Create a profile automatically when a user is created."""
if created:
Profile.objects.create(user=instance)
@receiver(post_save, sender=User)
def send_welcome_email(sender, instance, created, **kwargs):
"""Send welcome email to new users."""
if created:
send_mail(
subject='Welcome to our platform!',
message=f'Hi {instance.first_name}, welcome aboard!',
from_email='noreply@example.com',
recipient_list=[instance.email],
fail_silently=True,
)
from django.apps import AppConfig
class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'accounts'
def ready(self):
import accounts.signals # noqa
Signals allow different parts of the application to respond to model events without tight coupling. I use post_save for actions after an object is created or updated, like sending notifications or updating related records. The @receiver decorator is cleaner than connect() calls. I'm careful to check created flag to distinguish creation from updates. For heavy operations, I dispatch to Celery tasks instead of blocking the request. Signals are powerful but can make code harder to trace, so I document them well and use sparingly for cross-app coordination.