I am applying the post_save signal to apply user rights per object, and then filter the queryset accordingly.
My model is 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: when I catch the post-save signal, although I have entered values for departments, research_groups & employees, they always seem to be empty. Is there anything that I have missed?
Update: Below the current code, which is not yet working as expected. I have changed the post_save by 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):
# [...]
SOLUTION
In my Project model, I am explicitly defining PersonProjectMembership as intermediate model in the m2m relation employees:
class Project(models.Model):
# Relations with other entities.
employees = models.ManyToManyField('staff.Person', through='project.PersonProjectMembership', related_name='projects')
The timeline when I save a project, is as follows:
So it's normal that, on Project.post_save employees is still empty. What I had to do was listening to the PersonProjectMembership post_save signal:
@receiver(post_save, sender=PersonProjectMembership)
def my_listener(**kwargs):
# do stuff [...]
Look into 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/
After digging a lot on my code, and doing simple tests, I tried the m2m_changed signal as krasnoperov suggested. I realised that this signal does not work well if you explicitly declare a "through" model, "PersonProjectMembership" in my case.
Then I thought again and linked my method to the post_save signal of "PersonProjectMembership". That works perfectly.
As you might know, Many2Many relations are stored through additional table, which contains Primary Keys from both ends of relation. Because of that, saving of model instance with Many2Many relation it is two steps process:
At first, instance is save: new record in database is created and instance receives it's Primary Key. post_save
is fired at this moment.
After that, relations are saved: records in relations table are created. m2m-changed
signal is fired at this moment.
In other words, when post_save
is fired, m2m relations are not handled yet.
You can check the documentation: m2m-changed
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With