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
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
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
#
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.
#
Try defining the field in the init method
#
gonna try this
#
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
#
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:
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?
How to configure the app engine?
Thanks for any help
#
Please login first before commenting.