РЕПЕТИТОР математика физика информатика
Для школьников и студентов. Подтягивание пробелов. ЦЭ, ЦТ, ОГЭ, ЕГЭ.
Идет набор на ЛЕТО. Жмите для подробностей:)
170 of 818 menu

Сигнал m2m_changed

Сигнал m2m_changed отправляется при изменении связи ManyToManyField. Он позволяет выполнять дополнительные действия до или после добавления, удаления или очистки связанных объектов. Сигнал принимает несколько параметров: отправитель, экземпляр модели, действие, обратный набор моделей, идентификаторы объектов и флаг инициализации.

Параметры сигнала

Сигнал m2m_changed передает следующие аргументы в функцию-обработчик:

⁅b⁆sender⁅/b⁆ - класс модели, которая содержит ManyToManyField.

⁅b⁆instance⁅/b⁆ - экземпляр модели, для которого изменяется связь.

⁅b⁆action⁅/b⁆ - строка, указывающая тип операции: pre_add, post_add, pre_remove, post_remove, pre_clear или post_clear.

⁅b⁆reverse⁅/b⁆ - булево значение, указывающее, является ли операция обратной (через связанную модель).

⁅b⁆model⁅/b⁆ - класс модели, которая добавляется или удаляется.

⁅b⁆pk_set⁅/b⁆ - множество идентификаторов объектов, которые участвуют в операции.

⁅b⁆using⁅/b⁆ - псевдоним базы данных, на которой выполняется операция.

Синтаксис

from django.db.models.signals import m2m_changed from django.dispatch import receiver @receiver(m2m_changed, sender=Model.m2m_field.through) def handler(sender, instance, action, reverse, model, pk_set, using, **kwargs): # код обработчика pass

Важно указывать sender как промежуточную модель через атрибут through поля ManyToManyField.

Пример

Создадим модели Article и Tag со связью ManyToMany, и добавим обработчик, который выводит информацию о добавлении тегов:

from django.db import models from django.db.models.signals import m2m_changed from django.dispatch import receiver class Tag(models.Model): name = models.CharField(max_length=50) def __str__(self): return self.name class Article(models.Model): title = models.CharField(max_length=100) tags = models.ManyToManyField(Tag) def __str__(self): return self.title @receiver(m2m_changed, sender=Article.tags.through) def article_tags_changed(sender, instance, action, reverse, model, pk_set, using, **kwargs): if action == 'post_add' and pk_set: tag_names = Tag.objects.filter(pk__in=pk_set).values_list('name', flat=True) print(f"Added tags to article '{instance.title}': {list(tag_names)}")

Теперь при добавлении тегов к статье сработает сигнал и выведет информацию:

from myapp.models import Article, Tag article = Article.objects.create(title="Django signals") tag1 = Tag.objects.create(name="django") tag2 = Tag.objects.create(name="python") article.tags.add(tag1, tag2)

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

"Added tags to article 'Django signals': ['django', 'python']"

Пример

Используем сигнал для предотвращения удаления последнего тега у статьи. Если у статьи остается только один тег, запретим его удаление:

from django.db import models from django.db.models.signals import m2m_changed from django.dispatch import receiver from django.core.exceptions import ValidationError class Tag(models.Model): name = models.CharField(max_length=50) def __str__(self): return self.name class Article(models.Model): title = models.CharField(max_length=100) tags = models.ManyToManyField(Tag) def __str__(self): return self.title @receiver(m2m_changed, sender=Article.tags.through) def prevent_last_tag_removal(sender, instance, action, reverse, model, pk_set, using, **kwargs): if action == 'pre_remove' and pk_set: current_tags = instance.tags.count() removing_count = len(pk_set) if current_tags - removing_count < 1: raise ValidationError("Article must have at least one tag")

При попытке удалить последний тег будет вызвана ошибка:

from myapp.models import Article, Tag article = Article.objects.create(title="My article") tag = Tag.objects.create(name="important") article.tags.add(tag) # Попытка удалить единственный тег article.tags.remove(tag)

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

"ValidationError: Article must have at least one tag"

Пример

Обработаем событие очистки всех тегов у статьи с помощью действия pre_clear:

from django.db import models from django.db.models.signals import m2m_changed from django.dispatch import receiver class Tag(models.Model): name = models.CharField(max_length=50) def __str__(self): return self.name class Article(models.Model): title = models.CharField(max_length=100) tags = models.ManyToManyField(Tag) def __str__(self): return self.title @receiver(m2m_changed, sender=Article.tags.through) def log_tag_clear(sender, instance, action, reverse, model, pk_set, using, **kwargs): if action == 'pre_clear': tag_count = instance.tags.count() print(f"Clearing all {tag_count} tags from article '{instance.title}'") elif action == 'post_clear': print(f"All tags cleared from article '{instance.title}'")

Теперь при очистке тегов сработает сигнал и выведет информацию:

from myapp.models import Article, Tag article = Article.objects.create(title="Test article") article.tags.add(Tag.objects.create(name="tag1")) article.tags.add(Tag.objects.create(name="tag2")) article.tags.clear()

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

"Clearing all 2 tags from article 'Test article'" "All tags cleared from article 'Test article'"

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

  • сигнал pre_save,
    который отправляется перед сохранением модели
  • сигнал post_save,
    который отправляется после сохранения модели
  • сигнал pre_delete,
    который отправляется перед удалением модели
  • сигнал post_delete,
    который отправляется после удаления модели
Мы используем cookie для работы сайта, аналитики и персонализации. Обработка данных происходит согласно Политике конфиденциальности.
принять все настроить отклонить