首页 > 解决方案 > 导致未知麻烦的投票模型操作

问题描述

我做了一个下面给出的投票模型。我的博客模型有两个操作来支持或反对特定的博客。例如,当单个用户投票时,Blog 中的投票字段会增加,并且 UserVote 模型有记录,当同一用户投票时,它会更改其 vote_type 并更新记录。

当第二个用户投票时,问题就出现了。在一个 upvote 之后,当第二个用户 downvote 时,它​​表示他已经投票(尽管它应该将他的 vote_type 更改为 downvote)

我一直在解决这个问题,但在任何地方都找不到解决方案。

class Blog(models.Model):
title = models.CharField(max_length=100, blank=True, default='')
description = models.TextField(max_length=1500)
created = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey('auth.User', related_name='blog', on_delete=models.CASCADE, null=True)
image = models.FileField(blank=True, null=True)
votes = models.IntegerField(default=0)

def upvote(self, user):
    try:
        if UserVote.objects.filter(user=user, blog=self, vote_type='down').exists():
            self.post_votes.update(user=user, blog=self, vote_type='up')
            self.votes += 2

        else:
            self.post_votes.create(user=user, blog=self, vote_type='up')
            self.votes += 1

        self.save()

    except IntegrityError:
        return 'already_voted'

    return 'ok'

def downvote(self, user):
    try:
        if UserVote.objects.filter(user=user, blog=self, vote_type='up').exists():
            self.post_votes.update(user=user, blog=self, vote_type='down')
            self.votes -= 2

        else:
            self.post_votes.create(user=user, blog=self, vote_type='down')
            self.votes -= 1

        self.save()

    except IntegrityError:
        return 'already_voted'

    return 'ok'

class UserVote(models.Model):
user = models.ForeignKey('auth.User', related_name='user_votes', on_delete=models.CASCADE)
blog = models.ForeignKey(Blog, related_name='post_votes', on_delete=models.CASCADE)
vote_type = models.CharField(max_length=10)

class Meta:
    unique_together = ('user', 'blog', 'vote_type')

标签: pythondjangopython-3.xdjango-modelsdjango-rest-framework

解决方案


问题源于您可以为每个用户/博客/投票类型组合拥有一个用户投票对象。

具体来说,在以下几行中

self.post_votes.update(user=user, blog=self, vote_type='up')

self.post_votes.update(user=user, blog=self, vote_type='down')

您正在尝试将当前博客存在的所有投票对象更新为其中一种投票类型。这意味着您可能会尝试修改两个 UserVote 以拥有完全相同的用户、博客和 vote_type,这会导致 IntegrityError。

因此,我建议每个用户/博客组合而不是每个用户/博客/投票类型组合只有一个 UserVote,即将 unique_together 更改为

unique_together = ('user', 'blog')

然后,如果用户已经在该博客上投票,您只需更改投票类型。这可以防止同一用户同时对同一个博客投赞成票和反对票。

此外,您不必手动跟踪赞成票和反对票的总数,您可以直接从 UserVote 对象的计数中获取它们。我建议的 models.py 看起来像这样:

from django.db import models


class Blog(models.Model):
    title = models.CharField(max_length=100, blank=True, default='')
    description = models.TextField(max_length=1500)
    created = models.DateTimeField(auto_now_add=True)
    owner = models.ForeignKey('auth.User', related_name='blog', on_delete=models.CASCADE, null=True)
    image = models.FileField(blank=True, null=True)

    @property
    def votes(self):
        upvotes = UserVote.objects.filter(blog=self, vote_type='up').count()
        downvotes = UserVote.objects.filter(blog=self, vote_type='down').count()
        return upvotes - downvotes

    def upvote(self, user):
        vote, created = UserVote.objects.get_or_create(user=user, blog=self)
        if not created and vote.vote_type == 'up':
            return 'already_voted'
        vote.vote_type = 'up'
        vote.save()
        return 'ok'

    def downvote(self, user):
        vote, created = UserVote.objects.get_or_create(user=user, blog=self)
        if not created and vote.vote_type == 'down':
            return 'already_voted'
        vote.vote_type = 'down'
        vote.save()
        return 'ok'


class UserVote(models.Model):
    user = models.ForeignKey('auth.User', related_name='user_votes', on_delete=models.CASCADE)
    blog = models.ForeignKey(Blog, related_name='post_votes', on_delete=models.CASCADE)
    vote_type = models.CharField(max_length=10)

    class Meta:
        unique_together = ('user', 'blog')

我确信有一种更聪明的方法可以使用聚合来获得赞成票和反对票的总数这只是一个快速的工作示例。


推荐阅读