《Flask Web應用結合AJAX技術實現頁面中多級表單聯動》—人生苦短,我用python(九)

寫在前面:

近期,博主通過Flask Web框架,利用python語言實現多線程ping,數據庫採用SQLite實現了針對IP地址管理功能的初步實現。應用整體框架如下:

在應用中,希望能夠實現IP地址的使用登記,這時需要在前臺頁面中實現根據一級菜單選擇的內容,動態關聯出二級菜單可以選擇的內容。

其中一級菜單的內容是通過在數據庫輪詢得到的,二級菜單的內容對應的需要根據一級菜單選擇的內容作爲查詢條件,再去數據庫中搜索關聯內容。

依然由於博主不是專門做開發的,被這個問題困擾了很久,直到搜索到CSDN上另一篇文章才明白瞭解決這個問題的思路和方法,附上文章連接,並在此表示感謝。

https://blog.csdn.net/qq_38787214/article/details/86319271

博主的實現思路和很多代碼也與上述完全一致,此篇文章僅用於整理思路。


實現思路:

實現的思路其實很簡單

首先:通過Flask-WTF構造表單,一級菜單和二級菜單默認選項均爲數據庫中對應字段的所有值。

之後: 在用戶選擇了一級菜單以後,調用ajax,將用戶在表單中選擇的一級菜單的內容發送到flask視圖函數中

再後:在視圖函數對應的路由處理函數中,實現根據一級表單內容在數據庫中過濾查詢二級表單內容,並將查詢結果返回

最後:ajax讀取返回內容,將內容添加到下一級列表的選項中


實現代碼:

數據庫結構如下:

class IPResources(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    IP = db.Column(db.String(20), unique=True)
    Network = db.Column(db.String(200))
    Vlan_ID = db.Column(db.Integer)
    Area_Name = db.Column(db.String(200))
    SubArea_Name = db.Column(db.String(200))
    Option_Func = db.Column(db.String(200))
    Using_Status = db.Column(db.String(20),default='未使用')
    Info_Status = db.Column(db.String(20),default='未登記')
    Check_Time = db.Column(db.DateTime,default=datetime.now())

表單結構如下:

class IPUsingCreate(FlaskForm):
    area_name = SelectField(u'請選擇一級區域', validators=[DataRequired()], render_kw={ 'class': 'form-control'})
    subarea_name = SelectField(u'請選擇二級區域', validators=[DataRequired()], render_kw={'class': 'form-control'})
    sys_name = StringField(u'用途描述', validators=[DataRequired()], render_kw={'class': 'form-control'})
    submit = SubmitField(u'添加', render_kw={'class': 'btn btn-primary'})

首先,我們要實現一級二級菜單的默認選項爲該字段的所有值

要實現這一點的代碼如下:

分別創建能夠生成指定字段所有值並添加到列表中的函數,實現代碼如下:

查詢函數

def select_area_query():
    a0 = [(IPResource.Area_Name, IPResource.Area_Name) for IPResource in IPResources.query.all()]
    a = list(set(a0))
    a.sort(key=a0.index)
    print(a)
    return a

將查詢結果賦值給表單選項的方法:

form.area_name.choices = select_area_query()

二級菜單默認選項的獲取方法與一級表單相同,在此不再解釋了

之後需要通過flask的render template方法將form傳入模板中,並將模板進行渲染

前端代碼如下:

    <form class="form-inline" method="post">
         {{ form.csrf_token }} <!-- 渲染CSRF令牌隱藏字段 -->
         {{ form.area_name.label }}
         {{ form.area_name }}
         {{ form.subarea_name.label }}
         {{ form.subarea_name }}
         {{ form.sys_name.label }}
         {{ form.sys_name }}
         {{ form.submit }}
    </form>

這樣,我們在頁面中就可以得到如下表單,表單中的默認選項是對應數據庫字段的所有值

之後,我們希望實現在用戶選擇了一級菜單以後,調用ajax,將用戶在表單中選擇的一級菜單的內容發送到flask視圖函數中

這裏實現代碼如下:

我們首先將模板中的form進行修改,採用Bootstrap_flask支持的render_field方法快速渲染表單

    <form class="form-inline" method="post">
         {{ form.csrf_token }} <!-- 渲染CSRF令牌隱藏字段 -->
         {{ render_field(form.area_name,onchange="Select('area_name','#subarea_name','/dashboard/ipusing_create/select_area')") }}
         {{ form.subarea_name.label }}
         {{ form.subarea_name }}
         {{ form.sys_name.label }}
         {{ form.sys_name }}
         {{ form.submit }}
    </form>

通過HTML DOM事件中的表單事件onchange事件,當檢測到表單內容發生變化的時候觸發對應的javascript函數。

我們在這裏定義一個Javascript函數,Select,我們需要在select這個函數中傳入三個參數,分別是一級菜單的ID,二級菜單的ID和將數據傳導flask後臺對應的路由。

下面我們看具體的JavaScript代碼:

<script>
function Select(choose,id,register_url){
    var data;
    var select = document.getElementById(choose);
    $(id).html("");
    for (i=0;i<select.length;i++){
        if (select[i].selected){
            Area_Name = select[i].text;
            data = {
                "area_name":Area_Name
            };
            $.ajax({
                url:register_url,
                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');
                    }
                }
            });
        }
    }
}
</script>

