# widgets.py
from django import newforms as forms
from django.newforms.widgets import flatatt
from django.newforms.util import smart_unicode
from django.utils.html import escape
from django.utils.simplejson import JSONEncoder
class JQueryAutoComplete(forms.TextInput):
def __init__(self, source, options={}, attrs={}):
"""source can be a list containing the autocomplete values or a
string containing the url used for the XHR request.
For available options see the autocomplete sample page::
http://jquery.bassistance.de/autocomplete/"""
self.options = None
self.attrs = {'autocomplete': 'off'}
self.source = source
if len(options) > 0:
self.options = JSONEncoder().encode(options)
self.attrs.update(attrs)
def render_js(self, field_id):
if isinstance(self.source, list):
source = JSONEncoder().encode(self.source)
elif isinstance(self.source, str):
source = "'%s'" % escape(self.source)
else:
raise ValueError('source type is not valid')
options = ''
if self.options:
options += ',%s' % self.options
return u'$(\'#%s\').autocomplete(%s%s);' % (field_id, source, options)
def render(self, name, value=None, attrs=None):
final_attrs = self.build_attrs(attrs, name=name)
if value:
final_attrs['value'] = escape(smart_unicode(value))
if not self.attrs.has_key('id'):
final_attrs['id'] = 'id_%s' % name
return u'''
''' % {
'attrs' : flatatt(final_attrs),
'js' : self.render_js(final_attrs['id']),
}
# views.py - sample view
from django.http import HttpResponse, HttpResponseBadRequest
from django.views.decorators.cache import cache_page
from apps.foo.models import Foo
def autocomplete(request):
def iter_results(results):
if results:
for r in results:
yield '%s|%s\n' % (r.name, r.id)
if not request.GET.get('q'):
return HttpResponse(mimetype='text/plain')
q = request.GET.get('q')
limit = request.GET.get('limit', 15)
try:
limit = int(limit)
except ValueError:
return HttpResponseBadRequest()
foos = Foo.objects.filter(name__startswith=q)[:limit]
return HttpResponse(iter_results(foos), mimetype='text/plain')
autocomplete = cache_page(autocomplete, 60 * 60)