Flask - 装饰器使用,路由,实例化配置,对象配置,蓝图,特殊装饰器
一.视图函数多个装饰器报错问题
在我们使用多个装饰器对视图函数进行装饰时,会出现以下报错:
AssertionError: View function mapping is overwriting an existing endpoint function: warrper
我们会发现上有一个词是endpoint,也就是端点,在flask中路由到视图的过程中实际上还有一层,(URL-->endpoint-->viewfunction)从根本上来说,端点就是程序中一组逻辑处理单元的ID,该ID对应的代码决定了对此ID请求应该作出何种响应。通常,端点与视图函数同名,但是在当前的环境下,端点的名字必须是唯一的,这样在必要的时候就可以修改它了,
解决方案一:
from flask import Flask from flask import (redirect, render_template) from flask import request, session from flask import jsonify from flask import send_file from functools import wraps app = Flask(__name__) app.secret_key = 'asbhwui189asdnw' account_info = { "alex": ["123", 'man', 30, 'CEO'], "zj": ["234", 'man', '27', 'student'], "wusir": ["123", 'man', '35', 'manager'] } # 校验装饰器 def login_require(func): def warrper(*args, **kwargs): if not session.get('username'): return redirect('/login') return func(*args, **kwargs) return warrper # 设置endpoint @app.route("/", endpoint='index') @login_require def index(): name = 'alex' return render_template('index.html', name=name) # 设置endpoint @app.route("/userinfo", endpoint='user_info') @login_require def user_info(): return render_template('info.html', username=session.get('username'), info=account_info[session.get('username')][1:]) @app.route("/login", methods=['GET', 'POST']) def login(): # 获取请求方式 if request.method == 'GET': return render_template('login.html') # 获取formdata中的数据 if request.form.get('username') in account_info.keys() and \ request.form.get('password') == account_info[request.form.get('username')][0]: session['username'] = request.form.get('username') return redirect('/') else: return "用户名或密码错误!"
上面的代码中我们使用了login_require装饰器,多个装饰器,在执行过程中是先执行login_require,返回了warrper的函数对象,然后再使用app.route对warrper进行装饰,这个过程中如果不指定endpoint,当前的视图endpoint就会成为warrper,多个视图函数使用了login_require就会出现endpoint重名的情况,也就出现了上面的报错,所以直接指定endpoint就可以解决这个问题。
解决方案二:
使用functool工具
from functools import wraps def login_require(func): @wraps(func) def warrper(*args, **kwargs): if not session.get('username'): return redirect('/login') return func(*args, **kwargs) return warrper
使用这种方法之后就无须再指定endpoint了。
二.Flask中的路由
1.endpoint
看下面两段代码:
@app.route('/greeting/<name>') def give_greeting(name): return 'Hello, {0}!'.format(name)
注意,add_url_rule函数实现了同样的目的,只不过没有使用装饰器,因此,下面的程序是等价的:
# 抬头没有使用路由装饰器,我们在最后用另一种方法添加路由. def give_greeting(name): return 'Hello, {0}!'.format(name) app.add_url_rule('/greeting/<name>', 'give_greeting', give_greeting)
def route(self, rule, **options): def decorator(f): endpoint = options.pop('endpoint', None) self.add_url_rule(rule, endpoint, f, **options) return f return decorator
我们看route的源码时,发现也是调用了app.add_url_rule方法
而 add_url_rule()中3个参数依次是rule、view_func、endpoint.
当用户输入url请求过来的时候,拿着这个url地址到路由表中做匹配,flask发现这个地址指向了某个视图函数。
然而,当我们用这种最常用的方法创建视图时,flask却向我们隐藏了一些其他的细节信息。在这个场景中,flask并没有直接从URL地址跳转到应该响应它请求的视图函数上:
事实上,这里还有另一个步骤--把URL地址映射到端点上(URL-->endpoint-->viewfunction)
从根本上来说,端点就是程序中一组逻辑处理单元的ID,该ID对应的代码决定了对此ID请求应该作出何种响应
2.endpoint有什么作用(url_for)
端点通常用作反向查询URL地址(viewfunction-->endpoint-->URL)。例如,在flask中有个视图,你想把它关联到另一个视图上(或从站点的一处连接到另一处)。不用去千辛万苦的写它对应的URL地址,直接使用URL_for()就可以啦:
from flask import Flask from flask import url_for app = Flask(__name__) @app.route('/home', endpoint='myhome') def home(): get_path() return 'Home' def get_path(): # url_for 通过端点查找对应的视图函数路由名称 print(url_for('myhome')) # 打印 /home if __name__ == '__main__': app.run(debug=True)
3.methods(请求方式)
@app.route('/home', methods=['get', 'post']) def home(): return 'Home' if __name__ == '__main__': app.run(debug=True)
在设置路由时,还有指定只处理哪些类型的请求,
注意:这里如果不指定methods默认时只支持get方式的请求,如果指定了methods,则只处理该参数中选项的请求。
4.默认参数(后续补充)
defaults={"nid":"123456"} 默认参数
5.路由严格模式
strict_slashes=True #是否严格遵循路由地址
from flask import Flask app = Flask(__name__) @app.route('/home', methods=['get', 'post'], strict_slashes=True) def home(): return 'Home' if __name__ == '__main__': app.run(debug=True)
不设置该参数,默认值为flase,是非严格模式,可以匹配/home和/home/的路由,设置了严格模式之后就只能匹配/home。

