YUI Autocomplete

  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. autocompleter with database query by bbolli 6 years, 9 months ago
  2. FilteredSelect by beiske 5 years, 9 months ago
  3. Datetime widget by pigletto 6 years, 8 months ago
  4. Mobilize your Django site by stevena0 5 years ago
  5. Jquery ajax csrf framework for Django by chriszweber 2 years, 3 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

#

(Forgotten your password?)