• 饮冰三年-人工智能-Python-42 Python之Flask中的WTForm+DBUtils实现学生管理系统


    上篇中 我们通过Flask蓝图简单做了一个增删改查的学生管理系统,在数据和页面校验上都存在很大问题。接下来我们完善一下。

    代码存储:GitHub

    一、基于DBUtils实现数据库连接池

    1:原理浅析 

      连接池:是创建和管理连接的缓存池。简单的说:随时准备着。有一些国外学者喜欢称之曰“备胎”。
      优点
        一:减少连接创建时间
        二:受控的资源使用
        连接池能够使性能最大化,同时还能将资源利用控制在一定的水平之下,如果超过该水平,应用程序将崩溃而不仅仅是变慢。
      原理与操作 
    (1)建立数据库连接池对象(服务器启动)。
    (2)按照事先指定的参数创建初始数量的数据库连接(即:空闲连接数)。
    (3)对于一个数据库访问请求,直接从连接池中得到一个连接。如果数据库连接池对象中没有空闲的连接,且连接数没有达到最大(即:最大活跃连接数),创建一个新的数据库连接。
    (4)存取数据库。
    (5)关闭数据库,释放所有数据库连接(此时的关闭数据库连接,并非真正关闭,而是将其放入空闲队列中。如实际空闲连接数大于初始空闲连接数则释放连接)。
    (6)释放数据库连接池对象(服务器停止、维护期间,释放数据库连接池对象,并释放所有连接)

    2:连接练习

      1:首先安装DBUtils 和 PyMySQL

    from flask import Flask
    import pymysql
    import time
    from DBUtils.PooledDB import PooledDB, SharedDBConnection
    
    # 创建共享连接池
    POOL = PooledDB(
        creator=pymysql,  # 使用链接数据库的模块
        maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数
        mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
    
        maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
        maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制,连接使用次数过多会有缓存,有时需要使用最新的
        setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
    
        maxshared=3,
        # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
    
        blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
        ping=0,
        # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
        host='127.0.0.1',
        port=3306,
        user='root',
        password='',
        database='yk2012',
        charset='utf8',
    )
    
    
    # 记忆方法
    # 模块 最大 初始化
    # 闲置 最新 开始化
    # 阻塞 检查 基本化
    
    def get_conn():
        """
        连接数据库
        :return: conn, cursor
        """
        conn = POOL.connection()  # 数据库连接
        cursor = conn.cursor(pymysql.cursors.DictCursor)  # 数据库指针
        return conn, cursor
    
    
    def reset_conn(conn, cursor):
        """
         :param conn: 数据库连接
         :param cursor: 数据库指针
         :return: Null
         """
        cursor.close()
        conn.close()
    
    
    def fetch_all(sql, args):
        """
        :param sql: sql语句
        :param args: sql语句的参数
        :return: 查询结果
        """
        conn, cursor = get_conn()
        cursor.execute(sql, args)
        result = cursor.fetchall()
        reset_conn(conn, cursor)
        return result
    
    app=Flask(__name__)
    @app.route('/')
    def index():
        res=fetch_all("select * from student",())
        print(res)
        return "执行成功"
    
    if __name__ == '__main__':
        app.run(debug=True)
    DBUtils

    总结:配置项记忆方法

    # 模块 最大 初始化
    # 闲置 最新 开始化
    # 阻塞 检查 基本化

    二、数据准备   

    1:创建相应的数据库和数据表

      首先,我们创建一个数据库(StudentManage_WTF)。

      再创建一个用户表(User){ID:自增,UserName:用户名, Password:密码}

      再创建一个学生表(Student){ID:自增, StudentName: 姓名,Age:年龄, Gender:性别}  

    #创建数据库
    create database StudentManage_WTF;
    #创建用户表
    create table User(
        ID int primary key auto_increment,
        UserName nvarchar(20) not null,
        Password nvarchar(200) not null
    );
    #初始化管理员
    insert into User (UserName,Password) Values('Aaron','1');
    #创建学生信息表
    create table Student(
        ID int primary key auto_increment,
        StudentName nvarchar(20) not null,
        Age int,
        Gender int
    );
    数据库创建脚本

    三、代码结构和MYSQLHelper工具类  

    1:代码结构

    2:帮助类MYSQLHelper

    from flask import Flask
    import pymysql
    import time
    from DBUtils.PooledDB import PooledDB, SharedDBConnection
    
    
    class MYSQLHelper(object):
        def __init__(self, host, port, dbuser, password, database):
            self.pool = PooledDB(
                creator=pymysql,  # 使用链接数据库的模块
                maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数
                mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
                maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
                maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制,连接使用次数过多会有缓存,有时需要使用最新的
                setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
                maxshared=3,
                # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
                blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
                ping=0,
                # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
                host=host,
                port=int(port),
                user= dbuser,
                password=password,
                database= database,
                charset='utf8',
            )
    
    
        def get_conn(self):
            """
            连接数据库
            :return: conn, cursor
            """
            conn = self.pool.connection()  # 数据库连接
            cursor = conn.cursor(pymysql.cursors.DictCursor)  # 数据库指针
            return conn, cursor
    
    
        def reset_conn(self,conn, cursor):
            """
             :param conn: 数据库连接
             :param cursor: 数据库指针
             :return: Null
             """
            cursor.close()
            conn.close()
    
    
        def fetch_all(self,sql, args):
            """
            :param sql: sql语句
            :param args: sql语句的参数
            :return: 查询结果
            """
            conn, cursor = self.get_conn()
            cursor.execute(sql, args)
            result = cursor.fetchall()
            self.reset_conn(conn, cursor)
            return result
    
        def insert_one(self,sql,args):
            conn,cursor=self.get_conn()
            res=cursor.execute(sql,args)
            conn.commit()
            self.reset_conn()
            return res
    
        def update(self,sql,args):
            conn,cursor=self.get_conn()
            result = cursor.execute(sql,args)
            conn.commit()
            self.reset_conn()
            return result
    MYSQLHelper.py

    四、WTForm实现登录校验 

    1:登录页面

      a:使用数据库中User表判断是否有权限。

      b:使用WTForm框架做校验。

    #1:安装WTForms!ps:别安装错了,不是WTForm
    from wtforms.fields import simple,core
    from wtforms import Form,validators,widgets
    
    class LoginForm(Form):
        username =simple.StringField(
            label="用户名",
            validators=[
                validators.data_required(message="用户名不能为空")
            ],
            widget=widgets.TextInput(),
            render_kw={"class":"my_username"}
        )
        password =simple.StringField(
            label="密码",
            validators=[
                validators.data_required(message="密码不能为空!")
            ],
            widget=widgets.PasswordInput()
        )
        submit=simple.SubmitField(
            label="提交"
    
        )
    ModelForm
    {% extends "index.html" %}
    {% block css %}
        <style>
            label[for='submit'] {
                display: none
            }
        </style>
    {% endblock %}
    {% block content %}
        <h2>学生登录</h2>
        <form action="/s_login" method="post" novalidate>
            {% for filed in loginForm %}
                <p>{{ filed.label }} {{ filed }}{{ filed.errors.0 }}</p>
            {% endfor %}
        </form>
    {% endblock %}
    s_login.html
    from flask import Blueprint
    from flask import render_template, request, session, redirect
    from StudentManage_WTF.Tools.Helper.DBHelper.MYSQLHelper import MYSQLHelper
    from StudentManage_WTF.UserLogin.ModelForm.LoginModel import LoginForm
    
    # 每个蓝图都可以为自己独立出一套template模板文件夹,如果不写则共享项目目录中的templates
    sl = Blueprint("sl", __name__, template_folder="templates")
    
    
    @sl.route("/s_login", methods=("GET", "POSt"))
    def userLogin():
        if request.method == "GET":
            loginForm = LoginForm()
            return render_template("s_login.html", loginForm=loginForm)
        else:
            lf = LoginForm(request.form)
            if lf.validate():
                username = request.form.get("username")
                password = request.form.get("password")
                # 获取sql帮助类对象
                sqlhelper = MYSQLHelper("127.0.0.1", 3306, "root", "", "StudentManage_WTF")
                # 拼接sql语句
                sql = "select id,username from user where username='%s' and password='%s'" % (username, password)
                res = sqlhelper.fetch_all(sql, ())
                print(res)
                if res:
                    session["user"] = res
                    return redirect("/s_view")
                else:
                    session["user"] = None
                    lf.username.errors.append("用户名或密码错误!")
                    return render_template("s_login.html", loginForm=lf)
            else:
                return render_template("s_login.html", loginForm=lf)
    login.py

    五、学生信息的维护

    1:学生列表页面

    from flask import Blueprint
    from flask import render_template
    from StudentManage_WTF.Tools.Helper.DBHelper.MYSQLHelper import MYSQLHelper
    # 每个蓝图都可以为自己独立出一套template模板文件夹,如果不写则共享项目目录中的templates
    sv = Blueprint("sv", __name__, template_folder="templates")
    
    
    @sv.route("/s_view")
    def studentView():
        # 获取sql帮助类对象
        sqlhelper = MYSQLHelper("127.0.0.1", 3306, "root", "", "StudentManage_WTF")
        # 拼接sql语句
        sql = "select * from Student where 1=1"
        studentList = sqlhelper.fetch_all(sql, ())
        return render_template("s_view.html", studentList=studentList)
    s_view.py
    {% extends "index.html" %}
    {% block content %}
        <h2>学生列表</h2>
        <table border="3xp">
            <thead>
            <tr>
                <td>编号</td>
                <td>学生姓名</td>
                <td>年龄</td>
                <td>性别</td>
                <td>操作</td>
            </tr>
            </thead>
            <tbody>
            {% for student in studentList %}
                <tr>
                    <td>{{ student.ID }}</td>
                    <td>{{ student["StudentName"] }}</td>
                    <td>{{ student.get("Age") }}</td>
                    <td>{{ student.Gender }}</td>
                    <td><a href="/s_update/{{ student.ID }}">修改</a> | <a href="/s_del?id={{ student.ID }}">删除</a></td>
                </tr>
            {% endfor %}
            </tbody>
        </table>
        <a href="/s_add">新增</a>
    {% endblock %}
    s_view.html

    2:学生管理页面

    from flask import Blueprint
    from flask import render_template, request, redirect
    from StudentManage_WTF.student_oper.ModelForm.StudentModel import StudentForm
    from StudentManage_WTF.configer.MYSQLSetting import getMYSQLHelper
    
    # 每个蓝图都可以为自己独立出一套template模板文件夹,如果不写则共享项目目录中的templates
    sa = Blueprint("sa", __name__, template_folder="templates")
    sqlhelper = getMYSQLHelper()
    
    
    @sa.route("/s_add", methods=('GET', 'POST'))
    def studentAdd():
        if request.method == "GET":
            stu = StudentForm()
            return render_template("s_add_modify.html", curTitle="新增", curAction="/s_add", stu=stu)
        else:
            stu = StudentForm(request.form)
            if stu.validate():
                # 获取sql帮助类对象
                # 拼接sql语句
                sql = "insert into student (studentname,age,gender) values('%s',%s,%s);" % (
                    stu.StudentName.data, stu.Age.data, stu.Gender.data)
                res = sqlhelper.insert_one(sql, ())
                return redirect("/s_view")
            else:
                return render_template("s_add_modify.html", curTitle="新增", curAction="/s_add", stu=stu)
    
    
    @sa.route("/s_update/<int:id>", methods=('GET', 'POST'))
    def studentUpdate(id):
        if request.method == "GET":
            sql = "select * from student where ID='%s'" % (id)
            curStu = sqlhelper.fetch_all(sql, ())
            if curStu:
                curStu = StudentForm(**curStu[0])  # 神来之笔
            return render_template("s_add_modify.html", curTitle="编辑", curAction="/s_update/" + str(id), stu=curStu)
        else:
            stu = StudentForm(request.form)
            if stu.validate():
                sql = "update student set studentname='%s',age='%s',gender='%s' where ID='%s'" 
                      % (stu.StudentName.data, stu.Age.data, stu.Gender.data, id)
                res = sqlhelper.update(sql, ())
                return redirect("/s_view")
            else:
                return render_template("s_add_modify.html", curTitle="编辑", curAction="/s_update/" + str(id), stu=stu)
    
    
    @sa.route("/s_del", methods=('GET', 'POST'))
    def studentDel():
        id = request.args.get('id')
        sql = "delete from student where ID='%s'" % (id)
        curStu = sqlhelper.update(sql, ())
        return redirect("/s_view")
    s_add_modify_del.py方法
    from wtforms.fields import simple, core
    from wtforms import widgets, validators, Form
    
    
    class StudentForm(Form):
        ID = simple.StringField(
            widget=widgets.HiddenInput
        ),
        StudentName = simple.StringField(
            label="姓名",
            validators=[
                validators.data_required(message="学生姓名不能为空!"),
                validators.Length(min=2, max=20, message="学生姓名长度必须大于2位,小于20位")
            ]
        )
        Age = core.StringField(
            label="年龄",
            validators=[
                validators.data_required(message="学生年龄不能为空!"),
                validators.Regexp(regex="^d+$", message="学生年龄必须为数字")
            ]
        )
        Gender = core.RadioField(
            label="性别",
            coerce=int,  # 保存到数据中的值为int类型
            choices=(
                (0, ''), (1, '')
            ),
            default=1
        )
        submit = simple.SubmitField(
            label="提交"
        )
    StudentModel
    {% extends "index.html" %}
    {% block css %}
        <style>
            label[for='submit'] {
                display: none
            }
        </style>
    {% endblock %}
    {% block content %}
        <h2>学生{{ curTitle }}</h2>
        <form action="{{ curAction }}" method="post" novalidate>
            {% for filed in stu %}
                <p>{{ filed.label }} {{ filed }}{{ filed.errors.0 }}</p>
            {% endfor %}
        </form>
    {% endblock %}
    s_add_modify.html

  • 相关阅读:
    [转载] IE8+兼容小结
    vue添加,删除内容
    vue跳转的两种方法
    checked 完整版全选,单选,反选
    网页特殊符号HTML代码大全
    利用css 实现 视觉差效果
    css 自定义滚动条样式
    js 模拟键盘
    css 动画 transition和animation
    浅谈浏览器垃圾回收机制
  • 原文地址:https://www.cnblogs.com/YK2012/p/12168700.html
Copyright © 2020-2023  润新知