Login

Django JQuery Autocomplete for Model Selection

Author:
elpenia
Posted:
September 30, 2008
Language:
Python
Version:
1.0
Score:
2 (after 2 ratings)

This is a general JQuery Autocomplete Form Field for selecting any model instance in your forms.

1 Download jquery.1.2.6 and the jquery autocomplete plugin http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/, place theme somewhere in your media directory (in this case {{ MEDIA__URL}}/js/

2 copy fields.py to anywhere in your python-path (in this case utils.fields)

3 create a view for the ajax request that receives a query and returns a key|value list, and register it in your urls.py

4 Just Use the fields in any form like in forms.py

  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
# fields.py


# -*- coding: utf-8 -*-
from django import forms
from django.utils.safestring import mark_safe
from django.utils.encoding import force_unicode 
from django.core.urlresolvers import reverse
from django.forms.util import ErrorList, ValidationError
CLIENT_CODE = """
<input type="text" name="%s_text" id="%s_text"/>
<input type="hidden" name="%s" id="%s" value="" />
<script type="text/javascript">
     $(function(){
	function formatItem(row) {
		return row[1] ;
	}
	function formatResult(row) {
                return row[1];
	}
	$("#%s_text").autocomplete('%s', {
                mustMatch: true,
		formatItem: formatItem,
		formatResult: formatResult
	});
	$("#%s_text").result(function(event, data, formatted) {
              $("#%s").val(data[0]);                         

	});

     });
</script>
"""

class ModelAutoCompleteWidget(forms.widgets.TextInput):
    """ widget autocomplete for text fields
    """
    html_id = ''
    def __init__(self, 
                 lookup_url=None, 
                 *args, **kw):
        super(forms.widgets.TextInput, self).__init__(*args, **kw)
        # url for Datasource
        self.lookup_url = lookup_url
       

    def render(self, name, value, attrs=None):
        if value == None:
            value = ''
        html_id = attrs.get('id', name)
        self.html_id = html_id

        lookup_url = self.lookup_url
        detail_url = reverse('ajax_platos_detail')
        return mark_safe(CLIENT_CODE % (name, html_id, name, html_id, html_id,
                                       lookup_url, html_id, html_id, detail_url))


    def value_from_datadict(self, data, files, name):
        """
        Given a dictionary of data and this widget's name, returns the value
        of this widget. Returns None if it's not provided.
        """

        return data.get(name, None)



        
class ModelAutoCompleteField(forms.fields.CharField):
    """
    Autocomplete form field for Model Model
    """
    model = None
    url = None


    def __init__(self, model,  lookup_url, *args, **kwargs):
        self.model, self.url = model, lookup_url
        super(ModelAutoCompleteField, self).__init__(
            widget = ModelAutoCompleteWidget(lookup_url=self.url),
            max_length=255,
            *args, **kwargs)

    def clean(self, value):

        try: 
            obj = self.model.objects.get(pk=value)
        except self.model.DoesNotExist:
            raise ValidationError(u'Invalid item selected')            
        return obj     





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

urlpatterns = patterns('mymodel.views',
    # ajax
   url(r'^ajax/list/$', 'ajax_mymodel_list',
        name='ajax_mymodel_list'),                           

)


# views.py
from django.http import HttpResponse
from django.template import RequestContext
from mymodel.models import MyModel

def ajax_mymodel_list(request):
    """ returns data displayed at autocomplete list - 
    this function is accessed by AJAX calls
    """
    limit = 10
    query = request.GET.get('q', None)
    # it is up to you how query looks
    if query:
        qargs = [django.db.models.Q(name__istartswith=query)]
        
    instances = MyModel.objects.filter(django.db.models.Q(*qargs))[:limit]

    results = ""
    for item in instances:
        results += "%s|%s \n" %(item.pk,item.name)

    return HttpResponse(results)


# forms.py
from django import forms
from django.core.urlresolvers import reverse
from mymodel.models import MyModel
from utils.fields import ModelAutoCompleteField

class TestForm(forms.Form):
    theField = ModelAutoCompleteField(lookup_url = reverse('ajax_mymodel_list'),
                                   model = MyModel, required=True)

More like this

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

Comments

mirobe (on October 3, 2008):

Can I see record name instead of ID (pk) after choosing the right one from the list?

Also, In the line: "super(ModelAutoCompleteField, self).init(args, *kwargs)"

I am getting: "global name 'args' is not defined" on save()

Thanks

#

elpenia (on October 4, 2008):

This field was designed to retrieve the object selected by the user but you can always access to the pk and the text field values using POST['field'] and POST['field_text'] or GET['field'] and GET['field_text'].

Sorry for the error the line init in clean is wrong, I'm correcting this now, please let me now if it worked and send me any feedback.

Thanks for using it.

#

elpenia (on March 18, 2009):

Try defining the field in the init method

#

romain-hardouin (on October 28, 2009):

gonna try this

#

pedromagnus (on November 10, 2010):

I have a similar trouble to make this work as erosb. The view works fine, but the response isn't correctly displayed in the browser (maybe a .css missed that the autocomplete script needs to display it properly). I have a question about ModelAutoCompleteWidget.render(): What the ´detail_url´ supose to be? (specifically reverse('ajax_platos_detail') where has to point to??). Anyway, below that line, in the RETURN, the string CLIENT_CODE has space for 8 variables, and 9 are given. Is there any missing javascript function in CLIENT_CODE that requires that extra value (detail_url)? Regards. Pedro

#

jonativ (on November 12, 2011):

My application is using two forms:

The first one requesting from the user to provide a (partial) string of a geographic location, for example: "ictori"

The second form is providing a selection of all the places with that string, including Victoria in Australia, Canada etc.

The application is on GAE - Python

The code looks like this:

form action="/onarrive" method="post"

input type="text" name="description" maxlength="20" value="{{ user_input }}"

input type="submit" value=" Find "

/form

form action="/selectlocation" name="selectlocation" method="post"

select name="selected" size="3"

{% for location_name in locations_list_ %} option value = "{{ forloop.counter0 }}" {{location_name}} /option

{% endfor %} /select

input type="submit" value=" OK "

/form

Two questions:

  1. How to replace the first form with autocomplete so the user doesnot have to press the " Find " botton in order to refresh the whole page?

  2. How to configure the app engine?

Thanks for any help

#

Please login first before commenting.