Login

YUI Autocomplete

Author:
pigletto
Posted:
August 27, 2007
Language:
Python
Version:
.96
Score:
1 (after 1 ratings)

This snippet allows you to use YUI's autocomplete widget in a easy way.

  1. Download YUI (http://developer.yahoo.com/yui/) library and put it into MEDIA folder (in my case I put YUI/build/ directory as base/yui/ in my MEDIA folder)

  2. Create lookup view for your autocomplete field. See 'test_ajax_ac' function to see how this lookup view may be built. You have to define JsonResponse somewhere in your files. JsonResponse is taken from: http://www.djangosnippets.org/snippets/154/

  3. Define url for newly created view in urls.py (in usual way)

  4. Include necessary .js and .css files in your page (see example in test_ajax.html)

  5. Assign widget to a field - see form's init at UserForm in the example. Additional (optional) parameters are: format_result_fname (name of javascript function for formatting results - see YUI docs for examples)), item_select_handler_fname (name of javascript function for handling item select event (see YUI docs)).

When using YUI take care about proper skin - you'll possibly need to define wrapper like: <div class="yui-skin-sam">....</div> around your html code.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#----------------------- widgets.py------------------------
# -*- coding: utf-8 -*-
# 
# In order to use this widget you need:
# 1. download YUI library and put it in 
#    your MEDIA folder (http://developer.yahoo.com/yui/)
# 2. Include necessary js and css imports at your page
#    Check for necessary imports at 'YUI autocomplete' page
#    My imports are visible at test_ajax.html
# 3. Assign a widget to field (with schema and lookup_url parameters)
# 4. Define view to do a data lookup for ajax queries
#
from django import newforms as forms
from django.utils.safestring import mark_safe
from django.utils.encoding import force_unicode 

#AUTOCOMPLETE
AC_SNIPPET = """
<div class="ac_container">
    <input %s />
    <div id="%s_container" class="yui-skin-sam"></div>

    <script type="text/javascript">
        // An XHR DataSource
        var acServer_%s = "%s";
        var acSchema_%s = %s;
        var acDataSource_%s = new YAHOO.widget.DS_XHR(acServer_%s, acSchema_%s);
        var acAutoComp_%s = new YAHOO.widget.AutoComplete("%s","%s_container", acDataSource_%s);
        acAutoComp_%s.useIFrame = true;
        acAutoComp_%s.animSpeed = 0;
        %s
        %s
    </script>
</div>
"""

def_format_result = 'acAutoComp_%s.formatResult = %s;'
def_item_select_handler = 'acAutoComp_%s.itemSelectEvent.subscribe(%s);'

class AutoCompleteWidget(forms.widgets.TextInput):
    """ widget autocomplete for text fields
    """
    
    def __init__(self, 
                 schema=None, 
                 lookup_url=None, 
                 format_result_fname='', 
                 item_select_handler_fname='', 
                 *args, **kw):
        super(AutoCompleteWidget, self).__init__(*args, **kw)
        # YUI schema
        self.schema = schema
        # url for YUI XHR Datasource
        self.lookup_url = lookup_url
        # optional name of javascript function that formats results (YUI)
        self.format_result_fname = format_result_fname 
        # optional name of javascript function that handles item select event (YUI)
        self.item_select_handler_fname = item_select_handler_fname

    def render(self, name, value, attrs=None):
        html_id = attrs.get('id', name)
        # url for YUI XHR Datasource
        lookup_url = self.lookup_url
        # YUI schema
        schema = self.schema
        # optional name of javascript function that handles item select event (YUI)
        item_select_handler_fname = getattr(self, 'item_select_handler_fname', '')
        # optional name of javascript function that formats results (YUI)
        format_result_fname = getattr(self, 'format_result_fname', '')        

        if value is None: value = ''
        final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
        if value != '': final_attrs['value'] = force_unicode(value) # Only add the 'value' attribute if a value is non-empty.
        final_attrs['class'] = 'autocomplete_widget'

        fr = '' # format result
        sh = '' # select handler
        if self.format_result_fname:
            fr = def_format_result % (html_id, self.format_result_fname)
        if self.item_select_handler_fname:
            sh = def_item_select_handler % (html_id,
                                            self.item_select_handler_fname)

        return mark_safe(AC_SNIPPET % (forms.util.flatatt(final_attrs), html_id, html_id,
                             lookup_url,html_id, schema, html_id, html_id,
                             html_id, html_id, html_id, html_id,
                             html_id,html_id, html_id, fr, sh))
#/AUTOCOMPLETE
#/----------------------- widgets.py------------------------




#
#EXAMPLE APPLICATION: ---------------------------------------
#
# code below is a listing of files from example application 
# that uses AutocompleteWidget, you don't have to create these
# files! Only thing you need to put into your application 
# is code from above: widgets.py


#----------------------- test_ajax.html -------------------
<html>
<head>
  <!-- yui -->
  <link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}base/js/yui/assets/skins/sam/autocomplete.css" />
  <script type="text/javascript" src="{{ MEDIA_URL }}base/js/yui/utilities/utilities.js"></script>
  <script type="text/javascript" src="{{ MEDIA_URL }}base/js/yui/autocomplete/autocomplete-min.js"></script>
  <script type="text/javascript" src="{{ MEDIA_URL }}base/js/yui/datasource/datasource-beta-min.js"></script>
