0

I have a website with an AJAX upvote or downvote option on a post. The problem I am having is that the voting works on my Post detail page but not on my index page. I am getting the error Forbidden (CSRF token missing or incorrect.): /vote/

Both pages call the same view and URL pattern.

index.html:

{% 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">{{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: $('input[name=csrfmiddlewaretoken]').val(),
              action: 'postvote',
              button: button,
            },
            success: function(json){
              if (json.length < 1 || json == undefined) {
                //empty
              }
              document.getElementById("votes").innerHTML = json['result']
            },
            error: function(xhr, errmsg, err) {}
          })
        })
        {% endfor %}
      })
      </script>

post_detail.html:

    <span id="vote_id" data-value="{{post.id}}"></span>
    <button class="vote_action" value="upvote_button"> + </i></button>
    <span id="votes">{{post.points}}</span>                           
    <button class="vote_action" value="downvote_button"> - </button>

    <script type="text/javascript">
       // JQUERY - AJAX SCRIPT FOR voting
       $(document).ready(function(){
          $('.vote_action').click(function(e) {
              var postid = document.getElementById('vote_id').getAttribute('data-value');
              var button = $(this).attr("value");
              e.preventDefault();
              $.ajax({
                type: 'POST',
                url: '{% url "vote" %}',
                data: {
                  postid: postid,
                  csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
                  action: 'postvote',
                  button: button,
                },
                success: function(json){
                  if (json.length < 1 || json == undefined) {
                    //empty
                  }
                  document.getElementById("points").innerHTML = json['result']
                },
                error: function(xhr, errmsg, err) {}
              })
            })
          })
        </script>

urls.py

urlpatterns = [
    path('vote/', views.post_vote, name='vote' ),
]

views.py

@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

Sorry for the longest post ever! I've tried to cut down where possible and leave the essential information. Does anyone have any ideas?

bitFez
  • 184
  • 1
  • 12
  • Do the two pages have the same base URL? – Lajos Arpad Sep 05 '20 at 11:50
  • The index page and post detail pages both have separate URL patterns if that's what you mean. – bitFez Sep 05 '20 at 12:13
  • Then the problem is that your browser defends you against Cross Reference Origin. You will need to allow cross reference origin, specified in headers. See: https://stackoverflow.com/questions/10636611/how-does-access-control-allow-origin-header-work – Lajos Arpad Sep 05 '20 at 12:22
  • Thank you for your answer. What would you suggest the most straightforward way to do that in Django is? – bitFez Sep 05 '20 at 12:34
  • I am not a Django specialist, but this seems to be a good source to read: https://pypi.org/project/django-cors-headers/ – Lajos Arpad Sep 05 '20 at 13:05
  • I'm afraid this has not worked! – bitFez Sep 05 '20 at 13:13

1 Answers1

0

I seem to have it working now using the following method which I came across on another post.

In index.html I have changed:

csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),

to

csrfmiddlewaretoken: '{{ csrf_token }}',

I am not sure if this is the correct way of solving this problem and will have to wait to see if it works when the site is eventually live. Any opinions are welcome.

bitFez
  • 184
  • 1
  • 12