Search code examples
djangodjango-modelsdjango-signals

Django - Update integer field value in one model by change in other


Here is a project I've created to practice, in my models.py,

class Post(models.Model):
   title = models.CharField(max_length = 140)
   author = models.ForeignKey(User, on_delete=models.CASCADE)
   votes = models.BigIntegerField(default=0, blank=True)


class Vote(models.Model):
   user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='voter')
   post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='vpost')

 @receiver(post_save, sender=Vote)
 def update_votes(sender, **kwargs):
    # # ??

Here I have a Voteform with that user can vote any particular post. That part works well.

Here is my question, whenever a user votes a particular post, I want votes field in Post model to increase as well.

I know I can show it with {{post.vpost.count}} in my html. But I want that increment here.

Other way I have tried,

class Vote(models.Model):
   user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='voter')
   post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='vpost')

 def update_votes(self):
     p = self.post
     p.votes += 1
     p.save()

This one only works once, not working from second time, so I want to use signal method. So how can I update the vote field in Post model using signal?


Solution

  • Nearly there. I would rename Post.votes to Post.votes_count as votes indicates a reverse relationship.

    @receiver(post_save, sender=Vote)
    def update_votes(sender, instance, **kwargs):
        post = instance.post
        post.votes_count += 1
        post.save()
    

    Although you might want to make sure that the count is correct, by introducing another query:

    @receiver(post_save, sender=Vote)
    def update_votes(sender, instance, **kwargs):
        post = instance.post
        post.votes_count = post.votes_set.all().count()
        post.save()
    

    You might also want to do this when/if a Vote is deleted to make sure the count is correct.

    Bear in mind you could also just do this in the Vote's save method instead of needing signals.

    You could also do this as a cronjob or task depending on your circumstances