一、蓝图(BluePrint)
蓝图是什么
Flask中提供了蓝图,专门用作Flask的模块化。对于蓝图,可以看官方介绍,这里翻译过来的:
Flask使用蓝图的概念来制作应用程序组件和支持应用程序内部或跨应用程序的通用模式。蓝图可以大大简化大型应用程序的工作方式,并为Flask扩展提供了在应用程序上注册操作的中心手段。Blueprint对象的工作方式与Flask应用程序对象类似,但实际上它不是一个应用程序。相反,它是如何构造或扩展应用程序的蓝图。
总之,蓝图可以使我们的程序更加模块化,不同功能的路由可以放在不同的模块下,最后集中到启动类中
蓝图,听起来就是一个很宏伟的东西
在Flask中的蓝图 blueprint 也是非常宏伟的
它的作用就是将 功能 与 主服务 分开怎么理解呢?
比如说,你有一个客户管理系统,最开始的时候,只有一个查看客户列表的功能,后来你又加入了一个添加客户的功能(add_user)模块, 然后又加入了一个删除客户的功能(del_user)模块,然后又加入了一个修改客户的功能(up_user)模块,在这个系统中,就可以将
查看客户,修改客户,添加客户,删除客户的四个功能做成蓝图加入到客户管理系统中,本篇最后会做一个这样的例子,但是首先我们要搞清楚什么是蓝图 blueprint
初识蓝图
我们先构建一个项目的目录结构如图所示:
account.py的代码:
from flask import Blueprint # 导入 Flask 中的蓝图 Blueprint 模块 ac = Blueprint('ac', __name__) # 实例化一个蓝图(Blueprint)对象 @ac.route('/login/') # 这里添加路由和视图函数的时候与在Flask对象中添加是一样的 def login(): return 'Login'
manager.py的代码为:
from flask import Flask # 导入此前写好的蓝图模块 from student_flask.account import ac app = Flask(__name__) # type:Flask # 在Flask对象中注册蓝图模块中的蓝图对象 ac app.register_blueprint(ac) if __name__ == '__main__': app.run(debug=True) # 现在Flask对象中并没有写任何的路由和视图函数
启动服务后访问 http://127.0.0.1:5000/login/
可以看到页面返回
Login
上面的示例,我们可以看出在flask中我们并没有添加路由,但是我们注册了有路由和视图函数的ac蓝图对象
在实例化蓝图的时候我们可以传递的一些参数
目录结构为
上面我把蓝图放在一个目录下面,这个可以根据个人的喜好自己划分
user.py的代码为
from flask import Blueprint # 导入 Flask 中的蓝图 Blueprint 模块 from flask import render_template us = Blueprint("us", __name__, # 这里是相对路径,要加../ template_folder="../us_templates", # 每个蓝图都可以为自己独立出一套template模板文件夹 # 如果不写则共享项目目录中的templates,并且他会先找和manager.py同级目录下,再找student_flask static_folder="../us_static" # 静态文件目录也是可以独立出来的 ) # 实例化一个蓝图(Blueprint)对象 @us.route("/index/") def index(): return render_template("index.html")
index.html的代码为
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<p>Hello ! I am peiqip>
<img src="/us_static/xiaozhu.jpg">
body>
html>
manager.py的代码为
from flask import Flask # 导入此前写好的蓝图模块 from student_flask.views.account import ac from student_flask.views.user import us app = Flask(__name__) # type:Flask # 在Flask对象中注册蓝图模块中的蓝图对象 ac app.register_blueprint(ac) app.register_blueprint(us) if __name__ == '__main__': app.run(debug=True) # 现在Flask对象中并没有写任何的路由和视图函数
启动项目访问http://127.0.0.1:5000/index/得到
这个是不是最新很流行哈哈
从这个例子中我们总结出:
Blueprint 其实可以理解为一个了没有run方法的 Flask 对象
只要Blueprint被 Flask 注册了,就一定会生效
注意了注意了注意了重要的事情说三遍
蓝图内部的视图函数及route不要出现重复,否则你就可以尝尝自己造的孽。
二、特殊装饰器
2.1 before_request
下面有一个情景:我们在有的网页访问的时候我们需要做一个简单的认证,只有登陆的用户可以访问匿名用户不能够访问。
方法一
这里我们的第一个方法读取cookies中的session中的信息看看该用户是否登陆没登录我们就给他跳到登录页面。代码如下
from flask import Blueprint, session, redirect, url_for, render_template ac = Blueprint('ac', __name__) @ac.route('/index') def index(): if not session.get('user'): return redirect(url_for('login')) return render_template('index.html')
上面写的只是蓝图的代码,其他的请参考前面的写法。
我们上面只有一个index页面如果我们有非常多的页面要我们验证上面的方法是不是非常繁琐,且代码重复。
方法二
这里我们就想到了装饰器,
from flask import Blueprint, session, redirect, url_for, render_template import functools ac = Blueprint('ac', __name__) def auth(func): @functools.wraps(func) def inner(*args, **kwargs): if not session.get('user'): return redirect(url_for('login')) ret = func(*args, **kwargs) return ret return inner @ac.route('/index') @auth def index(): return render_template('index.html')
上面就是我们使用装饰器达到的效果,在这里我们知道如果我们有成千上万个页面需要验证只有个别的不需要验证那每次前面都要加@auth是不是有点麻烦,且有时候还有可能忘记加了。
所以在这里我们还是感觉有点麻烦。所以使用下面的方法三也就是befor_request
方法三
使用before_request。
from flask import Blueprint, session, redirect, url_for, render_template, request ac = Blueprint('ac', __name__) @ac.before_request # 这个就相当于Django中的中间件 def is_login(): # 判断是否登陆的函数 if request.path == '/login/': # 设置的白名单 return None if session.get('user'): return None return redirect('/login') @ac.route('/login/') def login(): return 'Login'
注意:上面我们的这个装饰器是写在一个蓝图里面的所以这个装饰器也就只对这个蓝图起作用属于局部的,要想全局起作用就在注册蓝图的地方写这个特殊的装饰器。
还有上面用到的是蓝图所以在使用session的时候我们在上面没有设置配置里面的参数:SECRET_KEY = "qwerdf"。
完整目录如下
全局的特殊装饰器卸载__init__.py里面。我们对flask的配置一般都写在settings里面。
@app.before_request 也是一个装饰器,他所装饰的函数,都会在请求进入视图函数之前执行
request.path 是来读取当前的url地址如果是 /login 就允许直接通过 return None 你可以理解成通过放行
校验session中是否有user 如果没有的话,证明没有登录,所以毫不留情的 redirect("/login") 跳转登录页面
还有一个要提的 @app.before_first_request 它与 @app.before_request 极为相似或者说是一模一样,只不过它只会被执行一次
@app.before_request修饰器在开发中用处非常大,比如判断某个ip是否有恶意访问行为,从而进行拦截等操作
1.2 after_request
after_request和before_request对应,它是在响应(response)之前做出响应
from flask import Flask app = Flask(__name__) @app.after_request def after1(response): print('after:1') return response @app.route('/index/') def index(): print('index') return "Index" if __name__ == '__main__': app.run()
上面代码当我们访问http://127.0.0.1:5000/index/的时候我们可以在控制台看到
index
after:1
从结果可以看出我们是在执行index函数后执行after_request这个特殊的装饰器,也就是说当用户的请求得到响应的时候才会执行after_request.
这里注意了如果和上面一样使用了蓝图来开发,那么我们after_request写的位置决定了他是在所有的蓝图其效果还是在单个蓝图里面其效果。
1.3多个before_request和after_request。
如下示例:
from flask import Flask app = Flask(__name__) @app.before_request def before1(): print('before:1') @app.before_request def before2(): print('before:2') @app.after_request def after1(response): print('after:1') return response @app.after_request def after2(response): print('after:2') return response @app.route('/index/') def index(): print('index') return "Index" if __name__ == '__main__': app.run()
当我们访问http://127.0.0.1:5000/index/的时候在管理控制台我们可以看到
before:1
before:2
index
after:2
after:1
从上面的结果我们可以看出before_request谁写在前面,当我们访问某个视图的时候就先执行谁,而after_request相反,写在前面的后执行。
如果有一个before_request有返回值那么这个请求会怎么走?看一段代码
from flask import Flask app = Flask(__name__) @app.before_request def before1(): print('before:1') return "before:1" @app.before_request def before2(): print('before:2') @app.after_request def after1(response): print("after:1") return response @app.after_request def after2(response): print("after:2") return response @app.route('/index/') def index(): print('index') return "Index" if __name__ == '__main__': app.run(debug=True)
运行上面的代码,然后访问http://127.0.0.1:5000/
我们在控制台可以看到
before:1
127.0.0.1 - - [30/Jan/2019 14:53:48] "GET / HTTP/1.1" 200 -
after:2
after:1
当请求来的时候,在第一个before_request我们有返回值的时候,程序就会直接跳过视图函数,执行after_request,执行的顺序还是先执行靠近视图函数的位置。
同时页面会返回前面要返回的信息。
1.4 before_first_request
启动flask的时候处理第第一个请求的时候会被执行,接下来的请求不会被执行, 而before_request则表示每一个请求都会执行该特殊的装饰器。
而且当第一个请求执行的时候会先执行before_first_request在执行before_request,代码如下:
from flask import Flask app = Flask(__name__) @app.before_request def xx1(): print('before_request') @app.before_first_request def x1(): print('before_first_request') @app.route('/index/') def index(): print('index') return "Index" @app.route('/order/') def order(): print('order') return "order" if __name__ == '__main__': app.run(debug=True)
我们先访问:http://127.0.0.1:5000/index/,在访问http://127.0.0.1:5000/order/
在管理控制台我们可以看到:
before_first_request
127.0.0.1 - - [30/Jan/2019 17:30:30] "GET /index/ HTTP/1.1" 200 -
before_request
index
before_request
127.0.0.1 - - [30/Jan/2019 17:30:37] "GET /order/ HTTP/1.1" 200 -
order
从上面可以看到当我们访问index的时候属于程序的第一次访问所以会执行before_first_request,当我们在访问order的时候,就没有执行before_first_request。
注意:当我们使用的访问地址为'/index/',当我们访问http://127.0.0.1:5000/index,在我这里会自动重定向到http://127.0.0.1:5000/index/这个时候就相当于发送了2次请求。
不知道这个是不是我个人情况。
1.5 errorhandler
这个可以定义一些页面显示想要的画面,比如访问出现404的时候不想要他出现404而是出现自己想要页面。
from flask import Flask app = Flask(__name__) @app.errorhandler(404) def not_found(arg): print(arg) return "没找到" @app.route('/index/') def index(): print('index') return "Index" if __name__ == '__main__': app.run(debug=True)
访问一个不存在的页面会看到页面
没找到 这三个字