Autocomplete classes¶
Note
This chapter assumes that you have been through the entire Quick start: adding simple autocompletes.
Design documentation¶
Any class which implements
AutocompleteInterface
is
guaranteed to work because it provides the methods that are expected by the
view which serves autocomplete contents from ajax, and the methods that are
expected by the form field for validation and by the form widget for rendering.
However, implementing those methods directly would result in duplicate code,
hence AutocompleteBase
. It
contains all necessary rendering logic but lacks any business-logic, which
means that it is not connected to any data.
If you wanted to make an Autocomplete class that implements business-logic
based on a python list, you would end up with
AutocompleteList
.
As you need both business-logic and rendering logic for an Autocomplete class
to be completely usable, you would mix both
AutocompleteBase
and
AutocompleteList
resulting in
AutocompleteListBase
:
If you wanted to re-use your python list business logic with a template based
rendering logic, you would mix
AutocompleteList
and
AutocompleteTemplate
,
resulting in
AutocompleteListTemplate
:
So far, you should understand that rendering and business logic are not coupled
in autocomplete classes: you can make your own business logic (ie. using
redis, haystack …) and re-use an existing rendering logic (ie.
AutocompleteBase
or
AutocompleteTemplate
) and
vice-versa.
For an exhaustive list of Autocomplete classes, refer to the Autocomplete API doc.
One last thing: Autocomplete classes should be registered so that the view can serve them and that form fields and widget be able to re-use them. The registry itself is rather simple and filled with good intentions, refer to Registry API documentation.
Examples¶
Create a basic list-backed autocomplete class¶
Class attributes are thread safe because
register()
always creates a class copy. Hence, registering a custom Autocomplete class for
your model in your_app/autocomplete_light_registry.py
could look like this:
import autocomplete_light.shortcuts as al
class OsAutocomplete(al.AutocompleteListBase):
choices = ['Linux', 'BSD', 'Minix']
al.register(OsAutocomplete)
First, we imported autocomplete_light
’s module. It should contain
everything you need.
Then, we subclassed autocomplete_light.AutocompleteListBase
, setting a list of
OSes in the
choices
attribute.
Finally, we registered the Autocomplete class. It will be registered with the class name by default.
Note
Another way of achieving the above using the register shortcut could be:
autocomplete_light.register(autocomplete_light.AutocompleteListBase,
name='OsAutocomplete', choices=['Linux', 'BSD', 'Minix'])
Using a template to render the autocomplete¶
You could use
AutocompleteListTemplate
instead
of AutocompleteListBase
:
import autocomplete_light.shortcuts as al
class OsAutocomplete(al.AutocompleteListTemplate):
choices = ['Linux', 'BSD', 'Minix']
autocomplete_template = 'your_autocomplete_box.html'
al.register(OsAutocomplete)
Inheriting from
AutocompleteListTemplate
instead
of AutocompleteListBase
like as
show in the previous example enables two optionnal options:
autocomplete_template
which we have customized, if we hadn’t thenAutocompleteTemplate.choice_html()
would have fallen back on the parentAutocompleteBase.choice_html()
,choice_template
which we haven’t set, soAutocompleteTemplate.choice_html()
will fall back on the parentAutocompleteBase.choice_html()
,
See Design documentation for details.
Note
Another way of achieving the above could be:
autocomplete_light.register(autocomplete_light.AutocompleteListTemplate,
name='OsAutocomplete', choices=['Linux', 'BSD', 'Minix'],
autocomplete_template='your_autocomplete_box.html')
Creating a basic model autocomplete class¶
Registering a custom Autocomplete class for your model in
your_app/autocomplete_light_registry.py
can look like this:
from models import Person
class PersonAutocomplete(autocomplete_light.AutocompleteModelBase):
search_fields = ['^first_name', 'last_name']
autocomplete_light.register(Person, PersonAutocomplete)
In the same fashion, you could have used
AutocompleteModelTemplate
instead of
AutocompleteModelBase
. You can see
that the inheritance diagram follows the same pattern:
Note
An equivalent of this example would be:
autocomplete_light.register(Person,
search_fields=['^first_name', 'last_name'])
Overriding the queryset of a model autocomplete to secure an Autocomplete¶
You can override any method of the Autocomplete class. Filtering choices based on the request user could look like this:
class PersonAutocomplete(autocomplete_light.AutocompleteModelBase):
search_fields = ['^first_name', 'last_name'])
model = Person
def choices_for_request(self):
if not self.request.user.is_staff:
self.choices = self.choices.filter(private=False)
return super(PersonAutocomplete, self).choices_for_request()
# we have specified PersonAutocomplete.model, so we don't have to repeat
# the model class as argument for register()
autocomplete_light.register(PersonAutocomplete)
It is very important to note here, that clean() will raise a
ValidationError
if a model is selected in a
ModelChoiceField
or ModelMultipleChoiceField
Note
Use at your own discretion, as this can cause problems when a choice is no longer part of the queryset, just like with django’s Select widget.
Registering the same Autocomplete class for several autocompletes¶
This code registers an autocomplete with name ContactAutocomplete
:
autocomplete_light.register(ContactAutocomplete)
To register two autocompletes with the same class, pass in a name argument:
autocomplete_light.register(ContactAutocomplete, name='Person',
choices=Person.objects.filter(is_company=False))
autocomplete_light.register(ContactAutocomplete, name='Company',
choices=Person.objects.filter(is_company=True))