0

I'm stuck with a simple problem. I want to create a voting system. I have a simple code that doesn't work properly. When I click UP it adds +1, when I click UP again, it removes -1. Same with the DOWN button. The problem is that when I click between the UP and DOWN buttons. The value increases or decreases (indefinitely) - it depends with the click of the button first.

enter image description here

def vote_comment(request):
comment = get_object_or_404(CommentsCode, id=request.POST.get('id'))
is_voted = comment.is_voted.filter(id=request.user.id).exists()
up = request.POST['name'] == "UP"
down = request.POST['name'] == "DOWN"

if up:
    if is_voted:
        comment.is_voted.remove(request.user)
        comment.vote -= 1
        comment.save()
    else:
        comment.is_voted.add(request.user)
        comment.vote += 1
        comment.save()
elif down:
    if is_voted:
        comment.is_voted.remove(request.user)
        comment.vote += 1
        comment.save()
    else:
        comment.is_voted.add(request.user)
        comment.vote -= 1
        comment.save()

template.html

<form action="{% url 'xvote_comment' %}" method="post">
    {% csrf_token %}
    <div id="vov-{{q.pk}}">
        {{q.vote}}
        <button type="submit" id="like" name="UP" value="{{ q.pk }}"
            class="btn btn-danger">!UP</button>
            <button type="submit" id="like" name="DOWN" value="{{ q.pk }}"
            class="btn btn-primary">DOWN</button>
    </div>
</form>

models.py

class CommentsCode(models.Model):
    cpost = models.ForeignKey(CodePost, on_delete=models.CASCADE, null=True, blank=True)
    cauthor = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
    body = models.TextField()
    vote = models.IntegerField(default=0)
    is_voted = models.ManyToManyField(User, related_name='is_voted', blank=True)
    is_approved = models.BooleanField(default=True)

js in template

<script type="text/javascript">
    $(document).ready(function (event) {
        $(document).on('click', '#like', function (event) {
            event.preventDefault();
            var pk = $(this).attr('value');
            var name = $(this).attr('name');
            $.ajax({
                type: 'POST',
                url: '{% url "xvote_comment" %}',
                data: {
                    'id': pk,
                    'name': name,
                    'csrfmiddlewaretoken': '{{ csrf_token }}'
                },
                success: function (response) {
                    $('#vov-' + pk).load(" #vov-" + pk);
                    console.log(response)
                },
                error: function (rs, e) {
                    console.log(rs.responseText);
                },
            });
        });
    });
</script>

EDIT

I finally have the code written, maybe not optimal, but I'm just learning. Thank you for all your help

def vote_comment(request):
    comment = get_object_or_404(CommentsCode, id=request.POST.get('id'))
    is_voted_positive = comment.is_voted_p.filter(id=request.user.id).exists()
    is_voted_negative = comment.is_voted_n.filter(id=request.user.id).exists()
    up = request.POST['name'] == "UP"
    down = request.POST['name'] == "DOWN"


    if up and is_voted_positive:
        comment.is_voted_p.add(request.user)
        comment.is_voted_n.remove(request.user)


    elif up and not is_voted_positive:
        comment.is_voted_p.add(request.user)
        if is_voted_negative:
            comment.is_voted_p.remove(request.user)
            comment.is_voted_n.remove(request.user)
        comment.vote += 1
        comment.save()


    elif up and is_voted_negative:
        comment.is_voted_n.remove(request.user)
        comment.is_voted_p.add(request.user)
        comment.vote += 1
        comment.save()


    elif down and is_voted_positive:
        comment.is_voted_n.add(request.user)
        if is_voted_positive:
            comment.is_voted_p.remove(request.user)
            comment.is_voted_n.remove(request.user)
        comment.vote -= 1
        comment.save()


    elif down and is_voted_negative:
        pass


    elif down and not is_voted_negative:
        comment.is_voted_n.add(request.user)
        if is_voted_positive:
            comment.is_voted_p.remove(request.user)
            comment.is_voted_n.remove(request.user)
        comment.vote -= 1
        comment.save()

and it's look:

enter image description here

DoDr
  • 85
  • 7
  • 1
    It is very hard to understand what exactly you mean. In general probably node.js would help the implementation of post voting, otherwise each vote would be treated as a view call resulting in a new page load. I guess your UP vote becomes -1 on the second attempt because of your "if is_voted: " statement. When If is_voted is True then the next line removes the comment.is_voted.remove() and thus next time when you vote for it the "if is_voted" statement result is False and the else part of the code gets executed. Not sure if its really the case but could be – d1spstack Jan 31 '22 at 18:25
  • do you mean that is_voted is always True ? – eisa nahardani Jan 31 '22 at 18:29
  • I *think* my answer below might help. @d1spstack is correct. – raphael Jan 31 '22 at 18:40
  • 1
    I have attached a gif – DoDr Jan 31 '22 at 19:37
  • Yes @eisanahardani, is_voted is always True, do you have any idea how to fix this? – DoDr Feb 01 '22 at 16:39
  • Did you solve problem? – eisa nahardani Feb 01 '22 at 21:05
  • Yes, Thank you for help – DoDr Feb 01 '22 at 21:07

