← Назад к вопросам

Что такое pre_save в Django?

1.0 Junior🔥 21 комментариев
#DevOps и инфраструктура#Django

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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 — это мощный инструмент для централизованного управления логикой обновления моделей.