《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做表单验证参数,博主这里没有做也没有出现问题,不确定是不是有安全隐患。


尾巴:

有的时候在其他商业化产品中很简单的功能,或者一般开发来讲很容易的需求。作为菜鸟需要研究很长时间,进行反复尝试。不过,好在问题都能最终解决,博主也是在这样的过程中不断提升自己的能力和对自己的信心。

有的时候碰到网上如果有现成的思路和案例,耐心研究和尝试,还是能够实现我们希望达到的效果的。

 

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