from django.db import models from django.db.models import Sum, F from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericRelation from django.contrib.contenttypes.models import ContentType from django.utils.translation import ugettext_lazy as _ class UnDVotes(models.Model): """ Up and down vote model """ #: username username = models.CharField(max_length=255, null=False) #: Score of the vote score = models.IntegerField(default=1) # Django generic relation content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() #: The Voted object content_object = GenericForeignKey("content_type", "object_id") class Meta: verbose_name = _("Vote") verbose_name_plural = _("Votes") def __str__(self): return "{}:{}:{}".format( self.username, self.content_object, self.score) class UnDVotedMixin(models.Model): """ A mixin to attach to a model that has up and down votes """ #: Votes und_votes = GenericRelation(UnDVotes) #: Score of the model und_score = models.IntegerField(default=0) und_score_up = models.IntegerField(default=0) und_score_down = models.IntegerField(default=0) class Meta: abstract = True def upvote(self, username): diff_score = 0 try: # Already voted content vote = self.und_votes.get(username=username) if vote.score == 1: # Cancel previous upvote vote.delete() diff_score = -1 else: # Previously downvoted vote.score = 1 vote.save() diff_score = 2 except: vote = UnDVotes(content_object=self, username=username, score=1) vote.save() diff_score = 1 self.und_score += diff_score self.und_score_up += diff_score # Update self score, use update and filter to avoid triggering signals self.__class__.objects.filter(id=self.id).update( und_score=F("und_score") + diff_score) def downvote(self, username): diff_score = 0 try: # Already voted content vote = self.und_votes.get(username=username) if vote.score == -1: # Cancel previous downvote vote.delete() diff_score = 1 else: # Previously upvoted vote.score = -1 vote.save() diff_score = -2 except: vote = UnDVotes(content_object=self, username=username, score=-1) vote.save() diff_score = -1 self.und_score += diff_score self.und_score_down += diff_score # Update self score, use update and filter to avoid triggering signals self.__class__.objects.filter(id=self.id).update( und_score=F("und_score") + diff_score) def update_und_score(self): """Reset score to the correct count (should not be necessary)""" score_up = self.und_votes.filter(score=1).count() score_down = self.und_votes.filter(score=-1).count() score = score_up + score_down self.und_score_up = score_up self.und_score_down = score_down self.und_score = score # Update self score, use update and filter to avoid triggering signals self.__class__.objects.filter(id=self.id).update(und_score=score)