问题描述:

I have an model called Contact that has a M2M relationship called tags. The model has a couple of booleanfields (in this example student, alumus and employee).

I want to achieve the following:

After each save of an Contact object, I want to check if for each booleanfield, a tags relationship exists. If it doesn't exist, it should be added.

I thought that this would work with a post_save hook, this is my code:

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)

I noticed however that this only works if I don't include my tags field in the ModelForm object. If it is included, all updates are ignored. If it isn't included, everything works as expected.

I did some research and found that apparently m2m relationships are quite different:

When you save a model via admin forms it's not an atomic transaction.

The main object gets saved first (to make sure it has a PK), then the

M2M is cleared and the new values set to whatever came out of the

form. So if you are in the save() of the main object you are in a

window of opportunity where the M2M hasn't been updated yet. In fact,

if you try to do something to the M2M, the change will get wiped out

by the clear().

However, since I am not using the admin form, I don't understand why this also occurs in my case. Does anybody know how I can solve my problem?

网友答案:

It's not just in admin forms, this happens for when you save any Django model anywhere.

I'm not finding any good doc links to the "why", but I believe it has to do with the database structure. In Django it's stored in a single model, but on the database level it creates an intermediate table between Contact and Tag (which is the proper way to do it on the database level) - it's just hidden when inside Django.

Instead of using the post_save signal, you need to use m2m_changed signal, which will fire when the ManyToMany field is changed.

相关阅读:
Top