上篇中 我们通过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)
总结:配置项记忆方法
# 模块 最大 初始化# 闲置 最新 开始化# 阻塞 检查 基本化
二、数据准备
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
四、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="提交"
)

{% 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 %}

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)

五、学生信息的维护
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)

{% 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 %}

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")

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="提交"
)

{% 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 %}


来源:https://www.cnblogs.com/YK2012/p/12168700.html
