Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
События в сигналах (Django Signals)
Django signals — это механизм отправки и получения сообщений при определённых событиях в приложении. Используется dispatch pattern для декуплирования.
Встроенные сигналы Django
1. Сигналы моделей (django.db.models.signals)
pre_save — ДО сохранения модели в БД:
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.contrib.auth.models import User
@receiver(pre_save, sender=User)
def normalize_email(sender, instance, **kwargs):
"""Привести email к нижнему регистру ДО сохранения"""
instance.email = instance.email.lower()
print(f"Pre-saving user: {instance.username}")
# Использование
user = User(username="john", email="JOHN@EXAMPLE.COM")
user.save() # Сигнал срабатывает, email становится john@example.com
post_save — ПОСЛЕ сохранения модели:
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
"""Создать профиль при создании пользователя"""
if created: # Новый объект создан
UserProfile.objects.create(user=instance)
send_welcome_email(instance.email)
else: # Объект обновлён
instance.profile.updated_at = timezone.now()
instance.profile.save()
print(f"Post-saved user: {instance.username}")
# Использование
user = User.objects.create(username="jane", email="jane@example.com")
# Сигнал post_save срабатывает, создаётся профиль
pre_delete — ДО удаления модели:
@receiver(pre_delete, sender=User)
def backup_user_data(sender, instance, **kwargs):
"""Сохранить данные пользователя ДО удаления"""
UserBackup.objects.create(
username=instance.username,
email=instance.email,
deleted_at=timezone.now()
)
print(f"Pre-deleting user: {instance.username}")
post_delete — ПОСЛЕ удаления модели:
@receiver(post_delete, sender=User)
def cleanup_after_user_deletion(sender, instance, **kwargs):
"""Очистить связанные данные ПОСЛЕ удаления"""
# Удалить файлы
if instance.profile.avatar:
instance.profile.avatar.delete(save=False)
# Отправить уведомление
log_deletion(f"User {instance.username} deleted")
print(f"Post-deleted user: {instance.username}")
# Использование
user = User.objects.get(id=123)
user.delete() # post_delete сигнал срабатывает
2. Сигналы управления (django.core.management.signals)
call_command — перед выполнением управления команды:
from django.core.management.signals import pre_command, post_command
@receiver(pre_command)
def before_command(sender, **kwargs):
print(f"Command starting: {sender}")
@receiver(post_command)
def after_command(sender, **kwargs):
print(f"Command finished: {sender}")
# Использование
python manage.py migrate # Сигналы срабатывают
3. Сигналы запросов (django.test.signals)
setting_changed — когда изменяется параметр конфигурации:
from django.test.signals import setting_changed
@receiver(setting_changed)
def on_setting_changed(sender, setting, **kwargs):
if setting == "DEBUG":
print(f"DEBUG mode changed")
elif setting == "DATABASES":
print("Database configuration changed")
4. Сигналы приложения (django.apps.signals)
apps_ready — когда приложение загружено:
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = "myapp"
def ready(self):
"""Вызывается когда приложение готово"""
import myapp.signals # Регистрируем сигналы
print("MyApp is ready!")
5. Custom сигналы (пользовательские события)
Можно создавать свои сигналы:
from django.dispatch import Signal, receiver
# Определяем свой сигнал
order_completed = Signal()
payment_processed = Signal()
user_verified = Signal()
# Отправляем сигнал
class OrderService:
def complete_order(self, order_id):
order = Order.objects.get(id=order_id)
order.status = "completed"
order.save()
# Отправляем свой сигнал
order_completed.send(
sender=self.__class__,
order_id=order_id,
user_id=order.user_id,
total_amount=order.total
)
# Слушаем сигнал
@receiver(order_completed)
def on_order_completed(sender, order_id, user_id, total_amount, **kwargs):
print(f"Order {order_id} completed for user {user_id}: ${total_amount}")
send_confirmation_email(user_id, order_id)
update_analytics(user_id, total_amount)
6. Практический пример: Полный workflow
from django.db import models
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from django.core.mail import send_mail
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
published = models.BooleanField(default=False)
class PostLike(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="likes")
user = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
# Сигналы
@receiver(post_save, sender=Post)
def post_published(sender, instance, created, **kwargs):
"""Уведомление при публикации поста"""
if not created and instance.published:
# Отправляем письма подписчикам
followers = User.objects.filter(following=instance.author)
for follower in followers:
send_mail(
subject=f"New post: {instance.title}",
message=f"Check out: {instance.content[:100]}...",
from_email="noreply@example.com",
recipient_list=[follower.email],
)
@receiver(post_save, sender=PostLike)
def on_post_liked(sender, instance, created, **kwargs):
"""Уведомление автору при like"""
if created:
post = instance.post
likes_count = post.likes.count()
# Отправляем уведомление каждый 10-й like
if likes_count % 10 == 0:
send_mail(
subject=f"Your post reached {likes_count} likes!",
message=f"{post.title} is getting popular!",
from_email="noreply@example.com",
recipient_list=[post.author.email],
)
@receiver(post_delete, sender=PostLike)
def on_post_unliked(sender, instance, **kwargs):
"""Логирование unlike"""
post = instance.post
likes_count = post.likes.count()
print(f"Post {post.id} now has {likes_count} likes")
7. Подключение сигналов
В apps.py:
from django.apps import AppConfig
class MyAppConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "myapp"
def ready(self):
import myapp.signals # Регистрируем сигналы
В signals.py:
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import User, UserProfile
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
8. Сравнение подходов
| Подход | Когда использовать | Плюсы | Минусы |
|---|---|---|---|
| Signals | Побочные эффекты | Декуплирование | Сложна отладка |
| Overrides | Расширение модели | Явно | Тесная связь |
| Managers | Кастомные запросы | Переиспользование | Не для побочных эффектов |
| Middleware | Глобальные эффекты | Ловит все | Сложна для моделей |
События в сигналах: Полный список
Модели:
pre_save— перед сохранениемpost_save— после сохраненияpre_delete— перед удалениемpost_delete— после удаления
Custom сигналы:
- Любые события которые определишь
Система:
setting_changed— изменение конфигурацииpre_command/post_command— управление командамиapps_ready— готовность приложения
Signals — мощный инструмент для декуплирования логики, но используй их осторожно: они делают код менее явным и сложнее отлаживаемым.