在model_save之后更新M2M值

我有一个名为Contact的模型,它具有称为标签的M2M关系 . 该模型有两个booleanfields(在本例中为student,alumus和employee) .

我想实现以下内容:在每次保存Contact对象之后,我想检查对于每个booleanfield,是否存在标记关系 . 如果它不存在,则应添加它 .

我认为这适用于post_save钩子,这是我的代码:

models.py

class Contact(BaseModel):
    title = models.CharField(max_length=30, blank=True)
    student = models.BooleanField(default=False)
    alumnus = models.BooleanField(default=False)
    employee = models.BooleanField(default=False)
    tags = models.ManyToManyField(Tag)

def update_tag(instance, tag_name, tagged):
    tag, created = Tag.objects.get_or_create(name=tag_name, defaults={'deletable': False})
    if tagged:
        instance.tags.add(tag)
    else:
        instance.tags.remove(tag)

@receiver(post_save, sender=Contact, dispatch_uid="update_tags")
def update_tags(sender, instance, **kwargs):
    update_tag(instance, "Alumni", instance.alumnus)
    update_tag(instance, "Students", instance.student)
    update_tag(instance, "Employees", instance.employee)

但是我注意到这只有在我没有在ModelForm对象中包含我的标签字段时才有效 . 如果包含,则忽略所有更新 . 如果没有包含,一切都按预期工作 .

我做了一些研究和found,显然m2m关系是完全不同的:

当您通过管理表单保存模型时,它不是原子事务 . 首先保存主对象(以确保它具有PK),然后清除M2M并将新值设置为表单中出现的任何值 . 因此,如果您在主对象的save()中,那么您将处于尚未更新M2M的机会窗口中 . 实际上,如果您尝试对M2M执行某些操作,则clear()将会消除此更改 .

但是,由于我没有使用管理员表单,我不明白为什么在我的情况下也会出现这种情况 . 有谁知道如何解决我的问题?

回答(1)

2 years ago

它不只是在管理表单中,当你在任何地方保存任何Django模型时都会发生这种情况 .

我'm not finding any good doc links to the 2778070 , but I believe it has to do with the database structure. In Django it'存储在一个模型中,但在数据库级别它创建了一个介于 ContactTag 之间的中间表(这是在数据库级别上执行它的正确方法) - 它只是在Django内部时隐藏 .

您需要使用m2m_changed信号,而不是使用 post_save 信号,该信号将在更改ManyToMany字段时触发 .