flask blueprint
为什么使用蓝图?
Flask 中蓝图有以下用途:
-
把一个应用分解为一套蓝图。这是针对大型应用的理想方案:一个项目可以实例化 一个应用,初始化多个扩展,并注册许多蓝图。
-
在一个应用的 URL 前缀和(或)子域上注册一个蓝图。 URL 前缀和(或)子域的 参数成为蓝图中所有视图的通用视图参数(缺省情况下)。
-
使用不同的 URL 规则在应用中多次注册蓝图。
-
通过蓝图提供模板过滤器、静态文件、模板和其他工具。蓝图不必执行应用或视图 函数。
-
当初始化一个 Flask 扩展时,为以上任意一种用途注册一个蓝图。
Flask 中的蓝图不是一个可插拨的应用,因为它不是一个真正的应用,而是一套可以 注册在应用中的操作,并且可以注册多次。那么为什么不使用多个应用对象呢?可以 使用多个应用对象(参见 应用调度 ),但是这样会导致每个 应用都使用自己独立的配置,且只能在 WSGI 层中管理应用。
而如果使用蓝图,那么应用会在 Flask 层中进行管理,共享配置,通过注册按需改 变应用对象。蓝图的缺点是一旦应用被创建后,只有销毁整个应用对象才能注销蓝图。
蓝图的概念
蓝图的基本概念是:在蓝图被注册到应用之后,所要执行的操作的集合。当分配请求 时, Flask 会把蓝图和视图函数关联起来,并生成两个端点之前的 URL 。
第一个蓝图
以下是一个最基本的蓝图示例。在这里,我们将使用蓝图来简单地渲染静态模板:
from flask import Blueprint, render_template, abort from jinja2 import TemplateNotFound simple_page = Blueprint('simple_page', __name__, template_folder='templates') @simple_page.route('/', defaults={'page': 'index'}) @simple_page.route('/<page>') def show(page): try: return render_template(f'pages/{page}.html') except TemplateNotFound: abort(404)
注册蓝图
from flask import Flask from yourapplication.simple_page import simple_page app = Flask(__name__) app.register_blueprint(simple_page)
以下是注册蓝图后形成的规则:
app.url_map Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>, <Rule '/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>, <Rule '/' (HEAD, OPTIONS, GET) -> simple_page.show>])
第一条很明显,是来自于应用本身的用于静态文件的。后面两条是用于蓝图 simple_page
的 show 函数的。你可以看到,它们的前缀都是蓝图的名称,并 且使用一个点( .
)来分隔。
蓝图还可以挂接到不同的位置:
app.register_blueprint(simple_page, url_prefix='/pages')
这样就会形成如下规则:
app.url_map Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>, <Rule '/pages/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>, <Rule '/pages/' (HEAD, OPTIONS, GET) -> simple_page.show>])
总之,你可以多次注册蓝图,但是不一定每个蓝图都能正确响应。是否能够多次注册 实际上取决于你的蓝图是如何编写的,是否能根据不同的位置做出正确的响应。
嵌套蓝图
把一个蓝图注册在另一个蓝图上是可行的。
parent = Blueprint('parent', __name__, url_prefix='/parent') child = Blueprint('child', __name__, url_prefix='/child') parent.register_blueprint(child) app.register_blueprint(parent)
子蓝图会把父蓝图的名称作为其前缀,子 URL 也会把父 URL 作为前缀。
url_for('parent.child.create')
/parent/child/create
父蓝图指定的请求前函数等会为子蓝图触发。如果子蓝图没有可以处理异常的出错 处理器,那么会尝试父蓝图的出错处理。
静态文件
蓝图的第三个参数是 static_folder
。这个参数用以指定蓝图的静态文件所在的 文件夹,它可以是一个绝对路径也可以是相对路径。
admin = Blueprint('admin', __name__, static_folder='static')
缺省情况下,路径最右端的部分是在 URL 中暴露的部分。这可以通过 static_url_path
来改变。因为上例中的文件夹为名称是 static
,那么 URL 应该是蓝图的 url_prefix
加上 /static
。 如果蓝图注册前缀为 /admin
,那么静态文件 URL 就是 /admin/static
。
端点的名称是 blueprint_name.static
。你可以像对待应用中的文件夹一样 使用 url_for()
来生成其 URL:
url_for('admin.static', filename='style.css')
但是,如果蓝图没有 url_prefix
,那么不可能访问蓝图的静态文件夹。 这是因为在这种情况下,URL应该是 / static
,而应用程序的 / static
路线优先。与模板文件夹不同,如果文件不存在于应用静态文件夹中,那么不会 搜索蓝图静态文件夹。
模板
如果你想使用蓝图来暴露模板,那么可以使用 Blueprint
的 template_folder 参数:
admin = Blueprint('admin', __name__, template_folder='templates')
案例
文件架构
apps --user --__init__.py --model.py --view.py --__init__.py --setting.py static templates --base.html --user --register.html --login.html --show.html app.py
app.py
from apps import create_app app = create_app() if __name__=='__main__': app.run()
apps/__init__.py
from flask import Flask from . import setting from apps.user import user_bp def create_app(): app = Flask(__name__,template_folder='../templates',static_folder='../static') #指定蓝图的模版、静态文件 app.config.from_object(setting) #蓝图 app.register_blueprint(user_bp) #注册蓝图 print(app.url_map) return app
apps/setting.py
# 配置文件 ENV = 'development' DEBUG = True
apps/user/__init__.py
from flask import Blueprint user_bp=Blueprint('users',__name__)创建蓝图 from .view import *
apps/user/model.py
class User: def __init__(self,username,password,phone=None): self.username = username self.password = password self.phone =phone def __str__(self): return self.username
apps/user/view.py
from flask import request,render_template,redirect from apps.user.model import User from apps.user import user_bp users = [] @user_bp.route('/',endpoint='usercenter') def user_center(): return render_template('user/show.html',users=users) @user_bp.route('/register',methods = ['GET','POST']) def register(): if request.method == 'POST': username = request.form.get('username') password = request.form.get('password') repassword = request.form.get('repassword') phone = request.form.get('phone') if password == repassword: for user in users: if user.username == username: return render_template('user/register.html',msg='用户名已存在') user = User(username, password, phone) users.append(user) print(users) return redirect('/') return render_template('user/register.html') @user_bp.route('/login',methods = ['GET','POST']) def login(): return 'user-login' @user_bp.route('/logout',methods = ['GET','POST']) def logout(): return 'user-logout' @user_bp.route('/del') def del_user(): username = request.args.get('username') for user in users: if user.username == username: users.remove(user) return '删除成功' else: return '删除失败'
template/base.html
<!doctype html> <html lang="en"> <head> <title>{% block title %}{% endblock %}</title> {% block mycss %}{% endblock %} <style> #head{ height: 50px; background-color: bisque; } #head ul{ list-style: none; height: 50px; } #head ul li{ float: left; width: 100px; text-align: center; font-size: 18px; line-height: 50px; } middle{ height: 900px; background-color: darkseagreen; } foot{ height: 50px; line-height: 50px; } </style> </head> <body> <div id="head"> <ul> <li>首页</li> <li>秒杀</li> <li>超市</li> <li>图书</li> <li>会员</li> </ul> </div> <div id="center"> {% block center %} {% endblock %} </div> <div id="foot"> {% block foot %} {% endblock %} </div> {% block myjs %}{% endblock %} </body> </html>
template/user/register.html
{% extends 'base.html' %} {% block title %} 用户注册 {% endblock %} {% block center %} <p>{{ msg }}</p> <form action="/register" method="post"> <p><input type="text" name="username" placeholder="用户名"></p> <p><input type="password" name="password" placeholder="密码"></p> <p><input type="password" name="repassword" placeholder="确认密码"></p> <p><input type="number" name="phone" placeholder="手机号码"></p> <p><input type="submit" value="用户注册"></p> </form> {% endblock %} {% block foot %} {% endblock %}
template/user/show.html
{% extends 'base.html' %} {% block center %} <h1>用户信息</h1> <span>当前用户人数:{{ users|length }}</span> <table> {% for user in users %} <tr> <td>{{ loop.index }}</td> <td>{{ user.username }}</td> <td>{{ user.password }}</td> <td>{{ user.phone }}</td> <td><a href="">修改</a><a href="javascript:;" onclick="del('{{ user.username }}')">删除</a></td> </tr> {% endfor %} </table> {% endblock %} {% block myjs %} <script> function del(username){ //consul.log(username) location.href = '/del?username=' +username } </script> {% endblock %}
参考:
https://dormousehole.readthedocs.io/en/latest/tutorial/views.html?highlight=%E8%93%9D%E5%9B%BE