skip to Main Content

OK so I have an index page with items that can be up or downvoted.
The issue is that when I upvote/downvote one item, all items on the page are upvoted/downvoted and the appearance of all buttons on the page all change instead of just the one object that was upvoted/downvoted.

Can anyone help me serialize each for loop object correctly?

my index page:

{% for post in posts %}
    <span id="post_{{forloop.counter}}" data-value="{{post.id}}"></span>
    <button class="vote_action" value="upvote_button"> + </i></button>
    
    <span id="votes_{{forloop.counter}}">{{post.points}}</span>
                                  
    <button class="vote_action" value="downvote_button"> - </button>
{% endfor %}
...
<script type="text/javascript">
 // JQUERY - AJAX SCRIPT FOR voting
 $(document).ready(function(){
   {% for post in posts %}
   $('.vote_action').click(function(e) {
      var postid = document.getElementById('post_{{forloop.counter}}').getAttribute('data-value');
          var button = $(this).attr("value");
          e.preventDefault();
          $.ajax({
            type: 'POST',
            url: '{% url "vote" %}',
            data: {
              postid: postid,
              csrfmiddlewaretoken: '{{ csrf_token }}',
              action: 'postvote',
              button: button,
            },
            success: function(json){
              if (json.length < 1 || json == undefined) {
                //empty
              }
              document.getElementById("votes_{{forloop.counter}}").innerHTML = json['result']
              //change button looks
              $("#uvb_{{forloop.counter}}").addClass("disabled");
              $("#dvb_{{forloop.counter}}").addClass("disabled");
            },
            error: function(xhr, errmsg, err) {}
          })
        })
        {% endfor %}
      })
      </script>

My views function:

@login_required
def post_vote(request):
    if request.POST.get('action') == 'postvote':
        # get information from request about what item id it is
        id = int(request.POST.get('postid'))
        # And also which button was pressed
        button = request.POST.get('button')
        post = Posts.objects.get(id=id)

        if button == 'downvote_button':
            if not post.voters.filter(id=request.user.id).exists():
                post.voters.add(request.user)
                post.votes +=1
                post.points -=2
                post.save()

        elif button == 'upvote_button':
            if not post.voters.filter(id=request.user.id).exists():
                post.voters.add(request.user)
                post.votes +=1
                post.points +=2
                post.save()

        # return result
        post.refresh_from_db()
        result = post.points
        return JsonResponse({'result':result})
    pass

2