1 Answers1

0

When you click UP the first time, the is_voted is false, and the else condition occurs and the vote increases by 1. Now the next time, the is_voted will be true, and thus the if statement will be true and the vote will decrement by 1. Perhaps this would work (I did not have a chance to test it):

def vote_comment(request):
    comment = get_object_or_404(CommentsCode, id=request.POST.get('id'))
    is_voted = comment.is_voted.filter(id=request.user.id).exists()
    up = request.POST['name'] == "UP"
    down = request.POST['name'] == "DOWN"

    if not is_voted:
        comment.is_voted.add(request.user)
        if down:
            comment.vote -= 1
        elif up:
            comment.vote += 1
        comment.save()

    # Or might even be easier like this:

    #if not is_voted:
    #    comment.is_voted.add(request.user)
    #    comment.vote = comment.vote + 1 if up else comment.vote - 1
        

EDIT

Based on your template I can see another error. The name attribute is what the request.POST will send as the key, the value is the value that will be sent. So there is NO request.POST['name'], since you have nothing that says name='name'. Try the following:

<form action="{% url 'xvote_comment' %}" method="post">
    {% csrf_token %}
    <div id="vov-{{q.pk}}">
        {{q.vote}}
        <button type="submit" id="like" name="vote" value="UP"
            class="btn btn-danger">!UP</button>
        <button type="submit" id="like" name="vote" value="DOWN"
            class="btn btn-primary">DOWN</button>
    </div>
</form>

Then in your view:

def vote_comment(request):
    comment = get_object_or_404(CommentsCode, id=request.POST.get('id'))
    print(request.POST) # Check to see what is actually being sent
    is_voted = comment.is_voted.filter(id=request.user.id).exists()
    up = request.POST['vote'] == "UP"
    down = request.POST['vote'] == "DOWN"

    if not is_voted:
        comment.is_voted.add(request.user)
        if down:
            comment.vote -= 1
        elif up:
            comment.vote += 1
        comment.save()

    # Or might even be easier like this:

    #if not is_voted:
    #    comment.is_voted.add(request.user)
    #    comment.vote = comment.vote + 1 if up else comment.vote - 1
raphael
  • 2,469
  • 2
  • 7
  • 19
  • Thanks for the code, but this code is also not working properly, I tried to modify it but the effect is like in my code. I have attached a gif – DoDr Jan 31 '22 at 19:11
  • Can you add your CommentsCode model and your html template. – raphael Jan 31 '22 at 19:38
  • Hi @DrewnyDrzew, I see that the problem is in your template, and I've edited my answer. In fact, I'm surprised you didn't get an error since there is NO `request.POST['name']` at least in the section that I see, you should have gotten a `MultiValueDictKeyError` error, unless you have an element with the name='name' somewhere else in your template. – raphael Jan 31 '22 at 20:09
  • yesterday I was tired and forgot to add JS code from template. Now I edited the post and added JS. Thank you for helping – DoDr Feb 01 '22 at 10:59
  • I'm not too familiar with jQuery, but it does explain why you're not getting the `MultiValueDictKeyError`. However, I don't see anything in the script that should affect the solution. If you make the changes I suggested, the script will receive the updated values, and it should work. – raphael Feb 01 '22 at 11:53
  • Hi @DrewnyDrzew, if it still does not work, I wonder what your `console.log` statements are printing. Maybe the def vote_comment function is not returning what you expect. – raphael Feb 01 '22 at 13:31
  • Hi!, the problem is with the backend script. I know why this is happening but I have no idea how to "fix" it. When I press the "UP" button it adds the user to "is_voted" and adds one point to the voting system. Then I click the "DOWN" button and the script checks for "is_voted". "is_voted" = True and then subtracts the user from "is_voted" and adds the point to the voting system. Over and over again. I checked it on the debugger – DoDr Feb 01 '22 at 16:17
  • I must be missing something, @DrewnyDrzew, the backend, the view, if you changed it to what I put in, then I don't see how that can be happening. The `if not is_voted:` in the view means that if `is_voted = True`, then the entire if statement should be skipped, and nothing should happen. Sorry, I'm out of ideas. Good luck. – raphael Feb 01 '22 at 16:36