Making a global navigation autocomplete

This guide demonstrates how to make a global navigation autocomplete like on http://betspire.com or facebook.

There is a living example in test_project/navigation_autocomplete, which this page describes.

Note that there are many ways to implement such a feature, we’re just describing a simple one.

A simple view

As we’re just going to use autocomplete.js for this, we only need a view to render the autocomplete:

from django import shortcuts
from django.db.models import Q
from django.contrib.auth.models import User, Group

from cities_light.models import Country, Region, City


def navigation_autocomplete(request,
    template_name='navigation_autocomplete/autocomplete.html'):

    q = request.GET.get('q', '')
    context = {'q': q}

    queries = {}
    queries['users'] = User.objects.filter(
        Q(username__icontains=q) |
        Q(first_name__icontains=q) |
        Q(last_name__icontains=q) |
        Q(email__icontains=q)
    ).distinct()[:3]
    queries['groups'] = Group.objects.filter(name__icontains=q)[:3]
    queries['cities'] = City.objects.filter(search_names__icontains=q)[:3]
    queries['regions'] = Region.objects.filter(name_ascii__icontains=q)[:3]
    queries['countries'] = Country.objects.filter(name_ascii__icontains=q)[:3]

    context.update(queries)

    return shortcuts.render(request, template_name, context)

And a trivial template (test_project/navigation_autocomplete/templates/navigation_autocomplete/autocomplete.html):

{% load url from future %}

{% for country in countrys %}
<a class="div choice" href="{% url 'admin:cities_light_country_change' country.pk %}">{{ country }}</a>
{% endfor %}
{% for region in regions %}
<a class="div choice" href="{% url 'admin:cities_light_region_change' region.pk %}">{{ region }}</a>
{% endfor %}
{% for city in cities %}
<a class="div choice" href="{% url 'admin:cities_light_city_change' city.pk %}">{{ city }}</a>
{% endfor %}
{% for group in groups %}
<a class="div choice" href="{% url 'admin:auth_group_change' group.pk %}">{{ group }}</a>
{% endfor %}
{% for user in users %}
<a class="div choice" href="{% url 'admin:auth_user_change' user.pk %}">{{ user }}</a>
{% endfor %}

And of course a url:

from django.conf.urls import patterns, url

urlpatterns = patterns('navigation_autocomplete.views',
    url(r'^$', 'navigation_autocomplete', name='navigation_autocomplete'),
)

A basic autocomplete configuration

That’s a pretty basic usage of autocomplete.js (test_project/navigation_autocomplete/templates/navigation_autocomplete/script.html):

{% load url from future %}

<script type="text/javascript">
$(document).ready(function() {
    $('#navigation_autocomplete').yourlabsAutocomplete({
        url: '{% url 'navigation_autocomplete' %}',
        choiceSelector: 'a',
    }).input.bind('selectChoice', function(e, choice, autocomplete) {
        document.location.href = choice.attr('href');
    });
});
</script>

Which works on such a simple input (test_project/navigation_autocomplete/templates/navigation_autocomplete/input.html):

<input type="text" name="q" id="navigation_autocomplete" style="width: 270px; font-size: 16px;" />
<style type="text/css">
    /* cancel out django default for now, or choices are white on white */
    #header a.choice:link, #header a.choice:visited {
        color: black;
    }
</style>

See how admin/base_site.html includes them:

{% extends "admin/base.html" %}
{% load i18n %}

{% block extrahead %}
    {# These are needed to enable autocompletes in forms #}
    <script src="{{ STATIC_URL }}jquery.js" type="text/javascript"></script>
    {% include 'autocomplete_light/static.html' %}

    {% if user.is_authenticated %}
        {# This script enables global navigation autocomplete #}
        {# refer to the docs about global navigation autocomplete for more information #}
        {% include 'navigation_autocomplete/script.html' %}
    {% endif %}
{% endblock %}

{% block branding %}
    <h1 style="display: inline-block" id="site-name">{% trans 'Autocomplete-light demo' %}</h1>

    {% if user.is_authenticated %}
        {% comment %}
        This is a simple input, used for global navigation autocomplete. It
        serves as an example, refer to the documentation to make a navigation
        autocomplete.

        FYI It leaves in test_project/navigation_autocomplete/templates
        {% endcomment %}
        {% include 'navigation_autocomplete/input.html' %}
    {% endif %}
{% endblock %}