2

I am rewriting this question, because I am uncertain if I would produce a duplicate if posting a new one. The first comments is answer to the original question.

I have a exam project which is partly to write a Django site with notes where a logged in user can vote on a note.

The html code, urls, view and jQuery don't give any errors. However, the number of likes will not increase after clicking the thumbs up icon from Bootstrap.

Updated with suggestions form Liαrεz and Sebastian Wozny

Can anyone please help me?

JS file

$(document).ready(function(){
var csrftoken = $.cookie('csrftoken');
$("#increase_num_likes_thumb").click(function(event){
    event.preventDefault();
    $.ajax({
        method: "POST",
        headers: { 'X-CSRFToken': $.cookie('csrftoken') },
        url: $('#num_likes_url').val(),
        success: function(data){
            result = JSON.parse(data);
            if (result.error){
                consloe.log(result.error_text);
            }else{
                var num_likes_updated = result['num_likes_updated'];
                $("#num_likes_div").html(num_likes_updated);
            }
        }
    });
});
});

HTML

<div class="row">
    <div class="col-sm-10">
        {% if notes %}
            {% for note in notes %}
                <div class="col-sm-5" style="border: 1px solid; margin: 10px;">
                    <h3 class="page-header"><a href="{% url 'notes:detailnote' %}?id={{ note.id }}">{{ note.label }}</a></h3>
                    <div style="padding: 5px;"> 
                        {{ note.body }}
                        <p>
                            <div>
                                <a href="/notes/?id={{ note.id }}/increase_num_likes/" id="increase_num_likes_thumb">
                                <span class="glyphicon glyphicon-thumbs-up" aria-hidden="true"></span>
                                </a>
                            </div>
                            <div id="num_likes_div">
                                {{ note.num_likes }}
                            </div>
                        </p>
                        <input type="hidden" id="num_likes_url" value="/notes/increase_num_likes/?id={{ note.id }}" >
                    </div>
                </div>
            {% endfor %}
        {% endif %}
    </div>
</div>

urls.py

from django.conf.urls import patterns
from django.conf.urls import url
from django.conf.urls import include
from notes.views import increase_num_likes

urlpatterns = patterns('',
    url(r'^(P<id>[\d\w]+)/increase_num_likes/$',
    increase_num_likes, name='increase_numlikes'),

,

Views.py

import json
from django.http import HttpResponse
def increase_num_likes(request):
    id = request.GET.get('id', None)
    if id is None:
        note = get_object_or_404(Note, id=id)
        data = {'error': True, 'error_text': 'Not ID supplied'}
    else:
        note = Note.objects.get(id=int(id))
        note.num_likes += 1
        note.save()
        data = {'num_likes_updated': note.num_likes}
    return HttpResponse(simplejson.dumps(data))
Arash Hatami
  • 5,297
  • 5
  • 39
  • 59
MelbyJim
  • 35
  • 1
  • 1
  • 9
  • Take a look at this http://stackoverflow.com/questions/8059071/django-are-there-any-tools-tricks-to-use-on-debugging-ajax-response and let us know more on the error. – Wtower May 23 '15 at 21:42
  • Thanks for the quick response. Do you reccon more info is needed? – MelbyJim May 23 '15 at 21:51
  • It seems like I am passing two arguments, not one, when updating the page with my ajax call. Hmm... – MelbyJim May 23 '15 at 22:03
  • 1
    do you care about the number `16` in the url? in that case your view function needs to accept a second argument. if you don't care then you shouldn't use a regex group in the url pattern, so that Django doesn't try to call the view function with two args. i.e. don't use brackets around the `\d+` – Anentropic May 23 '15 at 22:10
  • 1
    you django view code doesnt seem logical. how can your note.save() if note = None? – Mox May 24 '15 at 02:15
  • @Anentropic Thanks for your reply. I get the new url when I click the anchor, however the count of likes does not change. – MelbyJim May 24 '15 at 02:38
  • @Mox Thanks for mention that. I tried with note = Note.objects.get(pk=note_id) instead of the id and if else as you mention. However, this does not change anything as far as I can see. – MelbyJim May 24 '15 at 02:39

2 Answers2

1

You should change your AJAX view to:

from django.http import HttpResponse
import json

def increase_num_likes(request):
    id = request.GET.get('id', None)
    if id is None:
        note = get_object_or_404(Note, id=id)
        data = {'error':True, 'error_text': 'Not ID supplied'}
    else:
        note = Note.objects.get(id=int(id))
        note.num_likes += 1
        note.save()
        data = {'num_likes_updated': note.num_likes}
    return HttpResponse(simplejson.dumps(data))

and change your AJAX .done to .success managing the error:

$(document).ready(function(){
    $("#increase_num_likes_thumb").click(function(event){
        event.preventDefault();
        $.ajax({
            method: "POST",
            url: $('#num_likes_url').val()
        })
        .success: function(data) {
            result = JSON.parse(data);  # Parse the data received
            if (result.error){
                console.log(result.error_text);
            }else{
                var num_likes_updated = result['num_likes_updated'];
                $("#num_likes_div").html(num_likes_updated);
            }
        })
    });
});
AlvaroAV
  • 10,335
  • 12
  • 60
  • 91
1

Your problem lies in url: $('#num_likes_url').val()

<input type="hidden" id="num_likes_url" value="/notes/?id={{ note.id }}/increase_num_likes/" >

