Проблема возникает из-за того, что у вас может быть один объект UserVote на комбинацию User / Blog / VoteType.
В частности, в следующих строках
self.post_votes.update(user=user, blog=self, vote_type='up')
self.post_votes.update(user=user, blog=self, vote_type='down')
вы пытаетесьобновите все объекты голосования, существующие для текущего блога, до одного из voice_types.Это означает, что вы потенциально можете попытаться изменить два UserVotes, чтобы иметь одного и того же пользователя, blog и voice_type, что вызывает IntegrityError.
Таким образом, я бы предложил использовать только одно UserVote на комбинацию User / Blog вместо каждойКомбинация User / Blog / VoteType, то есть измените 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')
Я уверен, что есть гораздо более умный способ получить общее количество положительных и отрицательных голосов, используя агрегации , это простобыстрый рабочий пример.