目录
一、项目介绍
1、项目结构
db:数据库相关信息:mysql_db.py——定义数据库连接等事宜;user_dao.py——定义数据表增删改查相关操作
service:业务逻辑,将数据库资源和用户操作界面分开
app.py:编写业务逻辑等代码
2、系统部分功能提前展示
3、项目流程图
4、项目设计的数据库以及数据表信息
数据库:vega
数据表:
1)用户表t_user
2)角色表t_role
二、环境配置
mysql数据库安装MySQL :: MySQL Community Downloads
mysql驱动程序
连接mysql, 需要mysql connector,
pip install mysql-connector
pip install pymysql
windown10
pycharm
python3.7
三、项目实战数据库模块db编写
1、数据库连接池
1.为什么要使用数据库连接池 、好处是什么
对于一个简单的数据库应用,由于对于数据库的访问不是很频繁。这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不会带来什么明显的性能上的开销。但是对于一个复杂的数据库应用,情况就完全不同了。频繁的建立、关闭连接,会极大的减低系统的性能,因为对于连接的使用成了系统性能的瓶颈。
连接复用。通过建立一个数据库连接池以及一套连接使用管理策略,使得一个数据库连接可以得到高效、安全的复用,避免了数据库连接频繁建立、关闭的开销。
对于共享资源,有一个很著名的设计模式:资源池。该模式正是为了解决资源频繁分配、释放所造成的问题的。把该模式应用到数据库连接管理领域,就是建立一个数据库连接池,提供一套高效的连接分配、使用策略,最终目标是实现连接的高效、安全的复用。
数据库连接池的基本原理是在内部对象池中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法。如:
外部使用者可通过getConnection 方法获取连接,使用完毕后再通过releaseConnection方法将连接返回,注意此时连接并没有关闭,而是由连接池管理器回收,并为下一次使用做好准备。2.数据库连接池技术带来的优势:
1. 资源重用
由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。
2. 更快的系统响应速度
数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
3. 新的资源分配手段
对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接池技术,几年钱也许还是个新鲜话题,对于目前的业务系统而言,如果设计中还没有考虑到连接池的应用,那么…….快在设计文档中加上这部分的内容吧。某一应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源。
4. 统一的连接管理,避免数据库连接泄漏
在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。一个最小化的数据库连接池实现:
补充:这里的数据库连接指的是:
conn = pymysql.connect(host="localhost",user="root",passwd="123456",database="vega")
上面这个语句就是创建了一个数据库连接,在对数据库访问不频繁的时候,通常就会在使用数据库的时候,创建一个数据库连接,但是频繁访问数据库的时候,临时创建数据库连接的话,会带来极大的资源浪费,因此数据库连接池会事先维护一定的数据库连接,等需要的时候直接拿一个连接对数据库连接进行操作即可,一般一个系统只有一个全局连接池。
Authentication plugin 'caching_sha2_password' is not supported问题
添加参数:
"auth_plugin":'mysql_native_password'
mysql_db.py
# @Time : 2021/12/16 22:09
# @Author : @linlianqin
# @Site :
# @File : mysql_db.py
# @Software: PyCharm
# @description:
'''
创建连接池,这样可以避免频繁访问数据库的时候临时创建连接,使得资源浪费
'''
import mysql.connector.pooling
# 定义连接需要的参数,用字典封存,私有参数
__config = {
"host":"localhost",
"port":3306,
"user":"root",
"password":"",
"database":"vega",
"auth_plugin":'mysql_native_password'
}
import pymysql
# 创建连接池,定义最大连接数
try:
pool = mysql.connector.pooling.MySQLConnectionPool(
**__config,
pool_size=10
)
except Exception as e:
print("创建连接池出现异常:",e)
2、数据库数据访问接口(DAO)
DAO:data access object
这部分代码主要是为了将对数据的操作和业务分开
这部分写在代码user_dao.py中,主要含登录功能和角色查找功能
涉及到的知识点:密码加密解密(这里没有使用)、数据库查找操作、表连接等
基本步骤:
1)从连接池中取出一个连接;
2)创建cursor游标对象
3)SQL语句执行
4)逻辑代码执行
5)异常处理
6)返回数据库连接(这里虽然是close,但是不是将连接关闭,而是将其返回给了数据连接池)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/21 11:19
# @Author :
# @Site :
# @File : user_dao.py
# @Software: PyCharm
# @description:
'''
定义数据库操作,即一个数据访问接口,将数据库操作和业务逻辑封存起来
'''
from mysql_db import pool
class userDAO:
# 登录数据库
def login(self,username,password):
try:
# 从连接池中取出一个连接
conn = pool.get_connection()
cor = conn.cursor()
# 根据用户名和密码从用户表t_user中选择信息记录,若记录数为1则登录成功,否则不成功
sql = "select count(*) from t_user where username=%s and password=%s;"
cor.execute(sql,(username,password))
# 因为只有一条记录,取出第一个字段
count = cor.fetchone()[0]
if count == 1:
return True
else:
return False
except Exception as e:
print("login failed:",e)
finally:
# 当连接属性在当前目录中,关闭连接
if "conn" in dir():
conn.close()
# 根据用户名获取角色身份,因为客户和管理员身份看到的界面是不一样的
# 这里主要是通过用户表的角色id在身份表t_role中进行查找对应的身份
def search_user_role(self,username):
try:
conn = pool.get_connection()
cor = conn.cursor()
sql = "select r.role from t_user u join t_role r on u.role_id=r.role_id where u.username=%s"
cor.execute(sql,(username))
role = cor.fetchone()[0]
return role
except Exception as e:
print(e)
finally:
if "conn" in dir():
conn.close()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/22 13:47
# @Author : @linlianqin
# @Site :
# @File : role_dao.py
# @Software: PyCharm
# @description:
from db.mysql_db import pool
class roleDAO:
# 查询所有身份
def search_role(self):
try:
conn = pool.get_connection()
cor = conn.cursor()
sql = "select * from t_role"
cor.execute(sql)
role = cor.fetchall()
return role
except Exception as e:
print(e)
finally:
if "conn" in dir():
conn.close()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/21 14:45
# @Author : @linlianqin
# @Site :
# @File : news_dao.py
# @Software: PyCharm
# @description:
'''
定义新闻数据库操作,即一个新闻数据访问接口,将新闻数据库操作和业务逻辑封存起来
'''
from db.mysql_db import pool
class newDAO:
# 查找待审批的新闻,并且显示指定页数的记录
def search_unreview_list(self,page):
try:
# 从连接池中取出一个连接
conn = pool.get_connection()
cor = conn.cursor()
# 这里是按照编辑降序的方式排序,并且限制每页显示10个记录
sql = "select n.id,n.title,n.state,t.type,u.username from t_news n join t_type t on n.type_id=t.id join t_user u on n.editor_id=u.user_id " \
"where n.state=%s order by n.create_time desc limit %s,%s"
cor.execute(sql,("待审批",(page-1)*5,5))
res = cor.fetchall()
return res
except Exception as e:
print(e)
finally:
# 当连接属性在当前目录中,关闭连接
if "conn" in dir():
conn.close()
# 计算待审批新闻按照每页10条记录进行显示的话总共多少页
def count_page_unreview_list(self):
try:
# 从连接池中取出一个连接
conn = pool.get_connection()
cor = conn.cursor()
sql = "select ceil(count(*)/5) from t_news n where n.state=%s"
cor.execute(sql,["待审批"])
res = cor.fetchone()[0]
return res
except Exception as e:
print(e)
finally:
# 当连接属性在当前目录中,关闭连接
if "conn" in dir():
conn.close()
# 更新新闻状态,审批新闻,即将新闻的状态更新为已审批
def update_unreview_news_state(self,new_id):
try:
# 从连接池中取出一个连接
conn = pool.get_connection()
cor = conn.cursor()
sql = "update t_news set state=%s where id = %s"
cor.execute(sql,("已审批",new_id))
conn.commit()
except Exception as e:
print(e)
if "conn" in dir():
# 事务回滚
conn.rollback()
finally:
# 当连接属性在当前目录中,关闭连接
if "conn" in dir():
conn.close()
# 查询所有的新闻
def search_news_list(self,page):
try:
# 从连接池中取出一个连接
conn = pool.get_connection()
cor = conn.cursor()
# 这里是按照编辑降序的方式排序,并且限制每页显示10个记录
sql = "select n.id,n.title,n.state,t.type,u.username from t_news n join t_type t on n.type_id=t.id join t_user u on n.editor_id=u.user_id " \
"order by n.create_time desc limit %s,%s"
cor.execute(sql,((page-1)*5,5))
res = cor.fetchall()
return res
except Exception as e:
print(e)
finally:
# 当连接属性在当前目录中,关闭连接
if "conn" in dir():
conn.close()
# 计算新闻按照每页10条记录进行显示的话总共多少页
def count_page_list(self):
try:
# 从连接池中取出一个连接
conn = pool.get_connection()
cor = conn.cursor()
sql = "select ceil(count(*)/5) from t_news"
cor.execute(sql)
res = cor.fetchone()[0]
return res
except Exception as e:
print(e)
finally:
# 当连接属性在当前目录中,关闭连接
if "conn" in dir():
conn.close()
# 根据新闻id删除新闻
def delete_new_by_id(self,new_id):
try:
# 从连接池中取出一个连接
conn = pool.get_connection()
cor = conn.cursor()
sql = "delete from t_news where id = %s"
cor.execute(sql,[new_id])
conn.commit()
except Exception as e:
print(e)
if "conn" in dir():
# 事务回滚
conn.rollback()
finally:
# 当连接属性在当前目录中,关闭连接
if "conn" in dir():
conn.close()
if __name__ == '__main__':
l = newDAO()
print(l.search_unreview_list(1))
四、项目实战业务处理模块service编写
1、用户数据操作业务代码编写
user_service.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/21 11:57
# @Author : @linlianqin
# @Site :
# @File : user_service.py
# @Software: PyCharm
# @description:
'''
用于处理业务逻辑,比如调用多个数据库的dao,获取多个数据库的数据和保存数据到多个数据库当中
'''
from db.user_dao import userDAO
class userService:
__user_dao = userDAO() # 私有属性
# 登录数据库
def login(self,username,password):
flag = self.__user_dao.login(username,password)
return flag
# 根据用户名获取角色身份,因为客户和管理员身份看到的界面是不一样的
# 这里主要是通过用户表的角色id在身份表t_role中进行查找对应的身份
def search_user_role(self,username):
role = self.search_user_role(username)
return role
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/22 13:48
# @Author : @linlianqin
# @Site :
# @File : role_service.py
# @Software: PyCharm
# @description:
from db.role_dao import roleDAO
class roleService:
__role_dao = roleDAO()
# 查询所有身份
def search_role(self):
res = self.__role_dao.search_role()
return res
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/21 16:59
# @Author : @linlianqin
# @Site :
# @File : news_service.py
# @Software: PyCharm
# @description:
from db.news_dao import newDAO
class newsService:
__news_dao = newDAO()
# 查找待审批的新闻,并且显示指定页数
def search_unreview_list(self,page):
res = self.__news_dao.search_unreview_list(page)
return res
# 计算待审批新闻按照每页10条记录进行显示的话总共多少页
def count_page_unreview_list(self):
count_page = self.__news_dao.count_page_unreview_list()
return count_page
# 更新新闻状态,审批新闻,即将新闻的状态更新为已审批
def update_unreview_news_state(self,new_id):
self.__news_dao.update_unreview_news_state(new_id)
# 查询所有的新闻
def search_news_list(self,page):
res = self.__news_dao.search_news_list(page)
return res
# 计算新闻按照每页10条记录进行显示的话总共多少页
def count_page_list(self):
count_page = self.__news_dao.count_page_list()
return count_page
# 根据新闻id删除新闻
def delete_new_by_id(self,new_id):
self.__news_dao.delete_new_by_id(new_id)
五、APP程序控制台输入输出模块
因为一个系统不是运行一次就结束了,而是可以反复的循环运行,即比如登录界面,点击登录,退出后又可以登录,因此需要些一个可以进行循环运行的程序
app.py
1、模块介绍
getpass:用于掩盖密码
os:用于清空控制台——os.system("cls")
colorama:控制控制台打印内容的前背景色
2、代码
上图可以看出系统有三级菜单:
登录菜单
管理事项选择菜单:新闻管理、用户管理
具体管理操作菜单:审批新闻、删除新闻等
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/21 13:34
# @Author : @linlianqin
# @Site :
# @File : app.py
# @Software: PyCharm
# @description:
'''
用于处理控制台的输入输出,以及跟用户交互逻辑处理
'''
from colorama import Fore,Style
from getpass import getpass
import os
import sys
import time
from service.user_service import userService
from service.news_service import newsService
from service.role_service import roleService
__userService = userService()
__newsService = newsService()
__roleService = roleService()
# 登录系统轮询
while True:
# 每一轮的轮询后清空控制台
os.system("cls")
print(Fore.LIGHTBLUE_EX,"\n\t==================")
print(Fore.LIGHTBLUE_EX,"\n\t欢迎使用新闻管理系统")
print(Fore.LIGHTBLUE_EX, "\n\t==================")
print(Fore.LIGHTGREEN_EX, "\n\t1.登录系统")
print(Fore.LIGHTGREEN_EX, "\n\t2.退出系统")
print(Style.RESET_ALL)
opt = input("\n\t输入操作编号:")
# 登录系统
if opt == "1":
username = input("\n\t用户名:")
password = getpass("\n\t密码:") # 掩藏密码
# 登录
result = __userService.login(username,password)
# 登录成功
if result:
# 获取角色
role = __userService.search_user_role(username)
# 根据不同角色进入不同的界面
# 界面轮询
while True:
if role=="新闻编辑":
print("test")
elif role == "管理员":
os.system("cls")
print(Fore.LIGHTGREEN_EX, "\n\t1.新闻管理")
print(Fore.LIGHTGREEN_EX, "\n\t2.用户管理")
print(Fore.LIGHTRED_EX, "\n\tback.退出登录")
print(Fore.LIGHTRED_EX, "\n\texit.退出系统")
print(Style.RESET_ALL)
opt = input("\n\t输入操作编号:")
# 新闻管理
if opt == "1":
while True:
os.system("cls")
print(Fore.LIGHTGREEN_EX, "\n\t1.审批新闻")
print(Fore.LIGHTGREEN_EX, "\n\t2.删除新闻")
print(Fore.LIGHTRED_EX, "\n\tback.返回上一层")
print(Style.RESET_ALL)
opt = input("\n\t输入操作编号:")
# 审批新闻
if opt == "1":
page = 1 # 当前页码数
while True:
os.system("cls")
total_page = __newsService.count_page_unreview_list() # 总页数
cur_page_result = __newsService.search_unreview_list(page) # 当前页记录
# 逐条打印当前页记录
for index in range(len(cur_page_result)):
new = cur_page_result[index]
print(Fore.LIGHTBLUE_EX, "\n\t%d\t%s\t%s\t%s\t%s"%(index+1,new[1],new[2],new[3],new[4]))
print(Fore.LIGHTBLUE_EX, "\n\t-------------------------")
print(Fore.LIGHTBLUE_EX, "\n\t%d/%d"%(page,total_page))
print(Fore.LIGHTBLUE_EX, "\n\t-------------------------")
print(Fore.LIGHTRED_EX, "\n\tback.返回上一层")
print(Fore.LIGHTRED_EX, "\n\tprev.上一页")
print(Fore.LIGHTRED_EX, "\n\tnext.下一页")
print(Style.RESET_ALL)
opt = input("\n\t输入操作指令(输入数字表示审批对应记录的新闻):")
if opt == "back":
break
elif opt == "prev" and page > 1:
page -= 1
elif opt == "next" and page < total_page:
page += 1
elif int(opt) >= 1 and int(opt) <= 5:
# 获取新闻在数据表中的主键ID
new_id = cur_page_result[int(opt)-1][0]
__newsService.update_unreview_news_state(new_id)
# 删除新闻
elif opt == "2":
page = 1 # 当前页码数
while True:
os.system("cls")
total_page = __newsService.count_page_list() # 总页数
cur_page_result = __newsService.search_news_list(page) # 当前页记录
# 逐条打印当前页记录
for index in range(len(cur_page_result)):
new = cur_page_result[index]
print(Fore.LIGHTBLUE_EX,
"\n\t%d\t%s\t%s\t%s\t%s" % (index + 1, new[1], new[2], new[3], new[4]))
print(Fore.LIGHTBLUE_EX, "\n\t-------------------------")
print(Fore.LIGHTBLUE_EX, "\n\t%d/%d" % (page, total_page))
print(Fore.LIGHTBLUE_EX, "\n\t-------------------------")
print(Fore.LIGHTRED_EX, "\n\tback.返回上一层")
print(Fore.LIGHTRED_EX, "\n\tprev.上一页")
print(Fore.LIGHTRED_EX, "\n\tnext.下一页")
print(Style.RESET_ALL)
opt = input("\n\t输入操作指令(输入数字表示删除对应记录的新闻):")
if opt == "back":
break
elif opt == "prev" and page > 1:
page -= 1
elif opt == "next" and page < total_page:
page += 1
elif int(opt) >= 1 and int(opt) <= 5:
# 获取新闻在数据表中的主键ID
new_id = cur_page_result[int(opt) - 1][0]
__newsService.delete_new_by_id(new_id)
# 返回上一层
elif opt == "back":
break
# 用户管理
elif opt == "2":
while True:
os.system("cls")
print(Fore.LIGHTGREEN_EX, "\n\t1.添加用户")
print(Fore.LIGHTGREEN_EX, "\n\t2.修改用户")
print(Fore.LIGHTGREEN_EX, "\n\t3.删除用户")
print(Fore.LIGHTRED_EX, "\n\tback.返回上一层")
print(Style.RESET_ALL)
opt = input("\n\t输入操作编号:")
# 返回上一层
if opt == "back":
break
# 添加用户
elif opt == "1":
os.system("cls")
username = input("\n\t用户名:")
password = getpass("\n\t密码:")
repassword = getpass("\n\t重复密码:")
if password != repassword:
print(Style.RESET_ALL)
print("\n\t两次输入的密码不一样(3s后返回)")
time.sleep(3)
continue
email = input("\n\t邮箱:")
role_result = __roleService.search_role()
for index in range(len(role_result)):
one = role_result[index]
print(Fore.LIGHTBLUE_EX, "\n\t%d\t%s"%(index+1,one[1]))
print(Style.RESET_ALL)
opt = input("\n\t角色编号:")
role_id =role_result[int(opt)-1][0]
__userService.insert_new_user(username,password,role_id,email)
print("\n\t保存成功(3s自动返回)")
time.sleep(3)
# 修改用户
elif opt == "2":
page = 1 # 当前页码数
while True:
os.system("cls")
total_page = __userService.count_user_page() # 总页数
cur_page_result = __userService.search_all_users(page) # 当前页记录
# 逐条打印当前页记录
for index in range(len(cur_page_result)):
new = cur_page_result[index]
print(Fore.LIGHTBLUE_EX,
"\n\t%d\t%s\t%s\t%s\t%s" % (index + 1, new[1], new[2], new[3], new[4]))
print(Fore.LIGHTBLUE_EX, "\n\t-------------------------")
print(Fore.LIGHTBLUE_EX, "\n\t%d/%d" % (page, total_page))
print(Fore.LIGHTBLUE_EX, "\n\t-------------------------")
print(Fore.LIGHTRED_EX, "\n\tback.返回上一层")
print(Fore.LIGHTRED_EX, "\n\tprev.上一页")
print(Fore.LIGHTRED_EX, "\n\tnext.下一页")
print(Style.RESET_ALL)
opt = input("\n\t输入操作指令(输入数字表示要修改的用户名id):")
if opt == "back":
break
elif opt == "prev" and page > 1:
page -= 1
elif opt == "next" and page < total_page:
page += 1
elif int(opt) >= 1 and int(opt) <= 5:
os.system("cls")
username = input("\n\t新用户名:")
password = getpass("\n\t新密码:")
repassword = getpass("\n\t重复密码:")
if password != repassword:
print(Style.RESET_ALL)
print("\n\t两次输入的密码不一样(3s后返回)")
time.sleep(3)
continue
email = input("\n\t新邮箱:")
role_result = __roleService.search_role()
for index in range(len(role_result)):
one = role_result[index]
print(Fore.LIGHTBLUE_EX, "\n\t%d\t%s" % (index + 1, one[1]))
print(Style.RESET_ALL)
opt = input("\n\t新角色编号:")
role_id = role_result[int(opt) - 1][0]
user_id = cur_page_result[int(opt)-1][0]
opt = input("\n\t是否保存:Y/N?")
if opt == "Y" or opt == "y":
__userService.update_user_mess(user_id,username,password,role_id,email)
print("\n\t保存成功(3s后返回)")
elif opt == "N":
continue
__userService.insert_new_user(username, password, role_id, email)
print("\n\t保存成功(3s自动返回)")
time.sleep(3)
# 删除用户
elif opt == "3":
page = 1 # 当前页码数
while True:
os.system("cls")
total_page = __userService.count_user_page() # 总页数
cur_page_result = __userService.search_all_users(page) # 当前页记录
# 逐条打印当前页记录
for index in range(len(cur_page_result)):
new = cur_page_result[index]
print(Fore.LIGHTBLUE_EX,
"\n\t%d\t%s\t%s\t%s\t%s" % (index + 1, new[1], new[2], new[3], new[4]))
print(Fore.LIGHTBLUE_EX, "\n\t-------------------------")
print(Fore.LIGHTBLUE_EX, "\n\t%d/%d" % (page, total_page))
print(Fore.LIGHTBLUE_EX, "\n\t-------------------------")
print(Fore.LIGHTRED_EX, "\n\tback.返回上一层")
print(Fore.LIGHTRED_EX, "\n\tprev.上一页")
print(Fore.LIGHTRED_EX, "\n\tnext.下一页")
print(Style.RESET_ALL)
opt = input("\n\t输入操作指令(输入数字表示要删除的用户id):")
if opt == "back":
break
elif opt == "prev" and page > 1:
page -= 1
elif opt == "next" and page < total_page:
page += 1
elif int(opt) >= 1 and int(opt) <= 5:
os.system("cls")
user_id = cur_page_result[int(opt) - 1][0]
__userService.delete_by_id(user_id)
print("\n\t删除成功(3s自动返回)")
time.sleep(3)
# 退出登录
elif opt == "back":
break
# 退出系统
elif opt == "exit":
sys.exit(0)
# 登录失败
else:
print("\n\t登录失败(3s后自动返回)")
time.sleep(3)
# 退出系统
elif opt == "2":
sys.exit(0)