Flask 利用AJAX异步实现二级(甚至多级)下拉表单级联 --记录那些坑

首先我是用的WTForms生成的表单,我也用input标签写过,感觉不整洁,就弃用了,这里我以学院school、系department、团队team,这三级关系做介绍。(和省,市,区一样)
下面的Form类的定义(数据库定义就不给出了,这里使用SQLalchemy查询语言):

class RegisterForm(FlaskForm):
#注意这样定义school就是表单id,下面也一样
    school = SelectField('学院', coerce=int, default='xxx')
    department = SelectField('系', coerce=int, default='xxx')
    team = SelectField('团队', coerce=int, default='xxx')
    #初始化下拉表单值,直接给出了学院的所有值
        def __init__(self, *args, **kwargs):
	        super(RegisterForm, self).__init__(*args, **kwargs)
	        self.school.choices = [(school.id,school.name)
	                                 for school in School.query.order_by(School.name).all()]
	        self.department.choices = []
	        self.team.choices = []

那么前端表单生成如下(别忘了CSRF验证)

{{ form.csrf_token }}
{{ render_field(form.school,onchange="Select('school','#department',school_url,csrf)") }}
{{render_field(form.department,onchange="Select('department','#team',department_url,csrf)")}}
 {{ render_field(form.team) }}

当然,onchange就是JavaScript检测下拉表单值发生变化的函数,这个检测肯定是实时的,只要表单值改变,那么就会触发JS函数Select(),那么我传的这些参数是什么意思呢,可以先看下面的js程序。

function Select(choose,id,register_url,csrf) {
        var data;
        var csrftoken = csrf;
        var select = document.getElementById(choose);
        $(id).html("");       //每次重新选择当前列表框,就清空下一级列表框。
        for (i=0;i<select.length;i++){
            if (select[i].selected){     //判断被选中项
                Name = select[i].text;
                data ={
                    "name":Name
                };
                $.ajax({                       //发起ajax请求
                    url:register_url,
                    //加上csrf验证头,也可直接加在data里最前面
                    headers: {"X-CSRFToken": csrftoken },
                    type:"POST",
                    data:JSON.stringify(data),
                    contentType:"application/json; charset=UTF-8",
                    success:function (data) {    //后端返回数据,是列表形式的
                        if (data){
                            for (i=0;i<data.length;i++){
                                $("<option value='"+data[i]+"'>" + data[i] + "</option>").appendTo(id)//将后端返回的数据逐项插入到下一级列表框中
                            }
                        }
                        else {
                            alert('error');
                        }
                    }
                });
            }
        }
    }

那么应该可以猜到:

第1个参数choose是指当前选择变化的表单id
第2个参数id是指下一级表单的id,如choose是某个学院id,那么id应该传入系表单的id.
第3个参数register_url是指AJAX需要的参数url->后端视图函数路经
第4个参数csrf是ajax的表单验证参数(最坑,明明我已经有验证表单了)
那么这些参数怎么获取的
在根路经base.html

<script type="text/javascript" >
    var school_url = "{{ url_for('auth.SelectSchool') }}";
    var department_url = "{{ url_for('auth.SelectDepartment') }}";
    var csrf = "{{ csrf_token() }}";
</script>
<script type="text/javascript" src="{{ url_for('static', filename='js/script.js') }}" charset="UTF-8"></script>

那么接下来就是后端对AJAX的处理了,这个是选择学院后下级表单自动跳出对应系

@auth_bp.route('/selectschool/register',methods=['GET','POST'])
def SelectSchool():
    if request.method == 'POST':
        data = request.get_json()
        name = data['name']
        #这里我给了[''],而不是[],是避免第一个就是我要选择的,从而界面感受不到下拉表单变化,不是太懂的话可以用[]试试
        DepartmentName = ['']
        school = School.query.filter_by(name = name).first()
        departments = Department.query.filter_by(school_id = school.id).all()
        for department in departments:
            DepartmentName.append(department.name)
        return jsonify(DepartmentName)

选择系后自动跳出团队和上面一样就不多说了。
当然这些操作都可以用一个视图函数处理,只需js多传入一个判断是哪个表单发生了改变的值,在data字典里添加一个键值对即可。
按这种思路多少级关系都没问题(前提是你数据库设计好这些关系)

做个记录,共勉,虽然内容不算完美
还有就是post提交方式,一般后端用request.form.get()获取数据
get方式,一般用request.args.get()获取数据
我这是AJAX发送JSON数据,所以获取数据不一样,
获取数据一定要考虑会不会为None。

猜你喜欢

转载自blog.csdn.net/qq_38787214/article/details/86319271