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)
|
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.
#
Sorry for my english. I'm trying to use this snippet. But always get this error:
Tried people_add in module mysite.forum.views. Error was: 'module' object has no attribute 'people_add'
urls.py: from django.conf.urls.defaults import * urlpatterns = patterns('', url(r'^people_list/$', mysite.forum.views.people_list', name='people_list'), url(r'^people_add/$', 'mysite.forum.views.people_add', name='people_add'), )
forum/models.py: from django.db import models
class People(models.Model): name = models.CharField(max_length=100)
forum/forms.py: from django import forms from django.core.urlresolvers import reverse from forum.models import People from forum.fields import ModelAutoCompleteField
class TestForm(forms.Form): theField = ModelAutoCompleteField(lookup_url = reverse('mysite.forum.views.people_list'), model = People, required=True)
forum/views.py: from django.http import HttpResponse from django.template import RequestContext from django.shortcuts import render_to_response from django.db.models import Q from forum.models import People from forum.forms import TestForm
def people_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 qargs = '' if query: qargs = [Q(name__istartswith=query)]
def people_add(request): form = TestForm() return render_to_response('people_add.html', {'form': form})
fields.py was saved as mysite/forum/fields.py
P.S.:
If forum/forms.py looks like this: class TestForm(forms.Form): theField = forms.CharField(max_length=50)
All work perfectly.
#
Try defining the field in the init method
#
gonna try this
#
For some reason I can't get this to work.
When I enter the code verbatim (with some small changes for the models I'm using), I get an ImproperlyConfigured error - no patterns found, which I presume is due to the reverses.
When I change to a lazy reverse:
I eliminate the error but now nothing happens at all. When I go to the url that should supposedly be called:
http://127.0.0.1:8000/ajax/list/?q=a
I get the correct response output, But when I type it into my ModelAutoCompleteField I get nothing. I've verified with {% url artist_list %} that the reverse_lazy is working, but for some reason nothing happens on my actual template.
Here is some pertinent code:
------urls.py from django.utils.functional import lazy from django.http import HttpResponse
reverse_lazy = lazy(reverse, unicode) url(r'^ajax/list/$', 'showbook.booker.views.artist_list', name='artist_list'),
-------forms.py from django.core.urlresolvers import reverse from showbook.booker.fields import ModelAutoCompleteField from django.utils.functional import lazy
reverse_lazy = lazy(reverse, unicode) artist = ModelAutoCompleteField(lookup_url = reverse_lazy('artist_list'), model = Artist, required=True)
Please, any help would be very much appreciated. I've been working on autocomplete for three days and haven't been able to get any solution to work and this one seems the closest so far.
Thank you!
#
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
#
Never mind. I switch to ajax-select (http://code.google.com/p/django-ajax-selects/). It has the selects for ForeignKey I was looking for.
Both projects could merge in one (both have pros & cons), a good (and complete) result could born there.
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
#