Django/jQuery级联选择框?

mpe*_*pen 12 django jquery django-forms

我想建立一个国家/州选择器.首先,您选择一个国家/地区,该国家/地区的州显示在第二个选择框中.在PHP和jQuery中这样做相当容易,但我发现Django表单在这个意义上有点限制.

我可以在页面加载时将State字段设置为空,然后用一些jQuery填充它,但是如果有表单错误,它将无法"记住"您选择的状态.我也很确定它会抛出一个验证错误,因为你的选择不是Python方面表格中列出的那个之一.

那么我该如何解决这些问题呢?

gue*_*tli 14

这是我的解决方案.它使用未记录的Form方法_raw_value()来查看请求的数据.这适用于具有前缀的表单.

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
Run Code Online (Sandbox Code Playgroud)

jquery代码:

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');
     })  
    });
Run Code Online (Sandbox Code Playgroud)

回调代码,返回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')
Run Code Online (Sandbox Code Playgroud)


Mik*_*one 8

您可以将隐藏字段设置为具有真实的"状态"值,然后使用jQuery创建<select>列表,然后.select()将其值复制到隐藏字段.然后,在页面加载时,您的jQuery代码可以获取隐藏字段的值,并使用它在<select>元素填充后选择元素中的正确项目.

这里的关键概念是State弹出菜单是一个完全用jQuery创建的小说,而不是Django表单的一部分.这使您可以完全控制它,同时让所有其他字段正常工作.

编辑:还有另一种方法,但它不使用Django的表单类.

在视图中:

context = {'state': None, 'countries': Country.objects.all().order_by('name')}
if 'country' in request.POST:
    context['country'] = request.POST['country']
    context['states'] = State.objects.filter(
        country=context['country']).order_by('name')
    if 'state' in request.POST:
        context['state'] = request.POST['state']
else:
    context['states'] = []
    context['country'] = None
# ...Set the rest of the Context here...
return render_to_response("addressform.html", context)
Run Code Online (Sandbox Code Playgroud)

然后在模板中:

<select name="country" id="select_country">
    {% for c in countries %}
    <option value="{{ c.val }}"{% ifequal c.val country %} selected="selected"{% endifequal %}>{{ c.name }}</option>
    {% endfor %}
</select>

<select name="state" id="select_state">
    {% for s in states %}
    <option value="{{ s.val }}"{% ifequal s.val state %} selected="selected"{% endifequal %}>{{ s.name }}</option>
    {% endfor %}
</select>
Run Code Online (Sandbox Code Playgroud)

当国家/地区发生变化时,您还需要通常的JavaScript来重新加载状态选择器.

我没有对此进行过测试,因此可能会有一些漏洞,但它应该可以解决这个问题.

所以你的选择是:

  • 使用Django表单中的隐藏字段获取真实值,并通过AJAX或者在客户端创建选择菜单
  • 沟渠Django的表单和自己初始化菜单.
  • 创建一个自定义的Django表单小部件,我没有这样做,因此不会发表评论.我不知道这是否可行,但看起来你需要一些Selects MultiWidget,后者在常规文档中没有记录,所以你必须阅读源代码.