【发布时间】:2018-03-28 16:01:33
【问题描述】:
我想在多对多关系发生变化时触发一些行为,但我不确定哪种信号设置最适合捕获由于删除关系的一侧而导致的关系变化。 m2m_changed 在这种情况下似乎不会触发,常规的 post_save 和 post_delete 信号似乎也不适用于 through 模型?
我目前的解决方案是在构成关系任一侧的模型上收听pre_delete,然后清除该信号中的关系以触发m2m_changed。我很惊讶我必须这样做,并且觉得我有问题。
我在这里缺少什么?如果我没有遗漏任何东西,为什么这是必要的(即为什么默认情况下没有发出这样的信号)?
代码示例:
class ResearchField(models.Model):
name = models.CharField(unique=True, max_length=200)
class Researcher(models.Model):
name = models.CharField(max_length=200)
research_fields = models.ManyToManyField(ResearchField, blank=True)
@receiver(m2m_changed, sender=Researcher.research_fields.through)
def research_fields_changed(sender, instance, action, **kwargs):
# need to do something important here
print('m2m_changed', action)
volcanology = ResearchField.objects.create(name='Volcanology')
researcher = Researcher.objects.create(name='A. Volcanologist')
researcher.research_fields.add(volcanology)
>>> m2m_changed pre_add
>>> m2m_changed post_add
当关系从任一方删除时,此m2m_changed 信号会按预期触发:
researcher.research_fields.remove(volcanology)
# or equally volcanology.researcher_set.remove(researcher)
>>> m2m_changed pre_remove
>>> m2m_changed post_remove
但是,尽管 Django delete 输出表明 through 模型的实例已被删除,但如果我只是删除关系的一侧,则不会触发 pre_remove 或 post_remove m2m_changed 信号:
# with the relationship intact
volcanology.delete()
>>> (2, {'ResearchField': 1, 'Researcher_research_fields': 1})
此时我尝试了:
@receiver(post_delete, sender=Researcher.research_fields.through)
def through_model_deleted(sender, instance, **kwargs):
print('through model deleted')
但这永远不会触发?
因此,我目前的解决方案是:
@receiver(pre_delete, sender=ResearchField)
def research_field_deleted(sender, instance, **kwargs):
instance.researcher_set.clear()
要自定义强制当Researcher.research_fields.through 对象通过级联从已删除的ResearchField 中删除时,m2m_changed 信号毕竟会触发。但是,正如我在顶部所说的那样,我感觉我错过了一些必要的东西?
【问题讨论】:
标签: django django-signals manytomanyfield