在使用了一段django之后发现django太过于冗余,而简洁的Flask貌似更适合我。

搭建环境

在这里推介大家使用pipenv这个虚拟环境

  • 安装pipenv
    1
    pip install pipenv
  • 建立虚拟环境

    1
    pipenv install 

    然后在这个文件夹内会创建2个文件
    mark

  • 启动虚拟环境

    1
    pipenv shell
  • 安装flask

    1
    pipenv install flask

    打开pycharm,把刚刚搭建的虚拟环境配置到其中。

到这里,虚拟环境就已经配置完成啦!

配置数据库

在图管理这个项目中需要用到Mysql这个数据库

导入SQLALchemy扩展

1
2
3
4
from flask_sqlalchemy import SQLAlchemy
## 数据库配置:数据库地址/关闭自动跟踪修改
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:root@127.0.0.1/flask_books'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False

需要注意的是在使用flask_sqlalchemy的时候可能会有ImportError: No module named MySQLdb

这里有个解决的办法就是到 https://www.lfd.uci.edu/~gohlke/pythonlibs/# 这里下载mysqlclient然后自行安装。

创建db对象,并配置参数

1
2
3
4
## 数据库配置:数据库地址/关闭自动跟踪修改
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:root@127.0.0.1/flask_books'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False

创建数据库&添加数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
db.drop_all()
db.create_all()

#生成数据
au1 = Author(name='老王')
au2 = Author(name='老惠')
au3 = Author(name='老刘')
db.session.add_all([au1,au2,au3])
db.session.commit()

bk1 = Book(name= '老王回忆录',author_id=au1.id)
bk2 = Book(name='我读书少,你别骗我', author_id=au1.id)
bk3 = Book(name='如何才能让自己更骚', author_id=au2.id)
bk4 = Book(name='怎样征服美丽少女', author_id=au3.id)
bk5 = Book(name='如何征服英俊少男', author_id=au3.id)
db.session.add_all([bk1,bk2,bk3,bk4,bk5])
db.session.commit()

添加书和作者模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
##a.模型继承ab.Model
##b.__tablename__表名
##c.db.Column:字段
##d.relationship :关系引用
class Author(db.Model):
# 表名

__tablename__ = 'authors'

#字段
id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(16),unique=True)

#关系引用
books = db.relationship('Book',backref='author')

def __repr__(self):
return 'Author:%s' % self.name

##书籍模型
class Book(db.Model):
__tablename__ = 'books'

id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(16) , unique=True)
author_id = db.Column(db.Integer,db.ForeignKey('authors.id'))

def __repr__(self):
return 'Book:%s %s' % (self.name,self.author_id)

使用模板显示数据库查询的数据

查询所有的作者信息,让信息传递给模板

1
2
3
authors = Author.query.all()

return render_template('books.html',authors = authors)

