django-voting: A brief tutorial

django-voting is a reusable application which gives you reddit-style voting capabilities on generic objects. The application, itself, is a very well documented piece of software. Then again, that's not saying a lot considering its from Jonathan Buchannan. All of his projects are famously documented.

Installation

The installation of this application was standard fare for a google code app. SVN Checkout the application to a point on your python path and that's about it. For me, the command was: ::

svn co http://django-voting.googlecode.com/svn/trunk/voting/ voting

From there, you simply add 'voting' to your INSTALLEDAPPS, syncdb, and you're ready to go.

Make It Work

Okay, so now that its installed, you need to pull it into a view. For this, I'm using a generic view and the template tags provided. Below is the relevant part of the template.

{% score_for_object object as score %}
<h5>Votes <span id="score">{{ score.score }}</span> point{{ score.score|pluralize }}
         after <span id="num_votes">{{ score.num_votes }}</span> vote{{ score.num_votes|pluralize }}</h5>
<ul>
    <li><a href="#" onclick="vote('{{ object.slug }}', 'up');">I like it!</a></li>
    <li><a href="#" onclick="vote('{{ object.slug }}', 'down');">I hate it!</a></li>
    <li><a href="#" onclick="vote('{{ object.slug }}', 'clear');">I take it back! Clear my vote.</a></li>
</ul>

When clicking this textual link, it calls a "vote" function with the slug and the direction you're casting your vote. This jQuery function is:

function vote(slug, direction) {
    $.post('/tips/'+slug+'/'+direction+'vote/', {HTTP_X_REQUESTED:'XMLHttpRequest'},
           function(data) {
               if (data.success == true) {
                   $('#score').text(data.score.score);
                   $('#num_votes').text(data.score.num_votes);
               } else {
                   alert('ERROR: ' + data.error_message);
               }
           }, 'json'
          )
}

So there are a few things here to talk about. When we click the link, the vote function is called. It takes the slug of the item we're voting on and the direction you're voting in and builds a url. This doesn't present the most reusable option, but fills my use case well enough. It passes along a POST parameter which tells the voting app that this request is made via XMLHttpRequest (thereby bypassing confirmation of your vote). This url is getting passed through to a generic view provided by the django-voting application. The entry in the urls.py is below.

from django.conf.urls.defaults import *
from tips.models import Tip
from voting.views import vote_on_object

tip_dict = {
    'model': Tip,
    'template_object_name': 'tip',
    'slug_field': 'slug',
    'allow_xmlhttprequest': 'true',
}

urlpatterns = patterns('',
   url(r'^(?P[-\w]+)/(?Pup|down|clear)vote/?$', vote_on_object, tip_dict, name="tip-voting"),
)

Once the post result has been made, the javascript snippet parses the resulting JSON and updates the result via jQuery's text() function. And there we have it. A simplistic example of voting on a given object. It does exactly what it needs to do in the smallest amount of code possible. A fantastic reusable app!