javascriptjqueryajaxdjangodjango-voting

Django,Jquery,Ajax; refreshing div for voting system not working?


I have a working django voting system using up and down keypresses on entries in a database.

I need to have the {{ entry.score }} refresh on the page without a reload, as there will be other entries on the page. The {{ entry.text }} can refresh but needs to stay as the same entry until a different keypress selects a different entry.

I'm trying to do it with ajax, but get a 500 Internal Server Error and no refresh,

GET http://127.0.0.1:8000/voteup/?voteid=30 500 (INTERNAL SERVER ERROR) jquery.min.js:4
send jquery.min.js:4
n.extend.ajax jquery.min.js:4
n.(anonymous function) jquery.min.js:4
(anonymous function) (index):76
n.event.dispatch jquery.min.js:3
r.handle

even though the vote goes through correctly...

(index):76 in voting.html is: $.get("/voteup/", args).done(function(data) {

voting.html

 <div class = "table">
  <div id="Vote" class = "vote">
  <div style="text-align: left">
  {% for entry in voting_entry_list %} 
    <li><a href="/entries/{{ entry.id }}/">{{ entry.text }}&nbsp{{ entry.score }}</a></li>
    <p>
    <input type="submit" id="voteid" name='voteid' value="{{ entry.id }}" autofocus value="" onfocus="this.value = this.value;" class = "transparent"/>
          <script>
          var data = '#Vote';
          var url = "/voting/";
            $(document).ready(function() {
              $("#voteid").bind("keydown", function(e) { //input #submit?????
                if (e.keyCode == 38) {
                  var text = $("#voteid").val();        
                  var args = {'voteid':text};       
                  $.get("/voteup/", args).done(function(data) {
                    console.log("message: " + data);
                        $.ajax({      
                            url: url,
                            data: data,
                            dataType: 'html',
                            success: function(data){
                                $(this).html(data); //this?
                            }   
                        });
                  });
                return false;
                }       
              });
            });     
          </script>
     {% endfor %}
      </div>
      </div>
  </div>

views.py

def index(request):   
   context = { # actually one item, command from extended object manager
     'voting_entry_list': Entry.objects.unvoted_or_random(), 
   }     
   return render(request, 'entries/index.html', context); 

def voting(request):
    context = {'voting_entry_list': Entry.objects.random(),}      
    return render(request, 'entries/voting.html', context);

def voteup(request):
    voting_id = request.GET.get('voteid') 
    e = Entry.objects.unvoted_or_random()
    context = {'voting_entry_list': e,}
  if request.method=='GET':
    v = Entry.objects.get(pk=voting_id)
    v.score +=1 
    v.voted=True 
    v.save() 
    context = {'voting_entry_list': v,}
  else:
    pass
  return render(request, 'entries/voting.html', context);

Models.py

class EntryManager(models.Manager): #a basic extention to the model basemanager to insert new sorting
def unvoted_or_random(self): # command definition uses models input
    unvoted_entries = self.filter(voted = False).order_by('-pub_date') # sorted by youngest unvoted entries from any user
    voted_entries = self.filter(voted = True).order_by('?') # if no unvoted entries, voted boolean enables random selection '?'  
    if unvoted_entries: # for boolean unvoted
        return unvoted_entries[:1] # return latest
    else: # for boolean voted
        return voted_entries[:1] # return random

Solution

  • I just had a session with a Phd student at the University, and he put me on the right track.

    The basic issue I was having was about the philosophy of using Jquery and Ajax, something I found hard to glean from manuals and online support, which I found to be opaque on the very basics for simple tasks. To that end, I will present the final code here in the hopes it helps people starting out with a similar simple voting system.

    Thanks to Refresh a div in Django using JQuery and AJAX Reload entire page in Ajax on Django? How to correctly refresh a div using Jquery/Ajax in a Django template Passing list of values to django view via jQuery ajax call For bits and pieces...


    Voting refresh with no change of entry (button mashing)


    My main problem was what I did for the url and data. It turns out I had the wrong end of the stick. Data in my case is the return from the views .get (I thought it was the entry id that I had to pass to the refresh) , and I didn't need to define the url for the very simple system I was making.

    So This:

    voting.html

    var data = '#Vote';
    var url = "/voting/";
    
    $.ajax({      
         url: url,
         data: data,
         dataType: 'html',
         success: function(data){
         $(this).html(data);
      } 
    

    Became This:

    $("#score").text(data); //span updates score with new value from views
    

    To refresh the scores as they are voted on. Span in the href above that code allowed me to access the score:

    <span id="score">{{ entry.score }}</span>
    

    And the only other thing I had to do was add this to views in voteup(request) or votedown(request) when I accessed the entry models and added the vote:

    views.py

    return HttpResponse(v.score)
    

    This takes care of the vote up and down. When you button mash the up and down arrows, the entry stays the same, but the votes are refreshed from views after being saved to database.


    Key to change entry with refresh


    To change the entry (to a new, random entry), the .get for the right arrow key returns a string with the id, text and score of a random entry;

    views.py

    def random_entry(request):
    e = Entry.objects.random()[0]
    return HttpResponse("%s,%s,%s" % (e.id, e.text, e.score))
    

    Which voting.html splits and refreshes text and score, and changes the currentid:

    voting.html

    if(e.keyCode == 39) { //right key
         $.get("/random-entry/", {}).done(function(data) {
             var parts = data.split(",");
              $("#entry-text").text(parts[1]);
              $("#score").text(parts[2]);
              currentid = parts[0];
              console.log("random entry data: " + data);
             });
            return false;}
    

    So that's the main changes. I include the full(ish) code below in hope it helps someone;

    voting.html

     <div class = "table">
      <div id="Vote" class = "vote">
      <div style="text-align: left">
      {% for entry in voting_entry %} 
        <li><a href="/entries/{{ entry.id }}/"><span id="entry-text">{{ entry.text }}</span> <span id="score">{{ entry.score }}</span></a></li>
        <p>
        <input type="submit" id="voteid" name='voteid' value="{{ entry.id }}" autofocus value="" onfocus="this.value = this.value;" class = "transparent"/>
              <script>
                $(document).ready(function() {
                var currentid = {{entry.id}}; //entry id is defined
                  $("#voteid").bind("keydown", function(e) {
                    if (e.keyCode == 38) {  //up arrow key
                      var args = {'voteid':currentid }; //current id is used
                      $.get("/voteup/", args).done(function(data) { //when .get is done, returns data from views(score)                   
                        console.log("message: " + data);
                        $("#score").text(data); //span updates score with new value from views
                      });
                    return false;
                    }
                    if (e.keyCode == 40) { //down arrow key
                      var args = {'voteid':currentid };     //current id is used
                      $.get("/votedown/", args).done(function(data) { //when .get is done, returns data from views(score)
                        console.log("message: " + data);  
                        $("#score").text(data); //span updates score with new value from views
                      });
                    return false;
                    }
                    if(e.keyCode == 39) { //right key
                      $.get("/random-entry/", {}).done(function(data) {
                        var parts = data.split(",");
                        $("#entry-text").text(parts[1]);
                        $("#score").text(parts[2]);
                        currentid = parts[0];
                        console.log("random entry data: " + data);
                      });
                     return false; 
                     }      
                  });
                });     
              </script>
         {% endfor %}
          </div>
          </div>
      </div>    
    

    views.py

    from django.shortcuts import render
    from django.http import HttpResponse, HttpResponseRedirect
    from entries.models import Entry
    from datetime import datetime
    from django.utils import timezone
    from django.views.decorators.csrf import csrf_protect
    
    def index(request):   
     context = {
      'latest_entry_list': Entry.objects.order_by('-pub_date')[:10],
      'high_entry_list': Entry.objects.order_by('-score','-pub_date')[:10],
      'high_entry': Entry.objects.order_by('-score','-pub_date')[:1],
      'low_entry_list': Entry.objects.order_by('score','-pub_date')[:10],
      'voting_entry': Entry.objects.unvoted_or_random(),
     }
     return render(request, 'entries/index.html', context);
    
    def add(request):
     created_date = default=datetime.now()
     created_text = request.GET.get('text')    
     e = Entry(text=created_text,pub_date=created_date) 
     e.save()       
     return HttpResponse('done')
    
    def enter(request):
     return render(request, 'entries/enter.html'); 
    
    def top(request):
     context = {
      'high_entry': Entry.objects.order_by('-score','-pub_date')[:1],
     }
     return render(request, 'entries/top.html', context);
    
    def voting(request):
     context = {'voting_entry': Entry.objects.random(),}      
     return render(request, 'entries/voting.html', context);
    
    def random_entry(request):
     e = Entry.objects.random()[0]
     return HttpResponse("%s,%s,%s" % (e.id, e.text, e.score))
    
    def voteup(request):
     voting_id = request.GET.get('voteid')
     if request.method=='GET':
        v = Entry.objects.get(pk=voting_id)
        v.score +=1
        v.voted=True
        v.save()
        return HttpResponse(v.score)
     else:
        pass
     return HttpResponse('done')   
    
    def votedown(request):
     voting_id = request.GET.get('voteid')
     if request.method=='GET':
        v = Entry.objects.get(pk=voting_id)
        v.score -=1
        v.voted=True
        v.save()
        return HttpResponse(v.score)
     else:
        pass
     return HttpResponse('done')