Сигнал pre_delete
Сигнал pre_delete относится к механизму сигналов Django
и используется для выполнения пользовательского кода до того,
как объект модели будет удален из базы данных. Сигнал отправляется
при вызове метода delete экземпляра модели или
при удалении через QuerySet. Основная особенность этого
сигнала - возможность выполнить подготовительные действия
или вовсе предотвратить удаление объекта (в сочетании с
исключениями), а также обработать связанные объекты до
того, как каскадное удаление вступит в силу.
Параметрами сигнала являются: sender - класс модели,
отправляющий сигнал; instance - удаляемый экземпляр
модели; using - псевдоним базы данных, используемый
для операции; origin - источник удаления (например,
сам экземпляр или QuerySet); transaction - состояние
транзакции, в которой выполняется удаление. Эти параметры
позволяют гибко управлять процессом удаления и контекстом
его выполнения.
Синтаксис
from django.db.models.signals import pre_delete
from django.dispatch import receiver
@receiver(pre_delete, sender=MyModel)
def my_handler(sender, instance, **kwargs):
# выполнить действия перед удалением instance
pass
Пример
Рассмотрим простой пример логирования удаления статьи.
Создадим модель Article с полями title и
created_at, и модель Log для хранения
истории удалений. Затем подключим обработчик сигнала:
from django.db import models
from django.utils import timezone
class Article(models.Model):
title = models.CharField(max_length=200)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class Log(models.Model):
message = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.message
Теперь создадим обработчик сигнала, который будет записывать информацию об удалении:
from django.db.models.signals import pre_delete
from django.dispatch import receiver
from myapp.models import Article, Log
@receiver(pre_delete, sender=Article)
def log_article_deletion(sender, instance, **kwargs):
message = f"Article '{instance.title}' deleted at {timezone.now()}"
Log.objects.create(message=message)
При удалении объекта Article создается запись в логе:
from myapp.models import Article
article = Article.objects.get(id=1)
article.delete()
# В Log появится запись: "Article 'Some title' deleted at ..."
Пример
Часто возникает необходимость проверить зависимости перед удалением объекта. Например, удалять категорию можно только в том случае, если она не содержит статей. Реализуем это с помощью сигнала:
from django.db import models
from django.core.exceptions import ValidationError
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Article(models.Model):
title = models.CharField(max_length=200)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='articles')
Обработчик сигнала для модели Category, который
проверяет наличие связанных статей:
from django.db.models.signals import pre_delete
from django.dispatch import receiver
from django.core.exceptions import ValidationError
from myapp.models import Category
@receiver(pre_delete, sender=Category)
def prevent_category_deletion(sender, instance, **kwargs):
if instance.articles.exists():
raise ValidationError(
f"Category '{instance.name}' has related articles and cannot be deleted."
)
Попытка удалить категорию со статьями приведет к ошибке:
from myapp.models import Category
category = Category.objects.get(id=1)
try:
category.delete()
except ValidationError as e:
print(e.message)
Результат выполнения кода:
"Category 'Technology' has related articles and cannot be deleted."
Пример
Иногда необходимо выполнить очистку внешних ресурсов,
например, удалить файлы, связанные с удаляемым объектом.
Предположим, у нас есть модель с полем image:
from django.db import models
class Profile(models.Model):
name = models.CharField(max_length=100)
avatar = models.ImageField(upload_to='avatars/')
Обработчик сигнала, который удаляет файл с диска перед удалением записи из базы:
import os
from django.db.models.signals import pre_delete
from django.dispatch import receiver
from myapp.models import Profile
@receiver(pre_delete, sender=Profile)
def delete_avatar_file(sender, instance, **kwargs):
if instance.avatar:
if os.path.isfile(instance.avatar.path):
os.remove(instance.avatar.path)
Теперь при удалении профиля его аватар будет удален с файловой системы автоматически:
from myapp.models import Profile
profile = Profile.objects.get(id=1)
profile.delete() # файл avatar удаляется с диска
Смотрите также
-
сигнал
post_delete,
который срабатывает после удаления объекта -
сигнал
pre_save,
который срабатывает перед сохранением объекта -
сигнал
post_save,
который срабатывает после сохранения объекта -
сигнал
m2m_changed,
который срабатывает при изменении связи ManyToMany