Post_save signal and relationship

I use the post_save signal to apply user rights for each object, and then filter the request accordingly.

My model looks like this:

class Project(models.Model): # Relations with other entities. employees = models.ManyToManyField('staff.Person', through='project.PersonProjectMembership', related_name='projects') research_groups = models.ManyToManyField('group.Group', related_name='projects', through='project.ProjectGroupMembership') departments = models.ManyToManyField('department.Department', related_name='projects', through='project.ProjectDepartmentMembership') 

The problem is this: when I catch a signal after saving, although I have entered values โ€‹โ€‹for departments, research groups, and employees, they always appear empty. Is there something I missed?

Update: below the current code, which is not working properly so far. I changed post_save to m2m_changed.

 from django.db.models.signals import m2m_changed from django.db import models from django.dispatch.dispatcher import receiver class Project(models.Model): employees = models.ManyToManyField('staff.Person', through='project.PersonProjectMembership', related_name='projects') class PersonProjectMembership(models.Model): project = models.ForeignKey('project.Project', related_name="person_memberships") person = models.ForeignKey('staff.Person', related_name="project_memberships") lead = models.BooleanField(default=False) position = models.CharField(max_length=50) project_manager = models.BooleanField( default=False ) class Meta: permissions = ( ('view_personprojectmembership', _('View person project membership')), ) @receiver(m2m_changed, sender=Project.employees.through) def _on_save_project_assign_privileges(sender, instance, action, reverse, model, pk_set, using, **kwargs): # [...] 

DECISION

In my project model, I explicitly define PersonProjectMembership as an intermediate model in m2m relationships:

 class Project(models.Model): # Relations with other entities. employees = models.ManyToManyField('staff.Person', through='project.PersonProjectMembership', related_name='projects') 

The timeline when saving the project is as follows:

  • Project.save ()
  • PersonProjectMembership.save ()

So, it's normal that Project.post_save employees are still empty. I needed to listen to the post_save PersonProjectMembership message:

 @receiver(post_save, sender=PersonProjectMembership) def my_listener(**kwargs): # do stuff [...] 

Take a look at https://docs.djangoproject.com/en/1.9/ref/models/fields/#django.db.models.ManyToManyField.through and https://docs.djangoproject.com/en/1.9/topics/signals/

+4
source share
2 answers

After repeatedly digging my code and the simplest tests, I tried the m2m_changed signal, as the Krasnopers suggested. I realized that this signal does not work if you explicitly declare a "pass-through" model "PersonProjectMembership" in my case.

Then I thought again and connected my method with the post_save signal "PersonProjectMembership". This works great.

+1
source

As you know, Many2Many relationships are stored through an additional table that contains primary keys at both ends of the relationship. Because of this, saving a model instance with the Many2Many relationship is done in two steps:

  • First, the instance is saved: a new record is created in the database and the instance receives its Primary Key. post_save starts at this point.

  • After that, the relationship is saved: records are created in the relationship table. At this moment, the m2m-changed signal is triggered.

In other words, when post_save fired, m2m relationships are not yet processed.

You can check the documentation: m2m-changed

+1
source

Source: https://habr.com/ru/post/1486632/


All Articles