Nilesh Dubey
Nilesh Dubey

Reputation: 13

Django: Count of ManyToMany field in PositiveIntegerField during save() in a models.py

I want to alter the count of a ManyToMany Field during save of my model. For which I modified the save(). If I run save(), count will not be updated. If I run save second time without updating the field it will update the count.

class UserProfile(models.Model):

    follower = models.ManyToManyField('self', related_name='Followers',
        blank=True, symmetrical=False)
    following = models.ManyToManyField('self', related_name='Followings',
        blank=True, symmetrical=False)
    follower_count = models.PositiveIntegerField(null=True, blank=True,
        editable=False)
    following_count = models.PositiveIntegerField(null=True, blank=True,
        editable=False)

    class Meta:
        verbose_name = 'User Profile'
        verbose_name_plural = 'User Profiles'

    def __str__(self):
        return self.follower_count

    def save(self, *args, **kwargs):
        super(UserProfile, self).save(*args, **kwargs)
        self.following_count = self.following.all().count()

        self.follower_count = self.follower.all().count()
        super(UserProfile, self).save(*args, **kwargs)

I want to update count with save().

Upvotes: 1

Views: 327

Answers (3)

Iain Shelvington
Iain Shelvington

Reputation: 32274

If performance is not an issue both fields can be turned into properties that calculate the value on every access

class UserProfile(models.Model):

    follower = models.ManyToManyField('self', related_name='Followers',
        blank=True, symmetrical=False)
    following = models.ManyToManyField('self', related_name='Followings',
        blank=True, symmetrical=False)

    @cached_property
    def follower_count(self):
        return self.follower.count()

    @cached_property
    def following_count(self):
        return self.following.count()

Upvotes: 0

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477190

You can omit the first save, like:

def save(self, *args, **kwargs):
    self.following_count = self.following.count()
    self.follower_count = self.follower.count()
    super(UserProfile, self).save(*args, **kwargs)

That being said, I'm not entirely sure that the above is a good idea for several reasons:

  1. it makes "saving" a computationally expensive function, since you make two extra aggregates;
  2. it is likely that between two saves, the number of followers/followings will not change;
  3. if you remove/add followers/followings, you do not trigger the number of followers/followings automatically. Therefore at certain times, the counts will not be correct.

Upvotes: 1

ma_dev_15
ma_dev_15

Reputation: 1196

 def save(self, *args, **kwargs):
        self.following_count = self.following.all().count()
        super(UserProfile, self).save(*args, **kwargs)

Get the count first and then call save method

Upvotes: 1

Related Questions