6.永久重定向
redirect_to="/login" 永久重定向 301
from flask import Flask app = Flask(__name__) # 重定向到百度 @app.route('/home', methods=['get', 'post'], redirect_to='http://www.baidu.com') def home(): return 'Home' if __name__ == '__main__': app.run(debug=True)
7.动态路由匹配
在日常访问的url中,会发现很多地址中都包含可变部分,在flask中,可以直接在路由上设置动态路由匹配。
@app.route('/book/<page>') def book(page): return page
上面代码中的page就是动态可变的,默认的是string类型,如果你想让接收的字符是数字可以写成<int:page>的形式。
三.flask实例化配置
1.template_folder="temp" 默认模板路径 templates 2.static_folder="static", 默认静态文件路径 static 3.static_url_path="/static" 访问静态文件路由地址 默认是"/"+static_folder 4.static_host=None 指定静态文件服务器地址 5.host_matching = False, # 如果不是特别需要的话,慎用,否则所有的route 都需要host=""的参数 6.subdomain_matching = False, # 理论上来说是用来限制SERVER_NAME子域名的,但是目前还没有感觉出来区别在哪里 7.instance_path = None, # 指向另一个Flask实例的路径 8.instance_relative_config = False # 是否加载另一个实例的配置 9.root_path = None # 主模块所在的目录的绝对路径,默认项目目录
示例:
from flask import Flask, render_template # 设置模板目录,设置静态文件目录,设置静态文件访问路由url前缀 app = Flask(__name__, template_folder='tmp_template', static_folder='tmp_static', static_url_path='/static') @app.route('/index', methods=['get', 'post']) def home(): return render_template('index.html') if __name__ == '__main__': app.run(debug=True)


四.flask对象配置
'DEBUG': False, # 是否开启Debug模式 'TESTING': False, # 是否开启测试模式 'SECRET_KEY': None # 在启用Flask内置Session的时候/开启flash,一定要有它 'PERMANENT_SESSION_LIFETIME': 31, # days , Session的生命周期(天)默认31天 'SESSION_COOKIE_NAME': 'session', # 在cookies中存放session加密字符串的名字
使用对象的方式来对app对象进行配置
class FlaskDebug(object): DEBUG = True SECRET_KEY = "DEBUGmoshidesecret_key" PERMANENT_SESSION_LIFETIME = 7 SESSION_COOKIE_NAME = "debug_session" class FlaskProduct(object): DEBUG = False SECRET_KEY = "Dsecret_key&*(JKjk^VJ" PERMANENT_SESSION_LIFETIME = 30 SESSION_COOKIE_NAME = "session"
from flask import Flask, render_template from app_config import FlaskDebug, FlaskProduct # 设置模板目录,设置静态文件目录,设置静态文件访问路由url前缀 app = Flask(__name__, template_folder='tmp_template', static_folder='tmp_static', static_url_path='/static') # app.config.from_object app.config.from_object(FlaskProduct) @app.route('/index', methods=['get', 'post']) def home(): return render_template('index.html') if __name__ == '__main__': app.run()
五.蓝图(blueprint)
可以把Blueprint 当成一个不能被启动的 app Flask示例
create.views
from flask import Blueprint, render_template # url_prefix 路由前缀,必须为路由地址要加'/' create_app = Blueprint("create", __name__, template_folder='template', url_prefix='/create') @create_app.route('/create') def create(): return render_template('create.html')
s1
from flask import Flask from create import views app = Flask(__name__) # 将蓝图注册到app中 app.register_blueprint(views.create_app) app.run(debug=True)
使用蓝图,可以发现几乎跟实例化一个flask对象是相同的语法,使用蓝图可以让程序结构更加清晰,每一个蓝图都实现预期的功能,也有助于后期代码维护。
六.flash(待补充)
from flask import flash,get_flashed_messages flash("message","tag") get_flashed_messages()
七.flask特殊装饰器(类似与django中间件)
from flask import Blueprint, render_template create_app = Blueprint("create", __name__, template_folder='template', url_prefix='/create') @create_app.route('/create') def create(): return render_template('create.html') # before_request 处理请求前,如果return不是None,则直接跳过视图函数 @create_app.before_request def be1(): print('be1') @create_app.before_request def be2(): print('be2') @create_app.before_request def be3(): print('be3') # 请求处理之后,函数必须接收一个返回值(响应体),必须有一个返回值(响应体), @create_app.after_request def af1(res): print('af1') return res @create_app.after_request def af2(res): print('af2') return res @create_app.after_request def af3(res): print('af3') return res
@app.before_request # 请求进入视图函数之前
@app.after_request # 响应返回客户端之前
正常情况下流程:be1 - be2 - be3 - af3 - af2 - af1
异常情况下流程:be1 - af3 - af2 - af1
@app.errorhandler(404) # 重定义错误页面返回信息
from flask import Flask from create import views app = Flask(__name__) app.register_blueprint(views.create_app) # errorhandker只能使用flask实例设置,蓝图设置无效,接收一个参数(错误信息),返回一个响应对象。 # 404表示捕获的错误code,也可以定义405等其他的错误码 @app.errorhandler(404) def error404(args): print(args) return '页面被吃掉啦!' app.run(debug=True)
这些特殊的装饰器,我们可以用于身份校验,限流等全局策略。
浙公网安备 33010602011771号