Beardy Geek – Web Development Blog

A blog about web development – giving tips and tutorials about all aspects of web dev.


Beardy Geek – Web Development Blog

Django and Ajax – using Prototype and Scriptaculous

I started adding a sprinkling of Ajax in one of my Django projects, so I thought I’d share a few snippets that might help you add some Ajax goodness into your app.

To handle the Ajax callbacks, I’ll be using Prototype, a lightweight Ajax library. And for a couple of effects, like fade-in, I’ll be using Scriptaculous.

Ajax Voting

I wanted to add a Digg-like voter to my app, where you have the number of votes showing with a vote button, and when you click on the vote button, the number disappears, then fades back in with an increase in the vote. The new vote will be added to the database. I also wanted to only allow each user to only vote on each item once (for obvious reasons!)

Overview

Here’s how it hangs together. The user presses the vote button. This calls back to a url, which has a view, which retrieves and/or stores data, which then renders the output to a template, which is then inserted by magic under the button with the new stored value.

Now on with some code.

Urls

First we need to define a url for the callback.

url(r'^callback/vote/(?P\d+)/$', 'vote', name='callback_vote'),

Models

For the purposes of this post, I’ll create 2 models. The first is an item model, where we can store an item description, and the number of the votes that item has:


class Item(models.Model):
    item_desc = models.CharField(max_length=200)
    votes = models.IntegerField()

Now we can create a Voter model, which stores who voted for which item, so we can prevent multiple voting for each user:


from django.contrib.auth.models import User

class Voter(models.Model):
    voter = models.ForeignKey(User)
    item = models.ForeignKey(Item)

Script Tags

Now we need to import the Javascript for Prototype and Scriptaculous. We can either do this in our base.html, which means the javascript will be loaded on every page, or we could add a block into the header, and just load the javascript on a per page basis. I have also stored the scripts in my MEDIA_URL.


<script type="text/javascript" src="{{ MEDIA_URL }}scripts/prototype.js"></script>
<script type="text/javascript"
    src="{{ MEDIA_URL }}scripts/scriptaculous.js?load=effects"></script>

You’ll see the load=effects option after the scriptaculous url. This is so it doesn’t load all of the modules. You can if you want, but for this example we only need the effect module.

Prototype Script

For the prototype script to work, and update our vote number, we need to have a page element to update. You could have a div or a span, but you need to give the element an id and a name.

Now for the script.


<script type="text/javascript">
	function addVote(item_id) {
		$('item').style.display = 'none';
		var ajax = new Ajax.Request('callback/vote/' + item_id + '/', {
				method: 'get',
				onSuccess: function(transport) {
					var result = $('item');
					result.update(transport.responseText);
				}
			});
		$('item').appear();
	}
</script>

I’m assuming we’ve called the span or div id ‘item’. The $(‘item’) is a short cut to get a reference to the element. Firstly we call the function with the item id as a parameter. Then we set the display to ‘none’ before doing the callback. The callback is done, we update the div element using result.update, and the final line uses the Scriptaculous ‘appear()’ function, which fades the div back in.

Don’t forget to add an onclick() or similar event to what ever you want to use to trigger the callback. In my case I used the tick image.


<a onclick="addVote({{ n.id }})"><img src"..." /></a>

I used n as a context for my item on the view for this page, in case you’re wondering what the {{ n.id }} is for.

Callback Code

Now we have the client side script, we need to take that item id and do something with it, then return the result for display.

First we need to create the vote view.


def vote(request, item_id):
    i = Item.objects.get(pk=item_id)
    #see if user has voted
    voter = Voter.objects.filter(voter=request.user, item=i)

    if not voter:
        v = Voter(voter=request.user, item=i)
        v.save()
        i.votes += 1
        i.save()

    return render_to_response('item/callback_vote.html', {'votes':i.votes})

We get our item object first, then check the Voter model to see if the current logged in user has already voted for this item. If they haven’t, then we create a Voter entry, and increase the number of votes by one. We then return the number of votes in the context.

Callback Template

Now we create a simple template to display the vote view.


{{ votes }}

Conclusion

And there we have it. That’s the basics of Ajax in Django. There’s a lot more to Scriptaculous than just the fade and appear, so check it out.

FacebookTwitterGoogle+LinkedInRedditStumbleUponShare