Джанго - Пользователь должен иметь возможность голосовать только один раз

Я хочу, чтобы пользователь мог голосовать только один раз за запрос категории, но каким-то образом я получаю следующую ошибку, и я не знаю, как "правильно" вызвать экземпляр в этот момент:

Невозможно присвоить "1": "CategoryRequests_Voter.voted" должен быть экземпляром "CategoryRequests".

models.py

# Category Requests Model
class CategoryRequests(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)

    ....

# Vote(s) of Category Requests Model
class CategoryRequests_Voter(models.Model):
    voter = models.ForeignKey(User, on_delete=models.CASCADE)
    voted = models.ForeignKey(CategoryRequests, on_delete=models.CASCADE)
    published_date = models.DateField(auto_now_add=True, null=True)

    def publish(self):
        self.published_date = timezone.now()
        self.save()

views.py

def category_request_up_vote (request, pk):
    category_request = get_object_or_404(CategoryRequests, pk=pk)
    if request.method == 'GET':
        if CategoryRequests_Voter.objects.filter(voter=request.user, voted=category_request.pk).exists():
            messages.error(request, 'You already voted for this request.')
        else:
            category_request.up_vote = F('up_vote') + 1
            category_request.save()
            CategoryRequests_Voter.objects.create(voter=request.user, voted=category_request.pk)
            messages.success(request, 'You have successfully Provided an Up-Vote for this Request')
            return redirect('category_request_detail', pk=category_request.pk)
    else:
        messages.error(request, 'Uuups, something went wrong, please try again.')
        return redirect('category_request_detail', pk=category_request.pk)

заранее спасибо

2 ответа

Вам нужно исправить voted аргумент просто category_request, а не его первичный ключ, как:

CategoryRequests_Voter.objects.create(voter=request.user, voted=category_request)

Однако вы можете улучшить свою модель и внешний вид, чтобы улучшить согласованность и элегантность. Чтобы предотвратить User от голосования дважды или более, вы можете предотвратить создание CategoryRequest_Voter объект дважды для одного и того же voter а также voted, используя unique_together ограничение:

class CategoryRequests_Voter(models.Model):
    voter = models.ForeignKey(User, on_delete=models.CASCADE)
    voted = models.ForeignKey(CategoryRequests, on_delete=models.CASCADE)
    published_date = models.DateField(auto_now_add=True, null=True)

    class Meta:
        unique_together = ('voter', 'voted')

    def publish(self):
        self.published_date = timezone.now()
        self.save()

Кроме того, мы можем использовать get_or_create и, таким образом, сделать только одну выборку из базы данных. Обычно представление, которое изменяет данные, должно делать это с запросом POST, а не с GET. Запросы GET не должны иметь побочных эффектов.

def category_request_up_vote (request, pk):
    category_request = get_object_or_404(CategoryRequests, pk=pk)
    if request.method == 'POST':
        __, created = CategoryRequests_Voter.objects.get_or_create(
            voter=request.user,
            voted=category_request
        )
        if created:
            category_request.up_vote = F('up_vote') + 1
            category_request.save()
            messages.success(request, 'You have successfully Provided an Up-Vote for this Request')
        else:
            messages.error(request, 'You already voted for this request.')
    else:
        messages.error(request, 'Uuups, something went wrong, please try again.')
    return redirect('category_request_detail', pk=category_request.pk)

Возможно, стоит посчитать количество CategoryRequest_Voter s, вместо увеличения подсчета голосов, поскольку возможно, что, например, из-за удаления User s, или голосов, в конечном итоге количество голосов больше не согласуется с количеством CategoryRequests_Voter для этого CategoryRequests объект.

Возможно, подсчет количества объектов не так эффективен для каждого голоса, но вы можете сделать задачу, которая запускается время от времени, и таким образом вычислить:

CategoryRequests_Voter.objects.filter(voted=category_request).count()

посчитать количество CategoryRequests_Voter для данного category_request,

def category_request_up_vote (request, pk):
    category_request = get_object_or_404(CategoryRequests, pk=pk)
    try:
        if request.method == 'GET':
            if CategoryRequests_Voter.objects.filter(voter=request.user, voted=category_request).exists():
                messages.error(request, 'You already voted for this request.')
                return redirect('category_request_detail', pk=category_request.pk)
            else:
                category_request.up_vote = F('up_vote') + 1
                category_request.save()
                CategoryRequests_Voter.objects.create(voter=request.user, voted=category_request)
                messages.success(request, 'You have successfully Provided an Up-Vote for this Request')
                return redirect('category_request_detail', pk=category_request.pk)
        else:
            messages.error(request, 'Uuups, something went wrong, please try again.')
            return redirect('category_request_detail', pk=category_request.pk)
    except:
        messages.error(request, 'Uuups, something went wrong, please try again.')
        return redirect('category_request_detail', pk=category_request.pk)
Другие вопросы по тегам