Answers


  1. Chosen as BEST ANSWER

    After the support given by @swati, I was able to get this working correctly as demonstrated below for anyone else having trouble with a similar issue.

    All credit goes to @swati

    HTML portion of the page:

    {% for post in posts %}
      <div class="eachpost"> //<!--add this div-->
        <span id="post_{{forloop.counter}}" data-value="{{post.id}}"></span>
        <button class="vote_action" value="upvote_button" id="uvb_{{forloop.counter}}"> + </i></button>
        //<!--add class here-->
        <span id="votes_{{forloop.counter}}" class="votes_points">{{post.points}}</span>                              
        <button class="vote_action" value="downvote_button" id="dvb_{{forloop.counter}}"> - </button>
      </div>
    {% endfor %}
    

    The JS/AJAX Section of the page

    $('.vote_action').click(function(e) {
        //get closest div
          var selector = $(this).closest('div.eachpost');
          var button = $(this).attr("value");
          //find class get id and split to get counter
          var counter =selector.find('.votes_points').attr('id').split('_')
          //get span data-value
          var postid = selector.find("span[id=post_"+counter[1]+"]").attr('data-value');
          
          console.log("postID-->"+postid+"Button-->"+button+"counter[1]-->"+counter[1])
          e.preventDefault();
          //your ajax
           $.ajax({
            type: 'POST',
            url: '{% url "vote" %}',
            data: {
              postid: postid,
              csrfmiddlewaretoken: '{{ csrf_token }}',
              action: 'postvote',
              button: button
            },
            success: function(json) {
              if (json.length < 1 || json == undefined) {
                //empty
              }
              //add response to votes_point
              $("#votes_"+counter[1]).text(json['result']);
              //change button looks
              $("#uvb_"+counter[1]).addClass("disabled");
              $("#dvb_"+counter[1]).addClass("disabled");
            },
            error: function(xhr, errmsg, err) {}
          })
         
        })
    

  2. Instead of using for-loop for script you can simply use closest() and find() method of jquery to achieve same .Here are some change you need to make in your django code :

    {% for post in posts %}
      <div> //<!--add this div-->
        <span id="post_{{forloop.counter}}" data-value="{{post.id}}"></span>
        <button class="vote_action" value="upvote_button"> + </i></button>
        //<!--add class here-->
        <span id="votes_{{forloop.counter}}" class="votes_points">{{post.points}}</span>                              
        <button class="vote_action" value="downvote_button"> - </button>
    </div>
    {% endfor %}
    

    Then , in your jquery code whenever your button is clicked use closest() method to get the closest div then using this find the span where class="votes_points" and get its id and use split method to get the {{forloop.counter}} value.Using this we can easily get postid value also . So,your jquery code will look like below :

    $('.vote_action').click(function(e) {
        //get closest div
          var selector = $(this).closest('div');
          var button = $(this).attr("value");
          //find class get id and split to get counter
          var counter =selector.find('.votes_points').attr('id').split('_')
          //get span data-value
          var postid = selector.find("span[id=post_"+counter[1]+"]").attr('data-value');
          
          console.log("postID-->"+postid+"Button-->"+button+"counter[1]-->"+counter[1])
          e.preventDefault();
          //your ajax
           $.ajax({
            type: 'POST',
            url: '{% url "vote" %}',
            data: {
              postid: postid,
              csrfmiddlewaretoken: '{{ csrf_token }}',
              action: 'postvote',
              button: button
            },
            success: function(json) {
              if (json.length < 1 || json == undefined) {
                //empty
              }
              //add response to votes_point
              selector.find('.votes_points').text(json['result'])
              //change button looks
              $("#uvb_"+counter[1]).addClass("disabled");
              $("#dvb_"+counter[1]).addClass("disabled");
            },
            error: function(xhr, errmsg, err) {}
          })
         
        })
    

    Demo Code(with demo datas) :

    $('.vote_action').click(function(e) {
    //get closest div
      var selector = $(this).closest('div');
      var button = $(this).attr("value");
      //find class get id and split to get counter
      var counter =selector.find('.votes_points').attr('id').split('_')
      //get span data-value
      var postid = selector.find("span[id=post_"+counter[1]+"]").attr('data-value');
      
      console.log("postID-->"+postid+" || Button-->"+button+" ||   counter[1]-->"+counter[1])
      e.preventDefault();
      //your ajax
       $.ajax({
        type: 'POST',
        url: '{% url "vote" %}',
        data: {
          postid: postid,
          csrfmiddlewaretoken: '{{ csrf_token }}',
          action: 'postvote',
          button: button
        },
        success: function(json) {
          if (json.length < 1 || json == undefined) {
            //empty
          }
          //add response to votes_point
          selector.find('.votes_points').text(json['result'])
          //change button looks
          $("#uvb_"+counter[1]).addClass("disabled");
          $("#dvb_"+counter[1]).addClass("disabled");
        },
        error: function(xhr, errmsg, err) {}
      })
     
    })
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div>
      <span id="post_1" data-value="1"></span>
      <button class="vote_action" value="upvote_button"> +</button>
    
      <span id="votes_1" class="votes_points">15</span>
      <button class="vote_action" value="downvote_button"> - </button>
    </div>
    <div>
      <span id="post_2" data-value="2"></span>
      <button class="vote_action" value="upvote_button"> +</button>
    
      <span id="votes_2" class="votes_points">52</span>
    
      <button class="vote_action" value="downvote_button"> - </button>
    </div>
    <div>
      <span id="post_3" data-value="3"></span>
      <button class="vote_action" value="upvote_button"> +</button>
    
      <span id="votes_3" class="votes_points">52</span>
    
      <button class="vote_action" value="downvote_button"> - </button>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search