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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章