# widgets.py
from django.forms.widgets import TextInput, flatatt
from django.forms.util import force_unicode
from django.utils.html import escape
from django.utils import simplejson
from django.utils.safestring import mark_safe
class AutocompleteInput(TextInput):
"""
A form text input that gets rendered as an autocomplete widget using
jQuery UI's autocomplete.
http://api.jqueryui.com/autocomplete/
The only required option is choices:
class FooForm(forms.Form):
selections = forms.CharField(max_length=10, widget=AutocompleteInput(choices=MY_SELECTIONS))
"""
def __init__(self, choices, options={}, attrs={}, *args, **kwargs):
super(TextInput, self).__init__(attrs=attrs, *args, **kwargs)
self.choices = choices
self.options = simplejson.dumps(options) if len(options) > 0 else None
self.attrs = attrs
def render(self, name, value=None, attrs={}):
final_attrs = self.build_attrs(attrs, name=name)
# Handles different types of choices
if isinstance(self.choices, list):
source = simplejson.dumps(self.choices)
elif isinstance(self.choices, str):
source = "'{0}'".format(escape(self.choices))
elif isinstance(self.choices, tuple): # assumes tuple will be 2-item choice tuples (as in the docs)
try:
## This sets the displayed values
# If you have (for example) "fruit" tuples like (('apples', 'apples'),), then you can
# just use item[0] for the "label"
# The below set up is useful for displaying the human-readable format, and inserting
# the value that will go into the database. For instance, (('FL', 'Florida'),) will
# display "Florida" but insert "FL" into the field.
source = simplejson.dumps([{"label": "{0}".format(item[1]), "value": "{0}".format(item[0])} for item in self.choices])
except IndexError:
raise ValueError("choices tuple is not valid")
else:
raise ValueError("choices type is not valid")
options = ''
if self.options:
options += ",{0}".format(self.options) # for passing add'l autocomplete options
if value:
value = force_unicode(value)
final_attrs['value'] = escape(value)
if not self.attrs.has_key('id'):
final_attrs['id'] = 'id_{0}'.format(name)
return mark_safe(u'''
'''.format(name, flatatt(final_attrs), final_attrs['id'], source, options))
## EXAMPLE
#
# forms.py
#
from django.contrib.localflavor.us.us_states import US_STATES, US_TERRITORIES
from .widgets import AutocompleteInput
class ResidentForm(forms.ModelForm):
ALL_STATES = tuple(sorted(US_STATES + US_TERRITORIES, key=lambda obj: obj[1]))
...
state = forms.CharField(max_length=2, widget=AutocompleteInput(choices=ALL_STATES) # just use {{ form.state }} in template
...