蓝图
在前面,我们都是把代码写在单一的文件里面,虽然看起来很方便,但也只是供学习的时候用用而已,真正在一个实际项目中,是不应该这样做的,为什么呢?
我们还是从 hello world 开始讲起,新建一个脚本文件,比如 hello.py。
1. $ cat hello.py
3. # -*- coding: utf-8 -*-
5. from flask import Flask
7. app = Flask(__name__)
9. @app.route("/")
10. def index():
11. return "Hello World!"
13. if __name__ == "__main__":
14. app.run(host='127.0.0.1', port=5200, debug=True)
运行该脚本,在浏览器输入链接 http://localhost:5200/,可以看到 Hello World! 的字样。
OK,现在我们需要添加一个『读书』频道,允许查看并添加书籍。这不很简单吗,将上面的代码修改一下,如下:
1. $ cat hello.py
3. # -*- coding: utf-8 -*-
5. from flask import Flask, request, redirect, url_for
7. app = Flask(__name__)
8. app.secret_key = 'some secret key'
10. books = ['the first book', 'the second book', 'the third book']
12. @app.route("/")
13. def index():
14. render_string = '<ul>'
16. for book in books:
17. render_string += '<li>' + book + '</li>'
19. render_string += '</ul>'
21. return render_string
23. @app.route("/book", methods=['POST', 'GET'])
24. def book():
25. _form = request.form
27. if request.method == 'POST':
28. title = _form["title"]
29. books.append(title)
30. return redirect(url_for('index'))
32. return '''
33. <form name="book" action="/book" method="post">
34. <input id="title" name="title" type="text" placeholder="add book">
35. <button type="submit">Submit</button>
36. </form>
37. '''
39. if __name__ == "__main__":
40. app.run(host='127.0.0.1', port=5200, debug=True)
OK,上面的脚本实现了最基本的查看并添加书籍的功能。
接着,我们还想添加『电影』、『音乐』、『美食』和『旅游』等频道,我们是继续往这个文件添加功能吗?当然不是,是时候把这些功能进行拆分了,将我们的应用模块化,把一个大应用分解成若干个小应用。你可能会想:把这些功能分别写到多个文件,比如 movie.py,music.py 等等,可是这样的话,我们的应用是跑不起来的。为什么呢?因为 app 对象只有一个。
那怎么办呢?
事实上,Flask 提供了 Blueprint (蓝图) 的功能,让我们可以实现模块化的应用。使用它主要有以下好处:
- 将一个复杂的大型应用分解成若干蓝图的集合,也就是若干个子应用或者说模块,每个蓝图都包含了可以作为独立模块的视图、模板和静态文件等;
- 制作通用的组件,使开发者更易复用组件;
不过目前 Flask 蓝图的注册是静态的,不支持可插拔。
现在,让我们将上面的代码进行改写,加入蓝图的功能。
首先,我们对程序的结构做一些调整,如下:
1. ├── app.py -- 启动程序
2. ├── book -- book 模块
3. │ ├── __init__.py
4. │ └── book.py
5. ├── movie -- movie 模块
6. │ ├── __init__.py
7. │ └── movie.py
8. └── templates -- 模板
9. ├── 404.html
10. ├── book.html
11. ├── layout.html
12. └── movie.html
其中,movie 模块跟 book 模块是独立的,它们有各自的业务逻辑,互不影响。我们这里只分析 book 模块,book.py 的代码如下:
1. # -*- coding: utf-8 -*-
3. from flask import Blueprint, url_for, render_template, request, flash, redirect
5. # 创建一个蓝图对象
6. book_bp = Blueprint(
7. 'book',
8. __name__,
9. template_folder='../templates',
10. )
12. books = ['The Name of the Rose', 'The Historian', 'Rebecca']
14. @book_bp.route('/', methods=['GET'])
15. def index():
16. return '<h1>Hello World!</h1>'
18. @book_bp.route('/book', methods=['GET', 'POST'])
19. def handle_book():
20. _form = request.form
22. if request.method == 'POST':
23. title = _form["title"]
24. books.append(title)
25. flash("add book successfully!")
26. return redirect(url_for('book.handle_book'))
28. return render_template(
29. 'book.html',
30. books=books
31. )
33. @book_bp.route('/book/<name>')
34. def get_book_info(name):
35. book = [name]
36. if name not in books:
37. book = []
39. return render_template(
40. 'book.html',
41. books=book
42. )
注意到,我们使用了下面的代码创建一个蓝图对象:
1. book_bp = Blueprint('book', __name__, template_folder='../templates')
Blueprint 要求至少传入两个参数,第一个参数是蓝图的名称,第二个参数是蓝图所在的包或模块,其他参数是可选的,比如template_folder,url_prefix和static_folder等。
在蓝图中使用路由是这样的:
1. # book_bp 就是我们创建的蓝图对象
2. @book_bp.route('/book/<name>')
创建好了蓝图之后,我们还需要注册它,否则不能使用。我们在app.py中注册:
1. # -*- coding: utf-8 -*-
3. from flask import Flask, render_template
4. from book import book_bp
5. from movie import movie_bp
7. app = Flask(__name__)
8. app.secret_key = 'The quick brown fox jumps over the lazy dog'
10. # 注册蓝图
11. app.register_blueprint(book_bp)
12. app.register_blueprint(movie_bp)
14. @app.errorhandler(404)
15. def page_not_found(error):
16. return render_template('404.html'), 404
19. if __name__ == '__main__':
20. app.run(host='127.0.0.1', port=5200, debug=True)
本文完整的代码在这里。
在命令行中输入$ python app.py,我们就可以访问该应用了,如下:


总结
- 使用蓝图对我们的应用进行模块化
- 通过添加蓝图扩展我们的应用
- 蓝图定义完之后,还需要在应用中注册
- 可以给蓝图中的所有路由定义一个 URL 前缀 (url_prefix)
