寫在前面:
近期,博主通過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做表單驗證參數,博主這裏沒有做也沒有出現問題,不確定是不是有安全隱患。
尾巴:
有的時候在其他商業化產品中很簡單的功能,或者一般開發來講很容易的需求。作爲菜鳥需要研究很長時間,進行反覆嘗試。不過,好在問題都能最終解決,博主也是在這樣的過程中不斷提升自己的能力和對自己的信心。
有的時候碰到網上如果有現成的思路和案例,耐心研究和嘗試,還是能夠實現我們希望達到的效果的。