模板中按照格式,依次for循环作者和书籍(作者获取书籍,用的是关系引用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{#先遍历作者,然后在作者里面遍历书籍#}
<ul>
{% for author in authors %}
<li>{{ author.name }}<a href="{{ url_for("delete_author",author_id = author.id) }}">删除</a></li>
<ul>
{% for book in author.books %}
<li>{{ book.name }} <a href="{{ url_for("delete_book",book_id = book.id) }}">删除</a></li>
{% else %}
<li>无</li>

{% endfor %}

</ul>
{% endfor %}

使用WTF显示表单

自定义表单类

1
2
3
4
5
6
7
8
from flask_wtf import FlaskForm
from wtforms import StringField,SubmitField

##自定义表单类
class AuthorForm(FlaskForm):
author = StringField('作者',validators=[DataRequired()])
book = StringField('书籍', validators=[DataRequired()])
submit = SubmitField('提交')

模板中显示

1
2
3
4
5
6
7
8
9
10
11
<form method="post">
{{ form.csrf_token() }}
{{ form.author.label }}{{ form.author }}<br>
{{ form.book.label }}{{ form.book }}<br>
{{ form.submit }}<br>
{# 显示消息闪现的内容 #}
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %}

</form>

消息闪现:secret_key/csrf_token

1
2
from flask import flash
app.secret_key='baidu'
  • secret_key的作用

引用一段 Flask Web Development 中的内容:

1
2
3
SECRET_KEY 配置变量是通用密钥, 可在 Flask 和多个第三方扩展中使用. 
如其名所示, 加密的强度取决于变量值的机密度.
不同的程序要使用不同的密钥, 而且要保证其他人不知道你所用的字符串.

SECRET_KEY 的作用主要是提供一个值做各种 HASH, 我没有实际研究过源码, 不同框架和第三方库的功能也不尽相同, 我不能给出准确的答案, 但是主要的作用应该是在其加密过程中作为算法的一个参数(salt 或其他). 所以这个值的复杂度也就影响到了数据传输和存储时的复杂度.

实现相关的增删逻辑

增加书籍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
'''
验证逻辑:
1.调用WTF的函数实现验证
2.验证通过获取数据
3.判断作者是否存在
4.如果作者存在,判断书籍是否存在,没有重复书籍,添加数据,如果重复就提示错误
5.如果作者不存在,添加作者和书籍
6.验证不通过就提示错误
'''

# 1.调用WTF的函数实现验证

if author_form.validate_on_submit():
#2.验证通过获取数据
author_name = author_form.author.data
book_name = author_form.book.data

#3.判断作者是否存在
author = Author.query.filter_by(name = author_name).first()

#4.如果作者存在
if author:
#判断书籍是否存在,没有重复书籍,添加数据
book = Book.query.filter_by(name=book_name).first()
if book:
flash('已存在同名书籍')
else:
try:
new_book = Book(name=book_name,author_id=author.id)
db.session.add(new_book)
db.session.commit( )
except Exception as e:
print(e)
flash('添加书籍失败')
db.session.rollback() #回滚
else:
#5.如果作者不存在,添加作者和书籍
try:
new_author = Author(name=author_name)
db.session.add(new_author)
db.session.commit()

new_book=Book(name=book_name,author_id=new_author.id)
db.session.add(new_book)
db.session.commit()

except Exception as e:
print(e)
flash('添加作者和书籍失败')
db.session.rollback()

else:
if request.method == 'POST':
flash('参数不全')

authors = Author.query.all()
return render_template('books.html',authors = authors,form=author_form)

mark

删除书籍

删除书籍— 网页中删除 —点击需要发送书籍的ID—路由需要接受参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

@app.route('/delete_book/<book_id>')
def delete_book(book_id):


# 1.查询数据库,是否有该ID的书,如果有就删除,没有就提示错误
book = Book.query.get(book_id)

# 2.如果有就删除
if book:
try:
db.session.delete(book)
db.session.commit()
except Exception as e:
print(e)
flash('删除书籍出错')
db.SessionExtension.rollback()

else:
# 3.没有提示错误
flash('书籍找不到')

# redirect:重定向,需要传入网络/路由地址
# url_for('index'):需要传入视图函数名,返回该视图函数对应的路由地址
return redirect(url_for('index'))

删除作者

删除作者具体的思路跟删除书籍差不多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@app.route('/delete_author/<author_id>')
def delete_author(author_id):
# 查询数据库,是否有该ID的作者,如果有就删除(先删书,再删作者),没有就提示错误
# 1.查询数据库
author = Author.query.get(author_id)

# 2.如果有就删除(先删书,再删作者)
if author:
try:
#查询之后直接删除
Book.query.filter_by(author_id=author_id).delete()

#删除作者
db.session.delete(author)
db.session.commit()

except Exception as e:
print(e)
flash('删除作者出错')
db.session.rollback()

else:
flash('作者找不到')


return redirect(url_for('index'))

到这里一个简单的图书管理网站就已经做好啦,完整的项目代码:

https://github.com/dik111/Flask_book_project