Welcome to django-autocomplete-light’s documentation!¶
Features¶
This app fills all your ajax autocomplete needs:
- global navigation autocomplete like on http://betspire.com
- autocomplete widget for ModelChoiceField and ModelMultipleChoiceField
- 0 hack required for admin integration, just use a form that uses the widget
- no jQuery-ui required, the autocomplete script is as simple as possible
- all the design of the autocompletes is encapsulated in template, unlimited design possibilities
- 99% of the python logic is encapsulated in “channel” classes, unlimited development possibilities
- 99% the javascript logic is encapsulated in a class, you can override any attribute or method, unlimited development possibilities
- 0 inline javascript you can load the javascript just before </body> for best page loading performance
- simple python, html and javascript, easy to hack
- less sucking code, no funny hacks, clean api, as few code as possible, that also means this is not for pushovers
Full documentation¶
Quick start¶
The purpose of this documentation is to get you started as fast as possible, because your time matters and you probably have other things to worry about.
Quick install¶
Install the package:
pip install django-autocomplete-light
# or the development version
pip install -e git+git://github.com/yourlabs/django-autocomplete-light.git#egg=django-autocomplete-light
Add to INSTALLED_APPS: ‘autocomplete_light’
Add to urls:
url(r'autocomplete/', include('autocomplete_light.urls')),
Add before admin.autodiscover():
import autocomplete_light
autocomplete_light.autodiscover()
Add to your base template:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
{% load autocomplete_light_tags %}
{% autocomplete_light_static %}
Quick admin integration¶
Create yourapp/autocomplete_light_registry.py:
import autocomplete_light
from models import Author
autocomplete_light.register(Author)
In yourapp/admin.py:
from django.contrib import admin
import autocomplete_light
from models import Book
class BookAdmin(admin.ModelAdmin):
# use an autocomplete for Author
form = autocomplete_light.modelform_factory(Book)
admin.site.register(Book, BookAdmin)
Quick form integration¶
AutocompleteWidget is usable on ModelChoiceField and ModelMultipleChoiceField.
Integration with forms¶
The purpose of this documentation is to describe every element in a chronological manner. Because you want to know everything about this app and hack like crazy.
It is complementary with the quick documentation.
Django startup¶
Registry¶
Autodiscovery is part of the autocomplete_light.registry module:
Autodiscovery is the first thing that happens as it is called early in urls.py:
autocomplete_light.registry.autodiscover fills autocomplete_light.registry.registry, which is an instance of ChannelRegistry:
Channels¶
As you can see, registration creates a channel if it is only passed a model. You’ll want to make your own channel class, this is what a channel looks like:
Forms¶
To save you some boilerplate, a couple of helpers are provided:
Page rendering¶
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>
{% load autocomplete_light_tags %}
{% autocomplete_light_static %}
That said, 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>
Widget in action¶
Widget definition¶
The first thing that happens is the definition of an AutocompleteWidget in a form:
Widget rendering¶
This is what the default widget template looks like:
{% load i18n %}
{% load autocomplete_light_tags %}
{% comment %}
The outer element is called the 'widget wrapper'. It contains some data
attributes to communicate between Python and JavaScript. And of course, it
wraps around everything the widget needs.
{% endcomment %}
<span class="autocomplete_light_widget {{ name }}" id="{{ widget.html_id }}_wrapper"
data-maxitems="{{ widget.max_items }}"
data-mincharacters="{{ widget.min_characters }}"
data-bootstrap="{{ widget.bootstrap }}">
{# a text input, that is the 'autocomplete input' #}
<input type="text" class="autocomplete" name="{{ name }}_autocomplete" id="{{ widget.html_id }}_text" value="" {{ extra_attrs }} />
{# a hidden select, that contains the actual selected values #}
<select style="display:none" class="valueSelect" name="{{ name }}" id="{{ widget.html_id }}" {% if widget.max_items != 1 %}multiple="multiple"{% endif %}>
{% for value in values %}
<option value="{{ value }}" selected="selected">{{ value }}</option>
{% endfor %}
</select>
{# a deck that should contain the list of selected options #}
<ul id="{{ html_id }}_deck" class="deck" >
{% for result in results %}
{{ result|autocomplete_light_result_as_html:channel }}
{% endfor %}
</ul>
{# a hidden textarea that contains some json about the channel #}
<textarea class="json_channel" style="display:none">
{{ json_channel }}
</textarea>
{# a hidden div that serves as template for the 'remove from deck' button #}
<div style="display:none" class="remove">
{# This will be appended to results on the deck, it's the remove button #}
X
</div>
<ul style="display:none" class="add_template">
{% 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 %}
<li class="result">
</li>
</ul>
</span>
Javascript initialization¶
deck.js initializes all widgets that have bootstrap=’normal’ (the default), as you can see:
$('.autocomplete_light_widget[data-bootstrap=normal]').each(function() {
$(this).yourlabs_deck();
});
If you want to initialize the deck 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_deck({
getValue: function(result) {
// your own logic to get the value from an html result
return ...;
}
});
});
yourapp/static/yourapp/autocomplete_light.js will be automatically collected by by autodiscover, and the script tag generated by {% autocomplete_light_static %}.
In django-cities-light source, you can see a more interresting example where two autocompletes depend on each other.
You should take a look at the code of autocomplete.js and deck.js, as it lets you override everything.
One interresting note is that the plugins (yourlabs_autocomplete and yourlabs_deck) hold a registry. Which means that:
- calling someElement.yourlabs_deck() will instanciate a deck with the passed overrides
- calling someElement.yourlabs_deck() again will return the deck instance for someElement
Javascript cron¶
deck.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 deck, and adds it to the deck 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.
Javascript events¶
When the autocomplete input is focused, autocomplete.js checks if there are enought caracters in the input to display an autocomplete box. If minCharacters 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 channel view. The channel view will delegate the rendering of the autocomplete box to the actual channel. So that you can override anything you want directly in the channel.
Then, autocomplete.js recognizes options with a selector. By default, it is ‘.result’. This means that any element with the ‘.result’ class in the autocomplete box is considered as an option.
When an option is selected, deck.js calls it’s method getValue() and adds this value to the hidden select. Also, it will copy the result html to the deck.
When an option is removed from the deck, deck.js also removes it from the hidden select.
When things go wrong¶
If you don’t know how to debug, you should learn to use:
- Firebug javascript debugger
- Open the script tab, select a script, click on the left of the code to place a breakpoint
- Ipdb python debugger
- Install ipdb with pip, and place in your python code: import ipdb; ipdb.set_trace()
If you are able to do that, then you are a professional, enjoy autocomplete_light !!!
If you need help, open an issue on the github issues page.
But make sure you’ve read how to report bugs effectively and how to ask smart questions.
Also, don’t hesitate to do pull requests !