数据库设计
创建数据表
需要创建两个数据表
- users:`用户表,用于存储用户信息
articles:
博客表,用于存储博客信息
下面以创建users
为例:
//如果存在重名的将其删去
DROP TABLE IF EXISTS `articles`;
CREATE TABLE `articles` (
`id` int NOT NULL AUTO_INCREMENT,
`title` varchar(255) DEFAULT NULL,
`content` text,
`author` varchar(255) DEFAULT NULL,
`create_date` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
drop table if exists
:一般drop table if exists是数据库里面的,后面接表名如:drop table if exists xxx_book意思就是:如果数据库中存在xxx_book表,就把它从数据库中drop掉。备份sql中一般都有这样的语句,如果是数据库中有这个表,先drop掉,然后create表,然后再进行数据插入。AUTO_INCREMENT
:代表值自动增加,只有主键可以添加ENGINE=InnoDB
:使用baiinnobd引擎。CHARSET=utf8
:数据库默认编码格式为utf8.
### MySQL基本操作
创建数据库操作类
为了复用代码,以及使用的安全性,我们一般会创建一个`myspl_util.py`文件,文件中包含一个`MysqlUtil`类。import pymysql # 引入pymysql模块
import traceback # 引入python中的traceback模块,跟踪错误
import sys # 引入sys模块
class MysqlUtil():
def __init__(self):
# 初始化方法,连接数据库
host = 'localhost' # 主机名
user = 'root' # 数据库用户名
password = '******' # 数据库密码
database = 'notebook' # 数据库名称
self.db = pymysql.connect(host=host,user=user,password=password,db=database) # 建立连接
self.cursor = self.db.cursor(cursor=pymysql.cursors.DictCursor) # 设置游标,并将游标设置为字典类型
def insert(self, sql):
'''
插入数据库
sql:插入数据库的sql语句
'''
try:
# 执行sql语句
self.cursor.execute(sql)
# 提交到数据库执行
self.db.commit()
except Exception: # 方法一:捕获所有异常
# 如果发生异常,则回滚
print("发生异常", Exception)
self.db.rollback()
finally:
# 最终关闭数据库连接
self.db.close()
def fetchone(self, sql):
'''
查询数据库:单个结果集
fetchone(): 该方法获取下一个查询结果集。结果集是一个对象
'''
try:
# 执行sql语句
self.cursor.execute(sql)
result = self.cursor.fetchone()
except: # 方法二:采用traceback模块查看异常
# 输出异常信息
traceback.print_exc()
# 如果发生异常,则回滚
self.db.rollback()
finally:
# 最终关闭数据库连接
self.db.close()
return result
def fetchall(self, sql):
'''
查询数据库:多个结果集
fetchall(): 接收全部的返回结果行.
'''
try:
# 执行sql语句
self.cursor.execute(sql)
results = self.cursor.fetchall()
except: # 方法三:采用sys模块回溯最后的异常
# 输出异常信息
info = sys.exc_info()
print(info[0], ":", info[1])
# 如果发生异常,则回滚
self.db.rollback()
finally:
# 最终关闭数据库连接
self.db.close()
return results
def delete(self, sql):
'''
删除结果集
'''
try:
# 执行sql语句
self.cursor.execute(sql)
self.db.commit()
except: # 把这些异常保存到一个日志文件中,来分析这些异常
# 将错误日志输入到目录文件中
f = open("\log.txt", 'a')
traceback.print_exc(file=f)
f.flush()
f.close()
# 如果发生异常,则回滚
self.db.rollback()
finally:
# 最终关闭数据库连接
self.db.close()
#以下省略更新功能
traceback.print_exc()
: 将错误信息在终端输出traceback.print_exc(file=f)
:可以将错误打印在文本中,f
为文件类对象。flush()
:flush() 方法是用来刷新缓冲区的,即将缓冲区中的数据立刻写入文件,同时清空缓冲区,不需要是被动的等待输出缓冲区写入。
一般情况下,文件关闭后会自动刷新缓冲区,但有时你需要在关闭前刷新它,这时就可以使用 flush() 方法。commit()
:涉及到更改数据表中的内容,都要在处理之后使用该方法。
exc_info()
exc_info() 方法会将当前的异常信息以元组的形式返回,该元组中包含 3 个元素,分别为 type、value 和 traceback,它们的含义分别是:- type:异常类型的名称,它是 BaseException 的子类
- value:捕获到的异常实例。
- traceback:是一个 traceback 对象。
Python操作MySQL基本用法
下边的内容涉及到的Flask
知识点较多,可以先看下面这两篇博客。
# 用户登录功能实现
创建表单类
from wtforms import Form, StringField, TextAreaField, PasswordField
from wtforms.validators import DataRequired,Length,ValidationError
from flask_wtf import FlaskForm
# 导入我么自己创建的 操作数据库的类
from mysql_util import MysqlUtil
# Register Form Class
class LoginForm(FlaskForm):
username = StringField(
'用户名',
validators=[
DataRequired(message='请输入用户名'),
Length(min=2, max=25,message='长度在4-25个字符之间')
]
)
password = PasswordField(
'密码',
validators = [
DataRequired(message='密码不能为空'),
Length(min=6,max=20,message='长度在6-20个字符之间'),
]
)
def validate_username(self,field):
sql = "SELECT * FROM users WHERE username = '%s'" % (field.data) # 根据用户名查找user表中记录
db = MysqlUtil() # 实例化数据库操作类
result = db.fetchone(sql) # 获取一条记录
if not result:
raise ValidationError("用户名不存在")
上述代码中,在Login Form
中定义了useame
和password
字段,它们分别对应着登录页面表单中的用户名和密码。
此外,还使用validate
多段名函数对usermame
字段添加自定义验证规则,判断用户名是否存在。
实现登录功能
当用户填写登录信息后,如果验证全部通过,需要将登录标识
和username
写入Session
中,为后面判断用户是否登录做准备。此外,还需要在用户访问/logm路由时,判断用户是否已经登录。如果用
户之前已经登录过,那么则不需要再次登录,而是直接跳转到控制台。
@app.route('/login', methods=['GET', 'POST'])
def login():
if "logged_in" in session: # 如果已经登录,则直接跳转到控制台
return redirect(url_for("dashboard"))
form = LoginForm(request.form) # 实例化表单类
if form.validate_on_submit(): # 如果提交表单,并字段验证通过
# 从表单中获取字段
username = request.form['username']
password_candidate = request.form['password'] # 用户填写的密码
sql = "SELECT * FROM users WHERE username = '%s'" % (username) # 根据用户名查找user表中记录
db = MysqlUtil() # 实例化数据库操作类
result = db.fetchone(sql) # 获取一条记录
password = result['password'] # 用户注册时候的密码
# 对比用户填写的密码和数据库中记录密码是否一致
if sha256_crypt.verify(password_candidate, password): # 调用verify方法验证,如果为真,验证通过
# 写入session
session['logged_in'] = True
session['username'] = username
flash('登录成功!', 'success') # 闪存信息
return redirect(url_for('dashboard')) # 跳转到控制台
else: # 如果密码错误
flash('用户名和密码不匹配!', 'danger') # 闪存信息
return render_template('login.html',form=form)
request.form
**解释:**通过requset.form
可以直接提取请求体中的表单格式的数据, 是一个类字典的对象,所以可以通过request.form['username']
获得表单中字段为username
对应的数据。
sha256_crypt.verify()
解释:verify()
方法第一个参数是用户1输入的密码,第二个参数是数据库加密后的密码,如果返回True
,则表示密码相同。
session['logged_in'] = True
解释:Session
对象是一个字典对象,包含会话变量和关联值的键值对。在进行上述设置后,就可以在其余函数中采用session.get('logged_in')
,即字典取值的方法判断用户是否已经登录。
flash('登录成功!', 'success') # 闪存信息
**解释:**是用来闪现需要显示给用户的内容,上面写入后,可以在需要读取的地方进行读取,如
{% for message in get_flashed_messages() %}
<div class=flash>{{ message }}</div>
{% endfor %}
return render_template('login.html',form=form)
**解释:**通过将表单类参数传入模板,在模板中生成一个表单。
# 用户权限管理功能实现 对于需要用户登录后才能访问的路由,我们可以实现一个装饰器。
def is_logged_in(f):
@wraps(f)
def wrap(*args, **kwargs):
if 'logged_in' in session: # 判断用户是否登录
return f(*args, **kwargs) # 如果登录,继续执行被装饰的函数
else: # 如果没有登录,提示无权访问
flash('无权访问,请先登录', 'danger')
return redirect(url_for('login'))
return wrap
- 装饰器的作用: 在不改变原有功能代码的基础上,添加额外的功能,如用户验证等。
- @wraps(view_func)的作用: 不改变使用装饰器原有函数的结构(如name, doc)
- 需要导入库:
from functools import wraps
*args
:表示任何多个无名参数,它是一个tuple**kwargs
:表示关键字参数,它是一个dict
# 博客模块设计
博客列表功能实现
@app.route('/dashboard')
@is_logged_in
def dashboard():
db = MysqlUtil() # 实例化数据库操作类
sql = "SELECT * FROM articles WHERE author = '%s' ORDER BY create_date DESC" % (session['username']) # 根据用户名查找用户笔记信息
result = db.fetchall(sql) # 查找所有笔记
if result: # 如果笔记存在,赋值给articles变量
return render_template('dashboard.html', articles=result)
else: # 如果笔记不存在,提示暂无笔记
msg = '暂无笔记信息'
return render_template('dashboard.html', msg=msg)
session['username']
**解释:**因为在这之前用户已经登录成功,所以可以用上述方法来获取用户名。
添加博客功能实现
@app.route('/add_article', methods=['GET', 'POST'])
@is_logged_in
def add_article():
form = ArticleForm(request.form) # 实例化ArticleForm表单类
if request.method == 'POST' and form.validate(): # 如果用户提交表单,并且表单验证通过
# 获取表单字段内容
title = form.title.data
content = form.content.data
author = session['username']
create_date = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
db = MysqlUtil() # 实例化数据库操作类
sql = "INSERT INTO articles(title,content,author,create_date) \
VALUES ('%s', '%s', '%s','%s')" % (title,content,author,create_date) # 插入数据的SQL语句
db.insert(sql)
flash('创建成功', 'success') # 闪存信息
return redirect(url_for('dashboard')) # 跳转到控制台
return render_template('add_article.html', form=form) # 渲染模板
request.method == 'POST' and form.validate():
解释:validate
是验证数据的意思
所以from.validate_on_submit()
等价于request.method==' post ' and from.validate()
效果:
.详细代码可以在评论区留言哦