so valueis "/notes/?id={{ note.id }}/increase_num_likes/" which is not a valid URI. From wikipedia:

<scheme name> : <hierarchical part> [ ? <query> ] [ # <fragment> ]

The correct way would be to call if you'd like to retrieve the id as a get argument.

"/notes/increase_num_likes/?id={{ note.id }}"

your urls.py

url(r'^increase_num_likes/$', increase_num_likes, name='increase_numlikes')

Alternatively you could make use of djangos url parsing:

urls.py

url(r'^(P<id>[\d\w]+)/increase_num_likes/$', increase_num_likes, name='increase_numlikes')

views.py

from import json

def increase_num_likes(request,id):
if id is None:
    note = get_object_or_404(Note, id=id)
    data = {'error': True, 'error_text': 'Not ID supplied'}
else:
    note = Note.objects.get(id=int(id))
    note.num_likes += 1
    note.save()
    data = {'num_likes_updated': note.num_likes}
return HttpResponse(simplejson.dumps(data))

HTML

<div class="row">
    <div class="col-sm-10">
        {% if notes %}
            {% for note in notes %}
                <div class="col-sm-5" style="border: 1px solid; margin: 10px;">
                    <h3 class="page-header"><a href="{% url 'notes:detailnote' %}?id={{ note.id }}">{{ note.label }}</a></h3>
                    <div style="padding: 5px;"> 
                        {{ note.body }}
                        <p>
                            <div>
                                <a href="/notes/?id={{ note.id }}/increase_num_likes/" id="increase_num_likes_thumb">
                                <span class="glyphicon glyphicon-thumbs-up" aria-hidden="true"></span>
                                </a>
                            </div>
                            <div id="num_likes_div">
                                {{ note.num_likes }}
                            </div>
                        </p>
                        <input type="hidden" id="num_likes_url" value="/notes/increase_num_likes/?id={{ note.id }}" >
                    </div>
                </div>
            {% endfor %}
        {% endif %}
    </div>
</div>

Furthermore it's important to set the correct headers:

The documentation suggests you are required to set the X-CSRFToken header when posting data with AJAX that is not a form which contains the {% csrftoken %} tag.

With jquery acquiring the token on the client is straightforward:

var csrftoken = $.cookie('csrftoken');

Set the right header on your ajax call:

$.ajax({
      method: "POST",
      headers: { 'X-CSRFToken': $.cookie('csrftoken') }
      url: $('#num_likes_url').val()
    })

I suspect your POST never reaches your view and is caught by CSRFProtectionMiddleware

Arash Hatami
  • 5,297
  • 5
  • 39
  • 59
Sebastian Wozny
  • 16,943
  • 7
  • 52
  • 69
  • After DOM is loaded I have pasted the js code from the [Documention](https://docs.djangoproject.com/en/dev/ref/csrf/#ajax). The long version, not the short you like you suggest. And after that in the js file, I have pasted the function csrfSafeMethod(method) from Django. Not in headers like you suggest. – MelbyJim May 24 '15 at 10:18
  • I have updated the question based on you suggestion. However, I do believe I have found an error in my html. Is seems like I am using the same id on several html elements on the same page. Shame on me. Could this be the reason for not working? – MelbyJim May 24 '15 at 11:02
  • That seems unlikely, even though it's against HTML standards, typical implementations will silently ignore the error. – Sebastian Wozny May 24 '15 at 11:06
  • It worked once with the **url(r'^(P[\d\w]+)/increase_num_likes/$', increase_num_likes, name='increase_numlikes')** . I also changed the **value** of **num_likes_url** like you suggested. The first note changed from 0 til 1 vote. So that is great. However, if I try to vote again I get an **404 error**. Get the same error if a different user try to vote. The **console** says it's an **ananoymous function** line 5 in the **js file** – MelbyJim May 24 '15 at 11:54
  • what URI are you getting the 404 from, can you copy paste the url? – Sebastian Wozny May 24 '15 at 11:57
  • yeah so you are still querying the old url, try hitting localhost:8000/notes/16/increase_num_likes/ repeatedly – Sebastian Wozny May 24 '15 at 12:03
  • I don't understand what you mean. The url you're suggestion is missing ?id=, so I get an 404 error. However, refreashing the page with the url localhost:8000/notes/?id=16/increase_num_likes/ does not seem to do anything. – MelbyJim May 24 '15 at 12:11
  • if you're using my urlconfig then you have to use my views.py in which there is no ?id=, you're mixing code together that does not belong together. the url i have posted will work for my view + my urls.py – Sebastian Wozny May 24 '15 at 12:29
  • Not easy to be a code rookie ;-) I have tried to make it work bu using your code here, both the url, view and html. However, this raise and error of unexpected < when activating the script. Seems like there is something wrong with Json parse part of the ajax. Honestly, I am becoming a bit lost here. So I wonder if I should start over, trying another aproach to this add likes to a note problem. Thank you very much for your time and effort. – MelbyJim May 24 '15 at 13:27
  • can you upload your code to github and share? i'll fix it – Sebastian Wozny May 24 '15 at 13:38
  • I have not commited that changes done in this tread. However, the repo is here https://github.com/jimma1/harvsterOfNotes – MelbyJim May 24 '15 at 16:27
  • You have got a pull request on github. – Sebastian Wozny May 24 '15 at 18:49