I want to build a Country/State selector. First you choose a country, and the States for that country are displayed in the 2nd select box. Doing that in PHP and jQuery is fairly easy, but I find Django forms to be a bit restrictive in that sense.
I could set the State field to be empty on page load, and then populate it with some jQuery, but then if there are form errors it won't be able to "remember" what State you had selected. I'm also pretty sure that it will throw a validation error because your choice wasn't one of the ones listed in the form on the Python side of things.
So how do I get around these problems?
Here is my solution. It uses the undocumented Form method _raw_value() to peek into the data of the request. This works for forms, which have a prefix, too.
class CascadeForm(forms.Form):
parent=forms.ModelChoiceField(Parent.objects.all())
child=forms.ModelChoiceField(Child.objects.none())
def __init__(self, *args, **kwargs):
forms.Form.__init__(self, *args, **kwargs)
parents=Parent.objects.all()
if len(parents)==1:
self.fields['parent'].initial=parents[0].pk
parent_id=self.fields['parent'].initial or self.initial.get('parent') \
or self._raw_value('parent')
if parent_id:
# parent is known. Now I can display the matching children.
children=Child.objects.filter(parent__id=parent_id)
self.fields['children'].queryset=children
if len(children)==1:
self.fields['children'].initial=children[0].pk
jquery Code:
function json_to_select(url, select_selector) {
/*
Fill a select input field with data from a getJSON call
Inspired by: http://stackoverflow.com/questions/1388302/create-option-on-the-fly-with-jquery
*/
$.getJSON(url, function(data) {
var opt=$(select_selector);
var old_val=opt.val();
opt.html('');
$.each(data, function () {
opt.append($('<option/>').val(this.id).text(this.value));
});
opt.val(old_val);
opt.change();
})
}
$(function(){
$('#id_parent').change(function(){
json_to_select('PATH_TO/parent-to-children/?parent=' + $(this).val(), '#id_child');
})
});
Callback Code, which returns JSON:
def parent_to_children(request):
parent=request.GET.get('parent')
ret=[]
if parent:
for children in Child.objects.filter(parent__id=parent):
ret.append(dict(id=child.id, value=unicode(child)))
if len(ret)!=1:
ret.insert(0, dict(id='', value='---'))
return django.http.HttpResponse(simplejson.dumps(ret),
content_type='application/json')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With