<!-- /yui-->  
</head>
<body>
  <div class="yui-skin-sam">
    <form>
    {{ user_form.as_p }}
    </form>
  </div>
</body>
</html>
#/----------------------- test_ajax.html -------------------


#----------------------- views.py------------------------
# -*- coding: utf-8 -*-
from <SOMEWHERE: http://www.djangosnippets.org/snippets/154/> import JsonResponse

from django.contrib.auth.models import User
from django.template import RequestContext
import django.db.models
from django.shortcuts import render_to_response
from django import newforms as forms
from widgets import AutoCompleteWidget

def test_ajax_ac(request):
    """ returns data displayed at autocomplete list - this function is accessed by AJAX calls
    """
    limit = 10
    query = request.GET.get('query', None)
    qargs = []
    # it is up to you how query looks
    if query:
        qargs = [django.db.models.Q(first_name__startswith=query) | \
                 django.db.models.Q(last_name__startswith=query) | \
                 django.db.models.Q(email__startswith=query)]
    users = User.objects.filter(django.db.models.Q(*qargs)).order_by('first_name')[:limit]
    results = []
    for user in users:
        results.append({'id':user.pk,
                        'name':user.get_full_name(),
                        'email':user.email})
    ret_dict = {'resultset':{'totalResultsReturned':len(results),
                             'results':results}}
    return JsonResponse(ret_dict)


class UserForm(forms.Form):
    username = forms.CharField(max_length=100)
    
    def __init__(self, *args, **kwargs):
        super(UserForm, self).__init__(*args, **kwargs)
        
        n_lookup_url = reverse('test_ajax_ac')  
        n_schema = '["resultset.results", "name", "email"]'
        self.fields['username'].widget = \
                      AutoCompleteWidget(lookup_url = n_lookup_url,
                                         schema = n_schema)
        

def test_ajax(request):
    """ Displays user form
    """
    user_form = UserForm()
    return render_to_response('test_ajax.html',
                              {'user_form':user_form},
                              context_instance=RequestContext(request))
#/----------------------- views.py------------------------

#------------------------ urls.py-------------------------
from django.conf.urls.defaults import *
from django.conf import settings

urlpatterns = patterns('views',
    url(r'^test_ajax/$', 'test_ajax', name='test_ajax'),
    url(r'^test_ajax_ac/$', 'test_ajax_ac', name='test_ajax_ac'),
)
#/------------------------ urls.py-------------------------

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 2 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 2 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 9 months, 2 weeks ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 10 months, 1 week ago
  5. Help text hyperlinks by sa2812 11 months ago

Comments

arcanum (on November 20, 2008):

Thanx for the snippet. The .js imports needed updating but other than that it works fine.

One small note. It would really be better to used named parameters on the AC_SNIPPET instead of repeating html_id a billion times :p

#

Please login first before commenting.