Django Many to Many Field Set in Model Save Method

I am trying to override the save method in a model with logic to update a couple of many to many fields. Using print statements I can see values updating as expected but the values are not persisted after save.

In the below model the change_access_flag is changing as expected with a signal, the prints are executing with the appropriate values, but the allowed_departments and allowed_communities fields are not updating with the printed values.

Model

class Person(models.Model):

    user = models.OneToOneField(User, on_delete=models.CASCADE)
    full_name = models.CharField(null=True, blank=True, max_length=50)
    payroll_id = models.CharField(null=True, max_length=20)
    position = models.ForeignKey(Position, null=True, on_delete=models.SET_NULL)
    primary_community = models.ForeignKey(Community, null=True, on_delete=models.CASCADE, related_name="primary_community")
    region = models.CharField(max_length=2, choices=RegionChoices.choices, blank=True, null=True)
    allowed_communities = models.ManyToManyField(Community, blank=True, related_name="allowed_community")
    allowed_departments = models.ManyToManyField(Department, blank=True)
    access_change_flag = models.BooleanField(default=False)

    def __str__(self):
        return f'{self.user.first_name} {self.user.last_name}'

    class Meta:
        verbose_name_plural = "People"
        ordering = ['position__position_code', 'user__last_name', 'full_name']

    def save(self, *args, **kwargs):
        #Set Full Name field
        if self.user.last_name:
            self.full_name = f'{self.user.first_name} {self.user.last_name}'
        
        super().save(*args, **kwargs)

        #Change flag set in signals, set for events that require updating access settings
        if self.access_change_flag:
            self.access_change_flag = False

            #Allowed community access
            access_level = self.position.location_access_level
            self.allowed_communities.clear()
            if access_level == 'R':
                if self.primary_community.community_name == '#':
                    region = self.region
                else:
                    region = self.primary_community.region
                if region is not None:
                    communities = Community.objects.filter(region=region)
                    self.allowed_communities.set(communities)
                self.allowed_communities.add(self.primary_community)
            elif access_level == 'A':
                communities = Community.objects.filter(active=True)
                self.allowed_communities.set(communities)
            else:
                communities = self.primary_community
                self.allowed_communities.add(communities)

            print(self.allowed_communities.all())

            #Allowed department access
            dept_access = self.position.department_only_access
            if dept_access:
                depts = [self.position.department]
            else:
                depts = Department.objects.filter(active=True)
            self.allowed_departments.set(depts)

            print(self.allowed_departments.all())
            super().save(*args, **kwargs)

I have tried variations of set, clear, add, moving the super.save() around, and placing the logic in a signal but nothing seems to work. I have tested initiating save from both a model form through a view and admin.

Let me answer in quotes. You can find the source in this section.

If you wish to update a field value in the save() method, you may also want to have this field added to the update_fields keyword argument. This will ensure the field is saved when update_fields is specified.

Also read here

Specifying update_fields will force an update.

So try to call the super().save(*args, **kwargs) method at the end with defining the argument update_fields. This will force the update of your model regarding the specified fields.

Let me know how it goes.

Back to Top