数据库设计
创建数据表
需要创建两个数据表
- 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基本操作
MySQL基本操作
创建数据库操作类
为了复用代码,以及使用的安全性,我们一般会创建一个`myspl_util.py`文件,文件中包含一个`MysqlUtil`类。
import pymysql
import traceback
import 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:
self.cursor.execute(sql)
self.db.commit()
except Exception:
print("发生异常", Exception)
self.db.rollback()
finally:
self.db.close()
def fetchone(self, sql):
'''
查询数据库:单个结果集
fetchone(): 该方法获取下一个查询结果集。结果集是一个对象
'''
try:
self.cursor.execute(sql)
result = self.cursor.fetchone()
except:
traceback.print_exc()
self.db.rollback()
finally:
self.db.close()
return result
def fetchall(self, sql):
'''
查询数据库:多个结果集
fetchall(): 接收全部的返回结果行.
'''
try:
self.cursor.execute(sql)
results = self.cursor.fetchall()
except:
info = sys.exc_info()
print(info[0], ":", info[1])
self.db.rollback()
finally:
self.db.close()
return results
def delete(self, sql):
'''
删除结果集
'''
try:
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基本用法
Python操作MySQL
下边的内容涉及到的Flask 知识点较多,可以先看下面这两篇博客。
- Flask框架基础
- 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
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)
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)
db = MysqlUtil()
result = db.fetchone(sql)
password = result['password']
if sha256_crypt.verify(password_candidate, password):
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:
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)
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)
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()
效果: .详细代码可以在评论区留言哦
|