'How to create a voting model in Django?
I have a model called posts which users can vote up. If the user has voted a post up, he/she can only vote it down after that. How do I implement this system?
My current models looks something like this:
class Post(models.Model):
title = models.CharField(max_length = 140)
user = models.ForeignKey('Auth.user')
votes = models.IntegerField(default = 0)
Is it better to make another model called Vote and link that up to Post, and if so, how do I do it in a way that will scale with a large number of users?
Solution 1:[1]
The case you explained, says that a post can only take up votes.
And as you need to hold who has voted up, we need to keep vote and it's user
class Post(models.Model):
title = models.CharField(max_length = 140)
user = models.ForeignKey('Auth.user')
def votes_count(self):
return self.votes.all().count()
class Vote(models.Model):
class Meta:
unique_together = [('post', 'user')]
post = models.ForeignKey(Post, related_name='votes')
user = models.ForeignKey('auth.User')
Solution 2:[2]
My answer is similar to that of @doniyor's. But, have added some more features :
- User cant upvote or downvote posts more than once
- When a post has been downvoted previously, it can be upvoted as well. To ensure this, I add +2 to
Post.votes. Similarly, the vice-versa also holds. - Have used vote_type as a bool variable to save some memory!
class Post(models.Model):
title = models.CharField(max_length=200)
text = models.TextField()
votes = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def upvote(self, user):
try:
self.vote_set.create(user=user, post=self, vote_type=True)
self.votes += 1
self.save()
except IntegrityError:
vote = self.vote_set.filter(user=user)
if vote[0].vote_type == False:
vote.update(vote_type=True)
self.votes += 2
self.save()
else:
return 'already_upvoted'
return 'ok'
def downvote(self, user):
try:
self.vote_set.create(user=user, post=self, vote_type=False)
self.votes -= 1
self.save()
except IntegrityError:
vote = self.vote_set.filter(user=user)
if vote[0].vote_type == True:
vote.update(vote_type=False)
self.votes -= 2
self.save()
else:
return 'already_downvoted'
return 'ok'
class Vote(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
vote_type = models.BooleanField()
class Meta:
unique_together = ('user', 'post')
Examples:
u = User.objects.get(pk=1)
p1 = Post.objects.get(pk=7)
>>> p1.votes
3
>>> p1.upvote(u)
'ok'
>>> p1.votes
4
>>> p1.upvote(u)
'already_upvoted'
>>> p1.downvote(u)
'ok'
>>> p1.votes
2
>>> p1.downvote(u)
'already_downvoted'
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | kia |
| Solution 2 | Muhammad Dyas Yaskur |