下面我們對上面的代碼做簡單解釋,當一級菜單的某一項被選中的時候,將對應的內容傳入data中,之後將data轉換爲JSON數據,將該數據通過POST的方法發送到指定的URL,對應到flask應用中,之後將flask返回的數據添加到二級菜單的選項中,如果沒有返回數據則拋出錯誤。

最後,我們來看後端處理代碼

@app.route('/dashboard/ipusing_create/select_area',methods = ['GET', 'POST'])
def selectArea():
    if request.method == 'POST':
        data = request.get_json()
        area_name = data['area_name']
        subarea_name = select_subarea(area_name)
        return jsonify(subarea_name)

當後端收到到這個url的POST請求的時候,獲取數據,將數據內容作爲參數去數據庫中查詢對應的二級表單內容,查詢語句如下:

def select_subarea(area_name):
    a0 = [IPResource.SubArea_Name for IPResource in IPResources.query.filter_by(Area_Name = area_name).all()]
    a = list(set(a0))
    a.sort(key=a0.index)
    print(a)
    return a

這樣flask返回的是一個列表形式的數據,我們通過JavaScript中的函數,通過循環方法將他依次添加到二級表單的選項中就可以了。這個功能在前面的JS函數中實現了。

至此,便完成了一個二級表單的聯動功能

同理,也能夠完成多級表單的聯動,只需要在第二個表單內容選中後,將一級和二級數據均傳送到後臺,後臺再根據內容在數據庫中進行過濾查詢,再將查詢結果返回即可。

實現效果如下:


遺留問題:

目前,博主雖然通過這樣的方式實現了多級表單的聯動,但這之中仍然存在着部分問題。

包括必須當一級表單內容發生變化時纔會觸發JS函數,當一級表單第一個選項就是我想要選擇的內容是,不會去後臺觸發更新,需要先選擇其他內容再反過來選擇第一個選項,觸發表單內容變化。

還有一個問題就是之前博主提到的那篇文章中說需要重新設置CSRF做表單驗證參數,博主這裏沒有做也沒有出現問題,不確定是不是有安全隱患。


尾巴:

有的時候在其他商業化產品中很簡單的功能,或者一般開發來講很容易的需求。作爲菜鳥需要研究很長時間,進行反覆嘗試。不過,好在問題都能最終解決,博主也是在這樣的過程中不斷提升自己的能力和對自己的信心。

有的時候碰到網上如果有現成的思路和案例,耐心研究和嘗試,還是能夠實現我們希望達到的效果的。

 

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