Сигнал post_delete
Сигнал post_delete отправляется автоматически после того, как объект модели был успешно удален из базы данных. Сигнал полезен для выполнения дополнительных действий, таких как удаление связанных файлов, очистка кэша, отправка уведомлений или ведение журнала аудита. Важно отметить, что сигнал срабатывает даже при каскадном удалении через связь ForeignKey с параметром on_delete=models.CASCADE.
Сигнал принимает несколько аргументов: sender - класс модели, которая была удалена; instance - экземпляр удаляемого объекта (важно: объект еще существует в памяти, но уже удален из БД); using - псевдоним базы данных; origin - источник удаления (добавлен в Django 3.2). Сигнал работает в рамках транзакции, поэтому если транзакция будет откатана, изменения, внесенные в обработчике, также будут отменены.
Синтаксис
from django.db.models.signals import post_delete
from django.dispatch import receiver
@receiver(post_delete, sender=MyModel)
def my_handler(sender, instance, **kwargs):
# Your logic here
pass
Пример
Рассмотрим модель Article, которая хранит файлы изображений. При удалении статьи необходимо также удалить файл из файловой системы:
from django.db import models
from django.db.models.signals import post_delete
from django.dispatch import receiver
import os
class Article(models.Model):
title = models.CharField(max_length=100)
image = models.ImageField(upload_to='articles/')
created_at = models.DateTimeField(auto_now_add=True)
@receiver(post_delete, sender=Article)
def delete_article_image(sender, instance, **kwargs):
# Delete the image file from filesystem
if instance.image and os.path.isfile(instance.image.path):
os.remove(instance.image.path)
print(f"Deleted image: {instance.image.path}")
Результат выполнения при удалении объекта:
"Deleted image: /media/articles/photo_2025.jpg"
Пример
Рассмотрим использование сигнала для логирования удалений. Создадим модель LogEntry, которая будет хранить информацию о всех удаленных записях:
from django.db import models
from django.db.models.signals import post_delete
from django.dispatch import receiver
from django.utils.timezone import now
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.IntegerField()
class DeletionLog(models.Model):
model_name = models.CharField(max_length=100)
object_id = models.IntegerField()
object_repr = models.CharField(max_length=200)
deleted_at = models.DateTimeField(auto_now_add=True)
@receiver(post_delete, sender=Product)
def log_product_deletion(sender, instance, **kwargs):
DeletionLog.objects.create(
model_name='Product',
object_id=instance.id,
object_repr=str(instance),
deleted_at=now()
)
print(f"Logged deletion of product: {instance.name}")
Результат выполнения при удалении продукта:
"Logged deletion of product: Laptop"
Пример
Рассмотрим работу сигнала с каскадным удалением. При удалении категории, все связанные с ней статьи должны быть удалены, и сигнал сработает для каждого удаленного объекта:
from django.db import models
from django.db.models.signals import post_delete
from django.dispatch import receiver
class Category(models.Model):
name = models.CharField(max_length=50)
class Post(models.Model):
title = models.CharField(max_length=100)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
@receiver(post_delete, sender=Post)
def post_deleted_handler(sender, instance, **kwargs):
print(f"Post '{instance.title}' was deleted")
@receiver(post_delete, sender=Category)
def category_deleted_handler(sender, instance, **kwargs):
print(f"Category '{instance.name}' was deleted")
# Deleting category triggers post_delete for each related Post
category = Category.objects.get(id=1)
category.delete()
Результат выполнения кода:
"Post 'Django Basics' was deleted"
"Post 'Advanced Django' was deleted"
"Category 'Programming' was deleted"
Пример
Рассмотрим использование сигнала для обновления статистики. При удалении статьи уменьшаем счетчик общего количества статей:
from django.db import models
from django.db.models.signals import post_delete
from django.dispatch import receiver
class Author(models.Model):
name = models.CharField(max_length=50)
articles_count = models.IntegerField(default=0)
class Article(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
@receiver(post_delete, sender=Article)
def update_author_stats(sender, instance, **kwargs):
author = instance.author
author.articles_count -= 1
author.save()
print(f"Updated articles count for {author.name}: {author.articles_count}")
Результат выполнения при удалении статьи:
"Updated articles count for John Doe: 4"
Смотрите также
-
сигнал
pre_delete,
который срабатывает перед удалением объекта -
сигнал
post_save,
который отправляется после сохранения объекта -
сигнал
pre_save,
который срабатывает перед сохранением объекта -
метод
delete,
который удаляет объект из базы данных