学习记录:【Python项目实战】Python+MySQL开发新闻管理系统全集_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Qb4y1b75q?p=2&spm_id_from=pageDriver
目录
一、项目介绍
1、项目结构
2、系统部分功能提前展示
3、项目流程图
4、项目设计的数据库以及数据表信息
二、环境配置
三、项目实战数据库模块db编写
1、数据库连接池
2、数据库数据访问接口(DAO)
四、项目实战业务处理模块service编写
1、用户数据操作业务代码编写
五、APP程序控制台输入输出模块
1、模块介绍
2、代码
一、项目介绍
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、数据库连接池
为什么要使用数据库连接池 、好处是什么_yangniceyangyang的博客-CSDN博客_数据库连接池的作用https://blog.csdn.net/yangniceyangyang/article/details/87183013
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中,主要含登录功能和角色查找功能
涉及到的知识点:密码加密解密(这里没有使用)、数据库查找操作、表连接等
可参考:【数据库学习】——从零学习SQL语句(含SQL数据类型、SQL语句实例操作)_python_AI_fans的博客-CSDN博客https://blog.csdn.net/qq_45769063/article/details/121994889
基本步骤:
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 poolclass 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 Trueelse:return Falseexcept 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 roleexcept 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 poolclass 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 roleexcept 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 poolclass 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 resexcept 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 resexcept 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 resexcept 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 resexcept 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 userDAOclass 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 roleDAOclass 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 newDAOclass 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 timefrom 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":breakelif opt == "prev" and page > 1:page -= 1elif opt == "next" and page < total_page:page += 1elif int(opt) >= 1 and int(opt) <= 5:# 获取新闻在数据表中的主键IDnew_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":breakelif opt == "prev" and page > 1:page -= 1elif opt == "next" and page < total_page:page += 1elif int(opt) >= 1 and int(opt) <= 5:# 获取新闻在数据表中的主键IDnew_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)continueemail = 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":breakelif opt == "prev" and page > 1:page -= 1elif opt == "next" and page < total_page:page += 1elif 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)continueemail = 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":breakelif opt == "prev" and page > 1:page -= 1elif opt == "next" and page < total_page:page += 1elif 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)