python - 导致未知麻烦的投票模型操作
问题描述
我做了一个下面给出的投票模型。我的博客模型有两个操作来支持或反对特定的博客。例如,当单个用户投票时,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')
解决方案
问题源于您可以为每个用户/博客/投票类型组合拥有一个用户投票对象。
具体来说,在以下几行中
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')
我确信有一种更聪明的方法可以使用聚合来获得赞成票和反对票的总数,这只是一个快速的工作示例。
推荐阅读
- swift - 单元测试由 Swift 中的各种 didSets 调用的异步函数
- model-view-controller - 我们如何识别 MVC 桌面应用程序中的路径和控制器名称?
- python - flow_from_directory for one-hot encoding python 错误
- docker - 在容器内使用 webpack 构建时缺少加载器
- angular - ionic 5 cordova run android 为任何提供者返回“无提供者”
- domain-driven-design - 外部服务结果改变聚合状态
- tcp - lwip_connect() 函数返回 -1
- git - 陈旧分支合并到分支主干的最常用的健康程序员工作流程是什么?
- html - 在第二页和后续页面上打印时缺少 div 的 CSS 背景颜色
- tableau-api - Tableau Google Ads 连接器