Table Of Contents

Previous topic

16. Making Rango Tango! Exercises

Next topic

18. JQuery and Django

This Page


A newer version of this tutorial using Django 1.9 is available from Leanpub:

17. Making Rango Tango! Code and Hints

Hopefully, you will have been able to complete the exercises given the workflows we provided, if not, or if you need a little help checkout snippets of code and use them within your version of Rango.

17.1. Track Page Click Throughs

Currently, Rango provides a direct link to external pages. This is not very good if you want to track the number of times each page is clicked and viewed. To count the number of times a page is viewed via Rango you will need to perform the following steps.

17.1.1. Creating a URL Tracking View

Create a new view called track_url() in /rango/ which takes a parameterised HTTP GET request (i.e. rango/goto/?page_id=1) and updates the number of views for the page. The view should then redirect to the actual URL.

from django.shortcuts import redirect

def track_url(request):
    page_id = None
    url = '/rango/'
    if request.method == 'GET':
        if 'page_id' in request.GET:
            page_id = request.GET['page_id']
                page = Page.objects.get(id=page_id)
                page.views = page.views + 1
                url = page.url

    return redirect(url)

Be sure that you import the redirect() function to if it isn’t included already!

from django.shortcuts import redirect

17.1.2. Mapping URL

In /rango/ add the following code to the urlpatterns tuple.

url(r'^goto/$', views.track_url, name='goto'),

17.1.3. Updating the Category Template

Update the category.html template so that it uses rango/goto/?page_id=XXX instead of providing the direct URL for users to click.

{% for page in pages %}
            <a href="{% url 'goto' %}?page_id={{}}">{{ page.title }}</a>
        {% if page.views > 1 %}
            ({{ page.views }} views)
            {% elif page.views == 1 %}
            ({{ page.views }} view)
        {% endif %}
{% endfor %}

Here you can see that in the template we have added some control statements to display view, views or nothing depending on the value of page.views.

17.1.4. Updating Category View

Since we are tracking the number of click throughs you can now update the category() view so that you order the pages by the number of views, i.e:

pages = Page.objects.filter(category=category).order_by('-views')

Now, confirm it all works, by clicking on links, and then going back to the category page. Don’t forget to refresh or click to another category to see the updated page.

17.2. Searching Within a Category Page

Rango aims to provide users with a helpful directory of page links. At the moment, the search functionality is essentially independent of the categories. It would be nicer however to have search integrated into category browsing. Let’s assume that a user will first browse their category of interest first. If they can’t find the page that they want, they can then search for it. If they find a page that is suitable, then they can add it to the category that they are in. Let’s tackle the first part of this description here.

We first need to remove the global search functionality and only let users search within a category. This will mean that we essentially decommission the current search page and search view. After this, we’ll need to perform the following.

17.2.2. Creating a Search Form Template

Take the search form from search.html and put it into the category.html. Be sure to change the action to point to the category() view as shown below.

 <form class="form-inline" id="user_form" method="post" action="{% url 'category'  category.slug %}">
     {% csrf_token %}
     <!-- Display the search form elements here -->
     <input class="form-control" type="text" size="50" name="query" value="{{query}}" id="query" />
     <input class="btn btn-primary" type="submit" name="submit" value="Search" />

Also include a <div> to house the results underneath.

<div class="panel">
        {% if result_list %}
        <div class="panel-heading">
                <h3 class="panel-title">Results</h3>
                <!-- Display search results in an ordered list -->
                <div class="panel-body">
                <div class="list-group">
                        {% for result in result_list %}
                    <div class="list-group-item">
                        <h4 class="list-group-item-heading"><a href="{{ }}">{{ result.title }}</a></h4>
                        <p class="list-group-item-text">{{ result.summary }}</p>
                {% endfor %}
    {% endif %}

17.2.3. Updating the Category View

Update the category view to handle a HTTP POST request (i.e. when the user submits a search) and inject the results list into the context. The following code demonstrates this new functionality.

def category(request, category_name_slug):
    context_dict = {}
    context_dict['result_list'] = None
    context_dict['query'] = None
    if request.method == 'POST':
        query = request.POST['query'].strip()

        if query:
            # Run our Bing function to get the results list!
            result_list = run_query(query)

            context_dict['result_list'] = result_list
            context_dict['query'] = query

        category = Category.objects.get(slug=category_name_slug)
        context_dict['category_name'] =
        pages = Page.objects.filter(category=category).order_by('-views')
        context_dict['pages'] = pages
        context_dict['category'] = category
    except Category.DoesNotExist:

    if not context_dict['query']:
        context_dict['query'] =

    return render(request, 'rango/category.html', context_dict)

Notice that in the context_dict that we pass through, we will include the result_list and query, and if there is no query, we provide a default query, i.e. the category name. The query box then displays this variable.