← Назад к вопросам
Что такое post_save в Django?
1.3 Junior🔥 171 комментариев
#DevOps и инфраструктура#Django
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
post_save сигнал в Django
post_save — это Django сигнал, который автоматически отправляется после того, как модель сохранена в БД. Это позволяет выполнить дополнительные действия (например, отправить email, обновить кеш или создать сопутствующие объекты) без изменения кода сохранения.
Основная концепция
Джанго использует паттерн Observer через сигналы:
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=User) # Слушаем post_save для User
def create_user_profile(sender, instance, created, **kwargs):
"""Создает профиль пользователя при регистрации"""
if created: # Только при создании нового пользователя
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User) # Альтернативный способ
Параметры post_save
@receiver(post_save, sender=Post)
def handle_post_save(sender, instance, created, raw, using, update_fields, **kwargs):
"""
sender - модель которая отправила сигнал (Post)
instance - конкретный объект который был сохранен
created - True если объект создан, False если обновлен
raw - True если используется loaddata команда
using - имя БД
update_fields - набор полей которые были обновлены (или None)
"""
if created:
print(f'Создан новый пост: {instance.title}')
else:
print(f'Обновлен пост: {instance.title}')
Практический пример 1: Создание профиля при регистрации
# models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
bio = models.TextField(blank=True)
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
# signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
"""Автоматически создает профиль при регистрации пользователя"""
if created:
UserProfile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
"""Автоматически сохраняет профиль при сохранении пользователя"""
instance.profile.save()
# apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'myapp'
def ready(self): # Регистрируем сигналы когда app загружается
import myapp.signals
Практический пример 2: Отправка email при создании заказа
# models.py
class Order(models.Model):
STATUS_CHOICES = [('pending', 'В ожидании'), ('completed', 'Завершен')]
user = models.ForeignKey(User, on_delete=models.CASCADE)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
created_at = models.DateTimeField(auto_now_add=True)
total = models.DecimalField(max_digits=10, decimal_places=2)
# signals.py
from django.core.mail import send_mail
from django.template.loader import render_to_string
@receiver(post_save, sender=Order)
def send_order_confirmation(sender, instance, created, **kwargs):
"""Отправляет email подтверждение при создании заказа"""
if created:
subject = f'Заказ #{instance.id} подтвержден'
message = f'Ваш заказ на сумму {instance.total} создан'
send_mail(
subject,
message,
'noreply@example.com',
[instance.user.email]
)
Практический пример 3: Обновление кеша
from django.core.cache import cache
@receiver(post_save, sender=Product)
def invalidate_product_cache(sender, instance, created, **kwargs):
"""Очищает кеш при обновлении продукта"""
cache.delete(f'product_{instance.id}') # Удаляем кеш продукта
cache.delete('products_list') # Удаляем кеш списка продуктов
print(f'Очищен кеш для продукта {instance.id}')
Практический пример 4: Логирование изменений
from django.contrib.admin.models import LogEntry, ADDITION, CHANGE
@receiver(post_save, sender=Article)
def log_article_change(sender, instance, created, **kwargs):
"""Логирует создание и обновление статей"""
if created:
action_flag = ADDITION
action_type = 'Создана новая статья'
else:
action_flag = CHANGE
action_type = 'Статья обновлена'
LogEntry.objects.create(
content_type_id=ContentType.objects.get_for_model(Article).id,
object_id=instance.id,
object_repr=instance.title,
action_flag=action_flag,
change_message=f'{action_type}: {instance.title}'
)
Работа с update_fields
@receiver(post_save, sender=User)
def handle_partial_update(sender, instance, update_fields=None, **kwargs):
"""Обрабатывает частичные обновления"""
if update_fields is None:
# Полное сохранение
print('Сохранены все поля')
else:
# Частичное обновление
print(f'Обновлены поля: {update_fields}')
if 'email' in update_fields:
print(f'Email изменен на {instance.email}')
if 'first_name' in update_fields:
print(f'Имя изменено на {instance.first_name}')
# Использование:
user = User.objects.get(id=1)
user.email = 'new@example.com'
user.save(update_fields=['email']) # Сигнал получит update_fields=['email']
Отключение сигналов
Иногда нужно сохранить объект БЕЗ срабатывания сигналов:
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=User)
def expensive_operation(sender, instance, **kwargs):
print('Дорогая операция!')
# Например, отправка email, обновление кеша и т.д.
# Отключаем сигнал временно
post_save.disconnect(expensive_operation, sender=User)
user = User.objects.create(username='test') # Сигнал НЕ сработает
# Включаем обратно
post_save.connect(expensive_operation, sender=User)
Или используйте флаг:
class User(models.Model):
_skip_signals = False
# остальные поля
@receiver(post_save, sender=User)
def my_signal(sender, instance, **kwargs):
if instance._skip_signals:
return # Не выполняем операцию
print('Операция выполнена')
# Использование:
user = User.objects.get(id=1)
user._skip_signals = True
user.save() # Сигнал не сработает
Ошибка: Circular dependencies
# НЕПРАВИЛЬНО: бесконечный цикл!
@receiver(post_save, sender=Post)
def update_post(sender, instance, **kwargs):
instance.updated_count += 1
instance.save() # Это снова вызовет post_save!
# ПРАВИЛЬНО:
@receiver(post_save, sender=Post)
def update_post(sender, instance, created, **kwargs):
if created:
return # Только при создании
Post.objects.filter(id=instance.id).update(updated_count=F('updated_count')+1)
# Используем update() которая не вызывает сигналы
Pre_save vs Post_save
from django.db.models.signals import pre_save, post_save
@receiver(pre_save, sender=User)
def validate_before_save(sender, instance, **kwargs):
"""Выполняется ДО сохранения"""
if instance.age < 18:
raise ValueError('Возраст должен быть 18+')
@receiver(post_save, sender=User)
def action_after_save(sender, instance, **kwargs):
"""Выполняется ПОСЛЕ сохранения"""
print(f'Пользователь {instance.username} сохранен в БД')
Все доступные Django сигналы
from django.db.models.signals import (
pre_save, # Перед сохранением
post_save, # После сохранения
pre_delete, # Перед удалением
post_delete, # После удалением
m2m_changed # Изменение Many-to-Many
)
from django.core.signals import (
request_started, # Начало request
request_finished # Конец request
)
Производительность
Сигналы могут замедлить код если в них тяжелые операции:
# ПЛОХО: долгая операция в сигнале
@receiver(post_save, sender=Order)
def process_payment(sender, instance, **kwargs):
# Это блокирует весь request!
charge_credit_card(instance.user) # 5 секунд ждем
# ХОРОШО: используйте Celery для асинхронных задач
from celery import shared_task
@shared_task
def async_process_payment(order_id):
order = Order.objects.get(id=order_id)
charge_credit_card(order.user)
@receiver(post_save, sender=Order)
def trigger_payment(sender, instance, **kwargs):
async_process_payment.delay(instance.id) # Асинхронно
Итого
post_save используется для:
- Создания сопутствующих объектов
- Отправки уведомлений
- Обновления кеша
- Логирования
- Синхронизации данных
Минусы:
- Неявная логика (сложнее отладить)
- Может замедлить код
- Риск бесконечных циклов
- Не срабатывает при bulk update/delete
Альтернативы:
- Manager методы
- Model методы (overridе save())
- Celery для асинхронных задач
- Webhooks для интеграций
post_save мощный инструмент, но используйте его разумно и не для сложной бизнес-логики.