• 【Python成长之路】从 零做网站开发 -- 基于Flask和JQuery,实现表格管理平台


    【写在前面】

    你要开发网站?    嗯。。

    会Flask吗?    什么东西,没听过。。。

    会JQuery吗?    是python的库吗 ?

    那你会什么?    我会F12打开网站

    好吧,那我们来写个简单的表格管理平台。

    基于Flask框架和JQuery实现管理平台网站的开发功能,我代码编写用了2天的时间 ,从零开始写;又对具体实现流程,自己断断续续地整理总结了近半个月。从自我感觉来说,整个过程和结果的实现都让我很满意。

    【效果如下】

    【第一步】了解Flask框架

    1、了解python 主流的web框架

    (1)Django:简单来说就是重武器,是最全能的开发框架,你想要的功能它都有;但是比较繁重,适合企业级的web开发;

    (2)Flask:属于web开发微框架,小巧灵活,相关的第三方库丰富,适合开发小型web;

    (3)Tornado:天生异步,性能强悍,但是框架提供的功能少,需要开发者自己实现;

    因此,本文代码实现主要是基于Flask实现的。

    2、了解Flask框架

    (1)HelloWorld实现:

    几乎所有的编程都是基于“hello world”实现的,因此也大致讲下helloworld涉及的内容。

    from flask import Flask
    app = Flask(__name__)
     
    @app.route('/')
    def hello_world():
        return "Hello world!"
     
    if __name__ == '__main__':
        app.run()

    先声明一个Flask框架的对象,然后定义路由'/',即URL地址为 http://127.0.0.1:5000/;如果定义路径为‘/new’,那对应的访问地址需要改为http://127.0.0.1:5000/new。另外,@app.route是个装饰器。

    (2)如何实现具体IP地址或者端口的访问呢?

    app.run(debug=True,host="x.x.x.x",port="1234")

    通过对app.run()方法的参数定义,分别实现了调试功能,访问URL变更为 http://x.x.x.x:1234

    这里调试功能还是很好用的,不但可以帮助开发者重新加载网页,而且会打印详细的错误信息,协助定位。

    (3)如何在web上显示自己特定的html模板

    from flask import Flask,render_template
    app = Flask(__name__)
     
    @app.route('/')
    def hello_world():
        return render_template('test.html')
     
    if __name__ == '__main__':
        app.run()

    只需要导入render_template库,并且在函数返回时改成对应的方法即可。

    不过 这 里要 注意,test.html必须保存在工程目录与下template文件下,否则会报错。(这是因为render_template方法定义时默认写了template路径 )

    【第二步】了解Sqlite3数据库

    web数据交互离不开后台数据库的管理,本章节重点解释python自带的sqlite3数据库。相比较于其他“正规”的数据库,如mongo、solr、MySQL等,sqlite3相当简单,属于轻量级的数据库。

    1、sqlite3数据库的安装和创建

    用pip命令可以下载安装sqlite3数据库

    创建数据库:

    con = sqlite3.connect('material.db')

    如果有数据库material.db,则进行连接数据库的操作;如果没有此数据库,则先创建数据库再进行连接;

    2、创建数据表

    label = ['ID','网络IP','地址','责任人','联系方式']
    content = ['1','10.10.10.10','杭州滨江','鹏哥','123456']
    
    def create():
        sql = 'create table {0} ({1},{2},{3},{4},{5})'.format(tablename,label[0],label[1],label[2],label[3],label[4])
        result = cur.execute(sql)
        con.commit()
        return  True if result else False

    简单描述为:create table 表名 (各字段名1,各字段名2……) 

    当前对数据表的字段未进行输入类型及长度的限制,比如需要规则ID为必填项,并且为整形,长度在10个字节内,则需要修改为

    sql = 'create table matrial_table ("ID" int[10] primary key not null )'

    同理,其他字段也可以相同的方式进行类型、长度的限制。

    注意:在执行cur.execute()后,要记得con.commit()进行数据库提交,否则数据并不会真正写入数据库中。

    3、插入数据

    def insert():
        sql = 'insert into {0} ({1},{2},{3},{4},{5}) values({6},"{7}","{8}","{9}","{10}")'.format(tablename,label[0],label[1],
              label[2],label[3],label[4],content[0],content[1],content[2],content[3],content[4])
        result = cur.execute(sql)
        con.commit()
        return  True if result else False

     简单描述为:insert into 表名 (各字段名1,各字段名2……) values(数值1,数值2……)

    这 里要注意,”{7}“ 是有加双引号的,为什么呢?因为”{7}“对应的是网络IP,是个字符串,因此需要加 双引号,否则会报错。

    4、查询数据

    def query():
        sql = 'select * from {0}'.format(tablename)
        result = cur.execute(sql)
        return list(result)

    简单描述为:select XX,XX from 表名 where 字段名1="数值1"

    5、更新数据:update 表名  set 字段名1=”数值1“ where 字段名2="数值2"

    6、删除某条数据:delete from 表名 where 字段名1="数值1"

    【补充】

    如果生成了db数据库,如何查看呢?可以下载一个SQLite Expert,打开后就可以很直观地进行数据库查看,并且可以通过图形化按钮进行 数据表的增删改查。

    【第三步】了解html

    1、Flask框架方法编写

    前面已经讲过 Flask如何调用html模板,因此我们直接展示上图对应的flask框架方法的代码

    # coding=utf-8
    # @Auther : "鹏哥贼优秀"
    # @Date : 2019/9/23
    # @Software : PyCharm
     
    from flask import Flask,render_template
    import sqlite3
    app = Flask(__name__)
    con = sqlite3.connect('material.db',check_same_thread=False)
     
    @app.route('/')
    def Material_Mangement():
        # 获取数据库material_table表的内容
        cur = con.cursor()
        sql = 'select * from {0}'.format("material_table")
        cur.execute(sql)
        content = cur.fetchall()
        # 获取数据库表的表头
        labels = [tuple[0] for tuple in cur.description]
        return render_template('test.html',content=content,labels=labels)
     
    if __name__ == '__main__':
        app.run(debug=True)

    动态路由、sqlite3数据库操作、调试模式的设置,这些知识请参考之前的博客。但有2个知识点,我想再提下:

    (1)如果在数据库连接时,不添加check_same_thread=False参数,则

    数据库连接会报错:sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread. The object was created in thread id  XX。

    这是因为sqlite3数据库被多线程访问导致冲突,因此这里要注意下。

    (2)获取数据库表头:labels = [tuple[0] for tuple in cur.description]

    2、Html文件(仅展示表格内容)

    <!doctype html>
    <html lang="en">
    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
     
        <!-- Bootstrap CSS -->
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
              integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
     
        <title>物料管理平台</title>
    </head>
    <body>
    <div class="row">
        <div class="col-md-6 col-sm-12 col-xs-12">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3>表格管理平台</h3>
                </div>
                <div class="panel-body">
                    <div class="table-responsive">
                        <table class="table table-striped table-bordered table-hover">
                            <thead>
                            <tr>
                                {% for i in labels %}
                                    <td>{{ i }}</td>
                                {% endfor %}
                            </tr>
                            </thead>
                            <tbody>
                            {% for i in content %}
                                <tr>
                                    {% for j in i %}
                                        <td>{{ j }}</td>
                                    {% endfor %}
                                </tr>
                            {% endfor %}
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
     
        </div>
     
    </div>
    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
            integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
            crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"
            integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"
            crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"
            integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"
            crossorigin="anonymous"></script>
    </body>
    </html>
    

    对应的效果是这样的:

    因为一开始接触flask时,我只调试过html,但是根本没具体接触过html怎么写。因此上述这段代码是参考于大神的代码(https://blog.csdn.net/a19990412/article/details/84955802)。

    熟悉这段代码后,我觉得有几块内容是和我要实现的代码有关的。

    (1)title 标题修改

    (2)表格的长宽大小:<div class="col-md-6 col-sm-12 col-xs-12"> 。  col-xs-*和col-sm-* 和col-md-*(col-xs表示超小屏幕,col-md-中等屏幕,col-sm-小屏幕)主要是用来适应不同屏幕的表格展示。因此需要自适应调整对应的数值。

    (3)设置表格的ID:<table class="table table-striped table-bordered table-hover",id="test">。这里其实不设置id也是可以的,但是后续我要对表格进行编辑时,加上id会方便我定位表格,方法是:tab = document.getElementById("test")

    3、Html文件(添加编辑、提交按钮)

    根据上述要修改的点,我重新修改了html内容,新的html代码如下:

    <!doctype html>
    <html lang="en">
    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
     
        <!-- Bootstrap CSS -->
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
              integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
     
        <title>表格管理平台</title>
    </head>
    <body>
    <div class="row">
        <div class="col-md-12 col-sm-12 col-xs-12">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3>表格管理平台</h3>
                </div>
                <div class="panel-body">
                    <div class="table-responsive">
                        <table class="table table-striped table-bordered table-hover">
                            <thead>
                            <tr>
                                {% for i in labels %}
                                    <td>{{ i }}</td>
                                {% endfor %}
                            </tr>
                            </thead>
                            <tbody>
                            {% for i in content %}
                                <tr>
                                    {% for j in i %}
                                        <td>{{ j }}</td>
                                    {% endfor %}
                                    <td><input type="button" value="编辑"></td>
                                    <td><input type="submit" value="提交"></td>
                                </tr>
                            {% endfor %}
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
     
        </div>
     
    </div>
    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
            integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
            crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"
            integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"
            crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"
            integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"
            crossorigin="anonymous"></script>
    <script>
        (function(){
          $('input[type="button"]').on('click', function(){
            var $this = $(this),
              edit_status = $this.attr('edit_status'),
              status_value = edit_status && 1 == edit_status ? 0 : 1,
              $td_arr = $this.parent().prevAll('td');
            $this.val(1 == status_value ? '完成' : '编辑').attr('edit_status', status_value);
            $.each($td_arr, function(){
              var $td = $(this);
              if(1 == status_value) {
                $td.html('<input type="text" value="'+$td.html()+'">');
              } else if(0 == status_value){
                $td.html($td.find('input[type=text]').val());
              }
            });
          });
        })();
    </script>
    </body>
    </html>

    相比于第 2步时的html文件,这次我主要添加了2块内容:

    (1)添加编辑、提交按钮:

    <td><input type="button" value="编辑"></td>   <td><input type="submit" value="提交"></td>

    将这两行代码放在表格每行最后,就会生成相应的按钮

    (2)编辑功能的实现:

    编辑按钮对应的function是基于JQuery写的,当然这 段代码我也是参考另一位大神的

    (https://blog.csdn.net/luo201227/article/details/50559988),因为当前我对JQuery完全一无所知。

    但是在熟悉代码后,我根据自己对代码的理解进行了注释。

    <script>
        (function(){
                <!--定义属于是 button的按钮在点击后,产生下面的function功能-->
          $('input[type="button"]').on('click', function(){
                <!--获取当前事件,并进行当前按钮的状态,如果是编辑状态,就进行将每个单元格设置成可输入状态-->
            var $this = $(this),
              edit_status = $this.attr('edit_status'),
              status_value = edit_status && 1 == edit_status ? 0 : 1,
              $td_arr = $this.parent().prevAll('td');
            $this.val(1 == status_value ? '完成' : '编辑').attr('edit_status', status_value);
                <!--如果单元格是可编辑状态,获取每列元素的值,并在最后html表格上进行展示-->
            $.each($td_arr, function(){
              var $td = $(this);
              if(1 == status_value) {
                $td.html('<input type="text" value="'+$td.html()+'">');
                <!--如果按钮状态是完成状态,直接展示单元内的值-->
              } else if(0 == status_value){
                $td.html($td.find('input[type=text]').val());
              }
            });
          });
        })();
    </script>

    细心的同学会发现,我在点击”提交“按钮时,什么都没 发生。是的,”提交“功能,我将在下一个章节中进行介绍。

    【第四步】了解JQuery

    1、提交功能的实现,对我来说,最难的是html对前台数据的传输。因此,我就参考着编辑功能,自己一 点点写。以下是示例代码:

    (1)HTML文件编写前台界面提交功能

    <script>
        (function(){
                <!--定义属性是submit的按钮在点击后,产生下面的function功能-->
          $('input[type="submit"]').on('click', function(){
                    <!--获取当前行的id -->
                    var td = event.srcElement.parentElement;
                    var rownum = td.parentElement.rowIndex;
                    <!--获取html表格元素 -->
                    var tab = document.getElementById("test");
                    <!--将每个单元格元素进行取值,并以字典形式传给后台 -->
                    var data = {
                        "ID":tab.rows[rownum].cells[0].innerHTML,
                        "网络IP":tab.rows[rownum].cells[1].innerHTML,
                        "地址":tab.rows[rownum].cells[2].innerHTML,
                        "责任人":tab.rows[rownum].cells[3].innerHTML,
                        "联系方式":tab.rows[rownum].cells[4].innerHTML,
                    };
                    alert("提交成功!")
                    $.ajax({
                        type: "get",
                        url: "/edit",
                        data: data,
                        dataType: "json"
                        });
            });
            })();
    

    (2)后台对提交后的数据进行读取,并写数据库

    @app.route('/edit', methods=['get'])
    def edit():
        label = ['ID', '网络IP', '地址', '责任人', '联系方式']
        content = [request.args.get(i) for i in label]
        print(content)
        sql = 'update {0} set {1}="{6}",{2}="{7}",{3}="{8}",{4}="{9}" where {5}={10}'.format('material_table',
              label[1], label[2], label[3],label[4],label[0],content[1],content[2],content[3],content[4],content[0])
        cur = con.cursor()
        cur.execute(sql)
        con.commit()
        return "数据写入成功!"

    在实现“提交”功能时,我主要遇到了以下几个 坑:

    (1)html代码写完后,发现程序报错,提示$.ajax is not a function。我去,我看其他大神的数据交互也是这么写的呀,为什么我这不行?

    查看网上的帖子,都是说未定义或者和其他库有冲突,最后我自己发现,是因为我没有声明是JQuery。需要在script前面加上一行代码:

    <script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script>

    我理解这行代码的意思是声明下面要JQuery库,而不是其他JS库。(可能理解有误)

    最后加上这行代码后,问题成功解决!

    (2)获取当前行号

    一开始在获取行号,怎么获取不到,而且不知道要怎么调试,在同事的指导下,才学会通过alert(td.innerHTML)来查看获取的当前行内容是什么。

    最初从网上 查到获取当前行的写法是:

    var td = event.parentElement;
    var rownum = td.parentElement.rowIndex;

    通过alert调试,发现点击提交根本没有反应;又改成

    var td = event.srcElement;
    var rownum = td.parentElement.rowIndex;

    结果是获取不到任何内容

    就这么个小问题就花费了我近2个小时的时间去不停地查资料,尝试编写。最后想说的是,如果对html不熟悉,还是应该找个大神带带自己的。

    最后在别人的协助下,实现了获取当前行的功能:

    (3)获取每个表格里的 内容

    tab.rows[0].cells[0].innerHTML

    在获取单元格内数据时,最验证的是我获取不到tab,即表格元素。因为我原先没有在表格元素里加id,即

    网上找了很多方法都没法实现,最后老老实实地加上 id="test"

    讲道理,html里的这 20行代码是我搞这个表格管理平台时,花费最大精力的。至此,表格管理平台的功能基本成行 !

    【第五步】添加新增功能

    以上功能只实现了对现有数据的编辑保存功能,但是如果用户想要新增数据,怎么办?我当时第一个想法是让用户自己去改数据库,哈哈。还要我去写新增功能,要累死了,不想写。

    下面是关于新增功能的介绍和示例代码。

    1、如何添加“新增”按钮

    <td><input type="button" value="新增" id="create"></td><br>

    如果前面 的html能看懂了,这行是不难理解的。

    2、点击新增按钮后,如何动态增加表格行、列

    <script>
         (function () {
            $('input[id="create"]').on('click', function(){
     
                var editTable=document.getElementById("tbody");
                var tr=document.createElement("tr");
                var td1=document.createElement("td");
                td1.innerHTML="";
     
                tr.appendChild(td1);
                editTable.appendChild(tr);

    首先定义tr元素,然后在tr元素中再追加td元素。如果表格里有多列数据,那只需要重复td1的写法,进行复制粘贴就可以了。

    另外,当前td.innerTHML是设置为空,如果要将该单元格直接设置为编辑状态,则修改成  :

    td1.innerHTML="input[type=text] /";

    3、如何动态添加“编辑”、“提交”按钮,下面以“编辑”为例

    var td9 = document.createElement("td")
    var myedit =document.createElement("input");
    myedit.type = "button";
    myedit.value = "编辑"
    myedit.id = "edit"
    td9.appendChild(myedit)

    添加方式和添加文本框方式是一样的,只是需要注意元素类型是Input,并且要补充下元素的type/value/id。

    4、如何对动态添加的按钮进行事件绑定,下面以提交功能为例

    $('input[id="submit"]').on('click', function(){
        alert("test")
    }

    关于动态添加的按钮进行事件绑定有很多帖子,有用Live方法的,有用$(document).on('click','.edit',function()方法的,其实不用这么麻烦,和正常的“提交”写法是完全一样的。

    5、获取当前新增行内的数据,进行提交。

     var tab = document.getElementById("test");
                var rownum = td1.parentElement.rowIndex;
                $('input[id="submit"]').on('click', function(){
                var data = {
                    "ID":tab.rows[rownum].cells[0].innerHTML,
                };
                alert("新增成功!")
                $.ajax({
                        type: "get",
                        url: "/create",
                        data: data,
                        dataType: "json"
                        });
                });

    这段代码和提交功能的实现是一样的,大同小异。

    作者: 鹏哥贼优秀

  • 相关阅读:
    Asp.net Core 系列之--5.认证、授权与自定义权限的实现
    Asp.net Core 系列之--4.事务、日志及错误处理
    Asp.net Core 系列之--3.领域、仓储、服务简单实现
    Asp.net Core 系列之--2.ORM初探:Dapper实现MySql数据库各类操作
    Asp.net Core 系列之--1.事件驱动初探:简单事件总线实现(SimpleEventBus)
    Cocos2d-x项目创建
    Cocos2d-x编译Android环境
    Linux 之 RPM
    Channels实现扫码登录
    SQLALchemy中关于复杂关系表模型的映射处理
  • 原文地址:https://www.cnblogs.com/huaweicloud/p/11861246.html
Copyright © 2020-2023  润新知