Что такое pre_save в Django?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
pre_save в Django: Сигнал до сохранения модели
pre_save — это Django сигнал (signal), который срабатывает перед сохранением объекта модели в базу данных. Это мощный механизм для выполнения кода, который должен быть выполнен именно перед сохранением, например валидация, преобразование данных или логирование.
Что такое Django сигналы?
Сигналы в Django — это механизм, позволяющий компонентам приложения общаться друг с другом, не создавая жёсткие связи (coupling) между ними. Они работают по принципу Observer pattern (наблюдатель).
Как работает pre_save?
from django.db.models.signals import pre_save
from django.dispatch import receiver
from .models import User
@receiver(pre_save, sender=User)
def set_username_lowercase(sender, instance, **kwargs):
instance.username = instance.username.lower()
Функция set_username_lowercase будет вызвана перед сохранением любого User объекта. Это позволяет изменить данные перед их сохранением в БД.
Когда использовать pre_save?
Преобразование данных:
from django.db.models.signals import pre_save
from django.dispatch import receiver
from .models import Product
@receiver(pre_save, sender=Product)
def update_slug(sender, instance, **kwargs):
from django.utils.text import slugify
instance.slug = slugify(instance.name)
Логирование изменений:
@receiver(pre_save, sender=User)
def log_user_update(sender, instance, **kwargs):
if instance.pk: # Если это обновление, не создание
old_instance = User.objects.get(pk=instance.pk)
if old_instance.email != instance.email:
print(f"Email changed from {old_instance.email} to {instance.email}")
Установка значений по умолчанию:
from datetime import datetime
@receiver(pre_save, sender=Article)
def set_updated_at(sender, instance, **kwargs):
instance.updated_at = datetime.now()
Важное отличие: pre_save vs post_save
- pre_save — срабатывает ДО сохранения в БД, изменения ещё можно применить
- post_save — срабатывает ПОСЛЕ сохранения, объект уже в БД
from django.db.models.signals import pre_save, post_save
@receiver(pre_save, sender=User)
def before_save(sender, instance, **kwargs):
print(f"About to save: {instance.username}")
@receiver(post_save, sender=User)
def after_save(sender, instance, created, **kwargs):
if created:
print(f"Created new user: {instance.username}")
else:
print(f"Updated user: {instance.username}")
Как регистрировать сигналы
Способ 1: Через @receiver декоратор (рекомендуется):
from django.db.models.signals import pre_save
from django.dispatch import receiver
from .models import MyModel
@receiver(pre_save, sender=MyModel)
def my_signal_handler(sender, instance, **kwargs):
print("Signal received")
Способ 2: Вручную в AppConfig:
from django.apps import AppConfig
class MyAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'myapp'
def ready(self):
import myapp.signals # Импортируем сигналы при инициализации приложения
Производительность и осторожность
Сигналы могут привести к проблемам производительности если использовать их неправильно:
# Плохо: тяжёлые операции в pre_save
@receiver(pre_save, sender=User)
def expensive_operation(sender, instance, **kwargs):
result = expensive_calculation() # Блокирует сохранение
instance.result = result
Лучше: использовать Celery или другие асинхронные решения для тяжёлых операций.
Получение старых значений
@receiver(pre_save, sender=User)
def check_email_change(sender, instance, **kwargs):
try:
old_instance = sender.objects.get(pk=instance.pk)
if old_instance.email != instance.email:
instance.email_changed = True
except sender.DoesNotExist:
pass # Это новый объект
pre_save — это мощный инструмент для централизованного управления логикой обновления моделей.