Сигнал pre_save
Сигнал pre_save отправляется непосредственно перед тем, как объект модели будет сохранен в базу данных. Он позволяет выполнить произвольный код на этапе подготовки объекта к сохранению, например, изменить значения полей, провести дополнительную валидацию или создать связанные объекты. Сигнал отправляется как при создании нового объекта, так и при обновлении существующего, и передает в обработчик сам экземпляр модели, флаг создания и дополнительные аргументы.
Синтаксис
from django.db.models.signals import pre_save
from django.dispatch import receiver
@receiver(pre_save, sender=MyModel)
def my_handler(sender, instance, raw, using, update_fields, **kwargs):
# выполнить действия перед сохранением
pass
Параметры обработчика
Обработчик сигнала pre_save получает следующие параметры:
-
sender- класс модели, отправившей сигнал -
instance- экземпляр модели, который будет сохранен -
raw- булевый флаг, указывающий, выполняется ли сохранение из фикстур -
using- имя базы данных, используемой для сохранения -
update_fields- список полей, переданных в методsave, илиNone, если не указаны
Пример
Рассмотрим модель Article, в которой перед сохранением нужно автоматически обновлять поле updated_at текущей датой:
from django.db import models
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.utils import timezone
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
@receiver(pre_save, sender=Article)
def update_article_timestamp(sender, instance, **kwargs):
instance.updated_at = timezone.now()
# Создаем статью
article = Article(title="First Article", content="Some content")
article.save()
В этом примере перед сохранением каждой статьи в поле updated_at будет записано текущее время.
Пример
Перед сохранением пользователя автоматически преобразуем его email в нижний регистр:
from django.db import models
from django.db.models.signals import pre_save
from django.dispatch import receiver
class User(models.Model):
username = models.CharField(max_length=100)
email = models.EmailField()
is_active = models.BooleanField(default=True)
@receiver(pre_save, sender=User)
def normalize_user_email(sender, instance, **kwargs):
if instance.email:
instance.email = instance.email.lower()
# Создаем пользователя
user = User(username="john_doe", email="John@Example.com")
user.save()
# Проверяем результат
print(f"Email: {user.email}")
Результат выполнения кода:
"Email: john@example.com"
Пример
Используем сигнал pre_save для автоматического создания связанного объекта при сохранении нового продукта:
from django.db import models
from django.db.models.signals import pre_save
from django.dispatch import receiver
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.IntegerField()
class ProductLog(models.Model):
product = models.OneToOneField(Product, on_delete=models.CASCADE)
status = models.CharField(max_length=50)
@receiver(pre_save, sender=Product)
def create_product_log(sender, instance, **kwargs):
if not instance.pk:
# Новый продукт - создаем лог
log = ProductLog(product=instance, status="created")
log.save()
# Создаем продукт
product = Product(name="Laptop", price=1200)
product.save()
# Проверяем наличие лога
has_log = ProductLog.objects.filter(product=product).exists()
print(f"Log exists: {has_log}")
Результат выполнения кода:
"Log exists: True"
Пример
Отфильтруем сигнал по типу операции, используя проверку первичного ключа, и выполним разные действия при создании и обновлении записи:
from django.db import models
from django.db.models.signals import pre_save
from django.dispatch import receiver
class Category(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField(unique=True)
is_active = models.BooleanField(default=True)
@receiver(pre_save, sender=Category)
def handle_category_save(sender, instance, **kwargs):
if instance.pk is None:
# Создание новой категории
print(f"Creating new category: {instance.name}")
else:
# Обновление существующей категории
print(f"Updating category: {instance.name}")
# Создание новой категории
category = Category(name="Technology", slug="tech")
category.save()
# Обновление категории
category.is_active = False
category.save()
Результат выполнения кода:
"Creating new category: Technology"
"Updating category: Technology"
Смотрите также
-
сигнал
post_save,
который отправляется после сохранения объекта -
сигнал
pre_delete,
который отправляется перед удалением объекта -
метод
save,
который сохраняет объект в базу данных -
метод
clean,
который выполняет валидацию полей модели