Сигнал post_init

Сигнал post_init относится к встроенным сигналам Django и находится в модуле django.db.models.signals. Он отправляется после того, как экземпляр модели был создан и все его поля были инициализированы значениями. Этот сигнал полезен для выполнения дополнительной настройки объекта, которая должна происходить сразу после его создания, независимо от того, загружен ли объект из базы данных или создан в коде. Сигнал post_init не передаёт никаких дополнительных аргументов, кроме самого экземпляра модели и имени отправителя.

Синтаксис

pythonfrom django.db.models.signals import post_init
from django.dispatch import receiver

@receiver(post_init, sender=MyModel)
def my_handler(sender, instance, **kwargs):
  # Действия после инициализации
  pass

Параметры обработчика

Функция-обработчик сигнала post_init принимает следующие параметры:

Пример . С обработкой после загрузки из базы

Рассмотрим модель Article, у которой есть поле word_count. После инициализации объекта мы хотим автоматически подсчитывать количество слов в тексте статьи:

pythonfrom django.db import models
from django.db.models.signals import post_init
from django.dispatch import receiver

class Article(models.Model):
  title = models.CharField(max_length=100)
  content = models.TextField()
  word_count = models.IntegerField(default=0)

  def __str__(self):
    return self.title

@receiver(post_init, sender=Article)
def article_post_init(sender, instance, **kwargs):
  if instance.content:
    words = instance.content.split()
    instance.word_count = len(words)
    print(f"Article '{instance.title}' has {instance.word_count} words")

Теперь при создании нового экземпляра Article сразу после инициализации будет выполняться подсчёт слов:

pythonfrom myapp.models import Article

article = Article(
  title="My First Article",
  content="This is the content of the first article."
)
print(article.word_count)

Результат выполнения кода:

python"Article 'My First Article' has 8 words"
"8"

Пример . С модификацией атрибутов

Сигнал post_init можно использовать для добавления динамических атрибутов экземпляру. Например, добавим атрибут full_name для модели User:

pythonfrom django.db import models
from django.db.models.signals import post_init
from django.dispatch import receiver

class User(models.Model):
  first_name = models.CharField(max_length=50)
  last_name = models.CharField(max_length=50)
  email = models.EmailField()

  def __str__(self):
    return f"{self.first_name} {self.last_name}"

@receiver(post_init, sender=User)
def user_post_init(sender, instance, **kwargs):
  instance.full_name = f"{instance.first_name} {instance.last_name}"

После инициализации каждый экземпляр будет содержать дополнительный атрибут full_name:

pythonfrom myapp.models import User

user = User(first_name="John", last_name="Doe", email="john@example.com")
print(user.full_name)

Результат выполнения кода:

python"John Doe"

Пример . С кешированием вычисляемых данных

Сигнал post_init полезен для предварительного вычисления и кеширования данных, которые часто используются в бизнес-логике. Рассмотрим модель Product с вычисляемым рейтингом:

pythonfrom django.db import models
from django.db.models.signals import post_init
from django.dispatch import receiver

class Product(models.Model):
  name = models.CharField(max_length=100)
  price = models.DecimalField(max_digits=10, decimal_places=2)
  rating = models.FloatField(default=0.0)
  review_count = models.IntegerField(default=0)

  def __str__(self):
    return self.name

@receiver(post_init, sender=Product)
def product_post_init(sender, instance, **kwargs):
  if instance.review_count > 0:
    instance.cached_rating = instance.rating
  else:
    instance.cached_rating = None

  instance.discount_price = instance.price * 0.9

Теперь при обращении к экземпляру Product доступны кешированные значения:

pythonfrom myapp.models import Product

product = Product(
  name="Laptop",
  price=1000.00,
  rating=4.5,
  review_count=10
)
print(product.cached_rating)
print(product.discount_price)

Результат выполнения кода:

python"4.5"
"900.00"

Важные особенности

Сигнал post_init имеет несколько важных особенностей, которые следует учитывать:

Смотрите также

  • сигнал pre_init,
    который отправляется до инициализации экземпляра
  • сигнал pre_save,
    который отправляется перед сохранением объекта
  • сигнал post_save,
    который отправляется после сохранения объекта
  • метод refresh_from_db,
    который обновляет объект из базы данных