.. _ajax-label: AJAX, Django and JQuery ======================= To make the interaction with the Rango application more seamless let's add in a number of features that use AJAX, such as: * Add a "Like Button" to let registered users "like" a particular category * Add inline category suggestions - so that when a user types they can quickly find a category * Add an "Add Button" to let registered users quickly and easily add a Page to the Category AJAX essentially is a combination of technologies that are integrated together to reduce the number of page loads. Instead of reloading the full page, only part of the page or the data in the page is reloaded. If you haven't used AJAX before or would like to know more about it before using it, check out the resources at the Mozilla website: https://developer.mozilla.org/en-US/docs/AJAX To simplify the AJAX components you can use a library like JQuery. If you are using the Twitter CSS Bootstrap toolkit then JQuery will already be added in. Otherwise, download the latest version of JQuery and include it within your application. To include JQuery within your application, in the static folder create a *js* folder and plonk the JQuery javascript file (``jquery.js``) here along with an file called ``rango-ajax.js``, which will house our javascript code. In ``rango-ajax.js``, add the following javascript: .. code-block:: javascript $(document).ready(function() { // JQuery code to be added in here. }); Then in your *base* template include: .. code-block:: html If you aren't familiar with JQuery it is worth checking out http://jquery.com and going through some examples in the documentation. The documentation provides numerous worked examples of the different functionality that the JQuery API provides. Add a "Like Button" -------------------- It would be nice to let user, who are registered, denote that they "like" a particular category. In the following workflow, we will let users "like" categories, but we will not be keeping track of what categories they have "liked", we'll be trusting them not to click the like button multiple times. Workflow ........ To let users "like" certain categories undertake the following workflow: * In the ``category.html`` template: - Add in a "Like" button with ``id="like"``. - Add in a template tag to display the number of likes: ``{{% category.likes %}}`` - Place this inside a div with ``id="like_count"``, i.e. ``
{{ category.likes }}
`` - This sets up the template to capture likes and to display likes for the category. * Update the ``category`` view to pass through the number of likes for the category. * Create a view called, ``like_category`` which will examine the request and pick out the ``category_id`` and then increment the number of likes for that category. - Don't forgot to add in the url mapping; i.e map the ``like_category'' view to ``rango/like_category/``. The GET request will then be ``rango/like_category/?category_id=XXX`` - Instead of return a HTML page have this view will return the new total number of likes for that category. * Now in "rango-ajax.js" add the JQuery code to perform the AJAX GET request. - If the request is successful, then update the #like_count element, and hide the like button. Updating Category Template .......................... To prepare the template we will need to add in the "Like" button with id="like" and create a ``
to display the number of likes ``{{% category.likes %}}``. To do this, add the following ``
`` to the *category.html* template: .. code-block:: html

{{ category.likes }} people like this category {% if user.is_authenticated %} {% endif %}

Update the Category View ........................ To display the number of likes and the "Likes" button modify the ``category`` view: .. code-block:: python def category(request, category_name_url): context = RequestContext(request) cat_list = get_category_list() category_name = decode_url(category_name_url) context_dict = {'cat_list': cat_list, 'category_name': category_name} try: category = Category.objects.get(name=category_name) # Add category to the context so that we can access the id and likes context_dict['category'] = category pages = Page.objects.filter(category=category) context_dict['pages'] = pages except Category.DoesNotExist: pass return render_to_response('rango/category.html', context_dict, context) Create a Like Category View ........................... Create a view called, ``like_category`` in ``rango/views.py`` which will examine the request and pick out the category_id and then increment the number of likes for that category. .. code-block:: python from django.contrib.auth.decorators import login_required @login_required def like_category(request): context = RequestContext(request) cat_id = None if request.method == 'GET': cat_id = request.GET['category_id'] likes = 0 if cat_id: category = Category.objects.get(id=int(cat_id)) if category: likes = category.likes + 1 category.likes = likes category.save() return HttpResponse(likes) On examining the code, you will see that we are only allowing authenticated users to denote that they like a category. The view assumes that a variable ``category_id`` has been passed through via a GET so that the we can identify the category to update. In this view, we could also track and record that a particular user has "liked" this category if we wanted - but we are keeping it simple to focus on the AJAX mechanics. Don't forget to add in the URL mapping, into ``rango/urls.py``. Update the ``urlpatterns`` by adding in: .. code-block:: python url(r'^like_category/$', views.like_category, name='like_category'), Making the AJAX request ....................... Now in "rango-ajax.js" you will need to add some JQuery code to perform an AJAX GET request. Add in the following code: .. code-block:: javascript $('#likes').click(function(){ var catid; catid = $(this).attr("data-catid"); $.get('/rango/like_category/', {category_id: catid}, function(data){ $('#like_count').html(data); $('#likes').hide(); }); }); This piece of JQuery/Javascript will add an event handler to the element with id ``#likes``, i.e. the button. When clicked, it will extract the category id from the button element, and then make an AJAX GET request which will make a call to ``/rango/like_category/`` encoding the ``category id`` in the request. If the request is successful, then the HTML element with id like_count (i.e. the ) is updated with the data returned by the request, and the HTML element with id likes (i.e. the {% endif %} JQuery code: .. code-block: javascript $('.rango-add').click(function(){ var catid = $(this).attr("data-catid"); var url = $(this).attr("data-url"); var title = $(this).attr("data-title"); var me = $(this) $.get('/rango/auto_add_page/', {category_id: catid, url: url, title: title}, function(data){ $('#pages').html(data); me.hide(); }); }); Note here we are assigned the event handler to all the buttons with class ``rango-add``. View code: .. code-block:: python @login_required def auto_add_page(request): context = RequestContext(request) cat_id = None url = None title = None context_dict = {} if request.method == 'GET': cat_id = request.GET['category_id'] url = request.GET['url'] title = request.GET['title'] if cat_id: category = Category.objects.get(id=int(cat_id)) p = Page.objects.get_or_create(category=category, title=title, url=url) pages = Page.objects.filter(category=category).order_by('-views') # Adds our results list to the template context under name pages. context_dict['pages'] = pages return render_to_response('rango/page_list.html', context_dict, context)