The purpose of this documentation is to describe every element in a chronological manner.
It is complementary with the quick documentation.
The registry module provides tools to maintain a registry of autocompletes.
The first thing that should happen when django starts is registration of autocompletes. It should happen first, because autocompletes are required for widgets. And autocomplete widgets are required for forms. And forms are required for ModelAdmin.
It looks like this:
Dict with some shortcuts to handle a registry of autocompletes.
Register an autocomplete.
Two unordered arguments are accepted, at least one should be passed:
‘name’ is also an acceptable keyword argument, that can be used to override the default autocomplete name (which is its class name).
In addition, keyword arguments will be set as class attributes. For thread safety reasons, a copy of the autocomplete class is stored in the registry.
Check all apps in INSTALLED_APPS for stuff related to autocomplete_light.
For each app, autodiscover imports app.autocomplete_light_registry if available, resulting in execution of register() statements in that module, filling registry.
Consider a standard app called ‘cities_light’ with such a structure:
cities_light/
__init__.py
models.py
urls.py
views.py
autocomplete_light_registry.py
With such a autocomplete_light_registry.py:
from models import City, Country
import autocomplete_light
autocomplete_light.register(City)
autocomplete_light.register(Country)
When autodiscover() imports cities_light.autocomplete_light_registry, both CityAutocomplete and CountryAutocomplete will be registered. For details on how these autocomplete classes are generated, read the documentation of AutocompleteRegistry.register.
Simple model autocomplete:
import autocomplete_light
from cities_light.models import City
autocomplete_light.register(City, search_fields=('search_names',),
autocomplete_js_attributes={'placeholder': 'city name ..'})
Slightly advanced autocomplete:
import autocomplete_light
from cities_light.models import Country, City
from django.contrib.auth.models import User, Group
class AutocompleteTaggableItems(autocomplete_light.AutocompleteGenericBase):
choices = (
User.objects.all(),
Group.objects.all(),
City.objects.all(),
Country.objects.all(),
)
search_fields = (
('username', 'email'),
('name',),
('search_names',),
('name_ascii',),
)
autocomplete_light.register(AutocompleteTaggableItems)
This is the minimum to implement in a custom Autocomplete class. It has two attributes:
An autocomplete proposes “choices”. A choice has a “value”. When the user selects a “choice”, then it is converted to a “value”.
Class constructor sets the given request and values as instance attributes, casting values to list if necessary.
Return the HTML autocomplete that should be displayed under the text input. Use self.request if set.
A basic implementation of AutocompleteInterface that renders HTML and should fit most cases. However, it requires to overload choices_for_request().
Class constructor sets the given request and values as instance attributes, casting values to list if necessary.
There are many autocompletes you can use, just to name a few:
Each of them should have tests in autocomplete_light/tests and at least one example app in test_project/.
Note
Due to Django’s issue #9321, you may have to use autocomplete_light.FixedModelForm instead of django.forms.ModelForm. Otherwise, you might see help text like ‘Hold down “Control” key ...’ for MultipleChoiceWidgets.
A more high level API is also available:
A couple of helper functions to help enabling Widget in ModelForms.
Return a dict of field_name: widget_instance for model that is compatible with Django.
Inspect the model’s field and many to many fields, calls registry.autocomplete_for_model to get the autocomplete for the related model. If a autocomplete is returned, then an Widget will be spawned using this autocomplete.
The dict is usable by ModelForm.Meta.widgets. In django 1.4, with modelform_factory too.
Wraps around Django’s django_modelform_factory, using get_widgets_dict.
Basically, it will use the dict returned by get_widgets_dict in order and pass it to django’s modelform_factory, and return the resulting modelform.
It is important to load jQuery first, and then autocomplete_light and application specific javascript, it can look like this:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
{% include 'autocomplete_light/static.html' %}
However, autocomplete_light/static.html also includes “remote.js” which is required only by remote channels. If you don’t need it, you could either load the static dependencies directly in your template, or override autocomplete_light/static.html:
{% load static %}
{% include 'autocomplete_light/_ajax_csrf.html' %}
<script type="text/javascript" src="{% static 'autocomplete_light/autocomplete.js' %}"></script>
<script type="text/javascript" src="{% static 'autocomplete_light/widget.js' %}"></script>
<script type="text/javascript" src="{% static 'autocomplete_light/addanother.js' %}"></script>
<script type="text/javascript" src="{% static 'autocomplete_light/text_widget.js' %}"></script>
<script type="text/javascript" src="{% static 'autocomplete_light/remote.js' %}"></script>
<link rel="stylesheet" type="text/css" href="{% static 'autocomplete_light/style.css' %}" />
Or, if you only want to make a global navigation autocomplete, you only need:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}autocomplete_light/autocomplete.js" type="text/javascript"></script>
To enable autocomplete form widgets, you need to load:
Optionally:
A quick way to enable all this in the admin, is to replace template admin/base_site.html, ie.:
{% extends "admin/base.html" %}
{% block extrahead %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
{% include 'autocomplete_light/static.html' %}
{% endblock %}
The first thing that happens is the definition of an AutocompleteWidget in a form.
ChoiceWidget is intended to work as a replacement for django’s Select widget, and MultipleChoiceWidget for django’s SelectMultiple.
Constructing a widget needs an Autocomplete class or registered autocomplete name.
The choice autocomplete widget renders from autocomplete_light/widget.html template.
Base widget for autocompletes.
Mainly handles passing arguments from Python to HTML data-* attributes, via widget_js_attributes and autocomplete_js_attributes. Javascript will parse these data-* attributes.
This widget also renders the widget template.
Widget that provides an autocomplete for zero to one choice.
Widget that provides an autocomplete for zero to n choices.
Widget that just adds an autocomplete to fill a text input
Example, overriding some widget.js and autocomplete.js options directly from Python:
from django import forms
from django.contrib.auth.models import User
from cities_light.models import City
import autocomplete_light
from models import Profile
class ProfileForm(forms.ModelForm):
user = forms.ModelChoiceField(User.objects.all(),
widget=autocomplete_light.ChoiceWidget('UserAutocomplete'))
cities = forms.ModelMultipleChoiceField(City.objects.all(),
widget=autocomplete_light.MultipleChoiceWidget('CityAutocomplete',
# optionnal: override an autocomplete.js option
autocomplete_js_attributes={'minimum_characters': 0,
'placeholder': 'Choose 3 cities ...'},
# optionnal: override a widget.js option
widget_js_attributes={'max_values': 3}))
# Note that defining *_js_attributes on Autocomplete classes or instances
# also work.
class Meta:
model = Profile
Note that those have priority over those that are defined at the autocomplete level:
from django.contrib.auth.models import User
import autocomplete_light
class UserAutocomplete(autocomplete_light.AutocompleteModelBase):
search_fields = ('username', 'email', 'first_name', 'last_name')
# Note that defining *_js_attributes in a Widget also works. Widget has
# priority since it's the most specific.
autocomplete_js_attributes = {
'placeholder': 'type a user name ...',
}
autocomplete_light.register(User, UserAutocomplete)
This is what the default widget template looks like:
{% load i18n %}
{% load staticfiles %}
{% load autocomplete_light_tags %}
{% load url from future %}
{% block widget_open %}
<span class="autocomplete-light-widget {{ name }}
{% if widget.widget_js_attributes.max_values == 1 %}single{% else %}multiple{% endif %}"
id="{{ widget.html_id }}-wrapper"
{{ widget.widget_js_attributes|autocomplete_light_data_attributes }}
{{ widget.autocomplete_js_attributes|autocomplete_light_data_attributes:'autocomplete-' }}
>
{% endblock %}
{% block deck %}
{# a deck that should contain the list of selected options #}
{{ choices }}
<span id="{{ widget.html_id }}-deck" class="deck div" >
{% for choice in autocomplete.choices_for_values %}
{{ choice|autocomplete_light_choice_html:autocomplete }}
{% endfor %}
</span>
{% endblock %}
{% block input %}
{# a text input, that is the 'autocomplete input' #}
<input type="text" class="autocomplete" name="{{ name }}-autocomplete" id="{{ widget.html_id }}_text" value="" {{ extra_attrs }} />
{% endblock %}
{% block add_another %}
{# A link to add a new choice using a popup #}
{% if autocomplete.add_another_url_name %}
<a href="{% block add_another_href %}{% url autocomplete.add_another_url_name %}?_popup=1{% endblock %}" class="autocomplete-add-another" id="add_{{ widget.html_id }}" style="display:none;">
<img src="{% static 'admin/img/icon_addlink.gif' %}" width="10" height="10" alt="{% trans 'Add another' %}" />
</a>
{% endif %}
{% endblock %}
{% block select %}
{# a hidden select, that contains the actual selected values #}
<select style="display:none" class="value-select" name="{{ name }}" id="{{ widget.html_id }}" multiple="multiple">
{% for value in values %}
<option value="{{ value }}" selected="selected">{{ value }}</option>
{% endfor %}
</select>
{% endblock %}
{% block remove_template %}
{# a hidden div that serves as template for the 'remove from deck' button #}
<span style="display:none" class="remove div">
{# This will be appended to choices on the deck, it's the remove button #}
X
</span>
{% endblock %}
{% block choice_template %}
<span style="display:none" class="choice-template div">
{% comment %}
the contained element will be used to render options that are added to the select
via javascript, for example in django admin with the + sign
The text of the option will be inserted in the html of this tag
{% endcomment %}
<span class="choice div prepend-remove append-option-html">
</span>
</span>
{% endblock %}
{% block widget_close %}
</span>
{% endblock %}
widget.js initializes all widgets that have bootstrap=’normal’ (the default), as you can see:
$('.autocomplete-light-widget[data-bootstrap=normal]').each(function() {
$(this).autocompleteWidget();
});
If you want to initialize the widget yourself, set the widget or channel bootstrap to something else, say ‘yourinit’. Then, add to yourapp/static/yourapp/autocomplete_light.js something like:
$('.autocomplete-light-widget[data-bootstrap=yourinit]').each(function() {
$(this).yourlabs_widget({
getValue: function(choice) {
// your own logic to get the value from an html choice
return ...;
}
});
});
Also, load yourapp/autocomplete_light.js in your override of autocomplete_light/static.html.
You should take a look at the docs of autocomplete.js and widget.js, as it lets you override everything.
One interresting note is that the plugins (yourlabsAutocomplete and autocompleteWidget) hold a registry. Which means that:
This is exactly what you need to use to make autocompletes that depend on each other.
widget.js includes a javascript function that is executed every two seconds. It checks each widget’s hidden select for a value that is not in the widget, and adds it to the widget if any.
This is useful for example, when an item was added to the hidden select via the ‘+’ button in django admin. But if you create items yourself in javascript and add them to the select it would work too.
The reason for that is that adding and selecting an option from a select doesn’t trigger the javascript change event, which is a hudge pity.
When the autocomplete input is focused, autocomplete.js checks if there are enought caracters in the input to display an autocomplete box. If minimumCharacters is 0, then it would open even if the input is empty, like a normal select box.
If the autocomplete box is empty, it will fetch the autocomplete view. That view delegates the rendering of the autocomplete box to the registered autocomplete.
Simple view that routes the request to the appropriate autocomplete.
Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
Return an HttpResponse with the return value of autocomplete.render_autocomplete().
This view is called by the autocomplete script, it is expected to return the rendered autocomplete box contents.
To do so, it gets the autocomplete class from the registry, given the url keyword argument autocomplete, that should be the autocomplete name.
Then, it instanciates the autocomplete with no argument as usual, and calls autocomplete.init_for_request, passing all arguments it recieved.
Finnaly, it makes an HttpResponse with the result of autocomplete.render_autocomplete(). The javascript will use that to fill the autocomplete suggestion box.
Simple rendering of the autocomplete.
Return a choice formated according to self.choice_html_format.
Then, autocomplete.js recognizes options with a selector. By default, it is ‘[data-value]’. This means that any element with a data-value attribute in the autocomplete html is considered a selectable choice.
When an option is selected, widget.js calls it’s method getValue() and adds this value to the hidden select. Also, it will copy the choice html to the widget.
When an option is removed from the widget, widget.js also removes it from the hidden select.