Flask框架视图多层装饰器问题
Flask中的app.route装饰器
我们知道,在flask框架中,我们的路由匹配就是通过有参装饰器来实现的,我们看一个简单的例子:
from flask import Flask, render_template, redirect, request, session
app = Flask(__name__)
app.debug = True
app.secret_key = ''
@app.route('/index')
def index():
return "这是主页" @app.route("/detail")
def detail():
return "这是详情页面" if __name__ == '__main__':
app.run()
这个装饰器我们见怪不怪,很明显是可以正常运行的;
Flask视图的多个装饰器
现在需求来了,我们需要给某些视图函数添加一个装饰器,用来验证用户是否是登录的状态,只有登录的用户才能访问,否则就让他跳转登录页面去登录。
很多小伙伴很机智,很快就想到了装饰器的嵌套,对的,我们可以用多个装饰器来装饰,既然这样,问题又来了,装饰器是写在route装饰器上方呢,还是下方呢?
1.源码分析
这里我们截取一点route源码
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的源码,不难发现其实route方法也是返回了一个装饰器,然后用来装饰我们的视图函数,并且在add_url_rule中匹配了访问路径,以及处理视图函数的逻辑;
也就是说,route装饰器应该是最后来处理视图函数的逻辑的,那么很明显,route应该装饰在视图函数的外层,装饰被其他装饰器装饰过后返回的视图函数。
2.装饰器实践
有了思路后,一顿操作我把多层装饰器写出来了,代码如下
from flask import Flask, request, render_template, session, redirect
app = Flask(__name__)
app.debug = True
def wrapper(func):
"""验证登录状态装饰器函数"""
def inner(*args,**kwargs):
if session.get("username"):
# 如果session中有用户信息,正常执行
ret = func(*args, **kwargs)
return ret
else:
return redirect("/login")
return inner
@app.route("/login",methods=["get","post"])
def login():
if request.method == "GET":
return render_template("login.html")
else:
username = request.form.get("username")
password = request.form.get("password")
if username == "" and password == "":
session["username"] = username
return "登录成功!"
else:
return "登录失败!"
@app.route("/index")
@wrapper
def index():
return "这是主页"
@app.route("/detail")
@wrapper
def detail():
return "这是详情页面"
if __name__ == '__main__':
app.run()
激动的启动了我的项目,一秒都不用,啪,报错了。~~~
这是什么鬼!我们仔细看一下报错信息,视图函数映射被一个存在的末端函数inner覆盖写入了。
问题分析
报错说视图函数被一个存在的函数inner覆盖写入了,我们想一想,哪里用了inner啊,不就是装饰函数内的inner函数吗?
我们大概看一下源码,这里我截取的一部分
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
options["endpoint"] = endpoint
methods = options.pop("methods", None) if view_func is not None:
old_func = self.view_functions.get(endpoint)
if old_func is not None and old_func != view_func:
raise AssertionError(
"View function mapping is overwriting an "
"existing endpoint function: %s" % endpoint
)
self.view_functions[endpoint] = view_func
肯定有很多小伙伴看到这里,已经晕了,这里我就大概解释一下:
endpoint是我们传递的一个用来给视图函数去别名的变量,我们不传的情况下,默认是None,如果是None,那么endpoint会调用一个方法_endpoint_from_view_func去获取被装饰的函数的名字,因为我们的route装饰的是被wrapper装饰后返回的inner函数。
第一次在装饰index视图的时候,endpoint被赋值为inner,添加到self.view_functions中,第二次装饰detail的时候,route又装饰了第一次装饰过detail返回的inner。注意:两个inner名字是一样的,但是内容不一样。问题就在这里了,route装饰inner的时候,发现有两个inner,名字一样,内容不一样,这个时候route就不知道改装饰哪一个了,因为第二个inner会覆盖view_functions中第一次存入的inner函数,就会出现刚才的报错。
问题解决方法
问题的原因我们发现了,本质上就是wrapper两次装饰了不同视图函数,但是返回的都是inner函数,导致route装饰的时候出现覆盖的情况才报错。
我们只要让两次装饰后的inner函数名不一样就可以了,对不对!
方法一:endpoint
我们看过了源码,名字冲突的原因是因为每次都是去获取inner的__name__
,很明显会冲突,但是去获取名字的前提是因为我们没有传递endpoint别名,这样就好办了。
我们在使用route装饰的时候加上别名就轻松解决了。
@app.route("/index",endpoint="index")
@wrapper
def index():
return "这是主页"
@app.route("/detail",endpoint="detail")
@wrapper
def detail():
return "这是详情页面"
修改后运行,问题迎刃而解,so easy啊!
方法二:wraps工具
同样的问题,既然是名字冲突,那么我们是不是可以想到functiontool中的wraps工具呢,就是让内层函数使用原本函数的属性字典__dict__
,也包括名字啦。
我们在写wrapper装饰器的时候使用上
from functools import wraps
def wrapper(func):
"""验证登录状态装饰器函数"""
@wraps(func)
def inner(*args,**kwargs):
if session.get("username"):
# 如果session中有用户信息,正常执行
ret = func(*args, **kwargs)
return ret
else:
return redirect("/login")
return inner
修改运行,同样完美结局,excellent!
Flask框架视图多层装饰器问题的更多相关文章
- flask笔记(三)Flask 添加登陆验证装饰器报错,及解析
Flask 添加登陆验证装饰器报错,及解析 写这个之前,是想到一个需求,这个是关于之前写Flask笔记(二)中的一个知识点,路由相关 需求为 : 有一些页面必须是登陆之后才能访问的,比如Shoppin ...
- python 多层装饰器
25.多层装饰器: 1.原理:执行顺序从上往下,#2和#3组成一个函数假设为nf1,#1和nf1组成一个函数nnf1 f1成为ck_ty_of_us的inner函数即nf ...
- Django中类视图使用装饰器的方式
类视图使用装饰器 为类视图添加装饰器,可以使用两种方法. 为了理解方便,我们先来定义一个为函数视图准备的装饰器(在设计装饰器时基本都以函数视图作为考虑的被装饰对象),及一个要被装饰的类视图. def ...
- python-day5-装饰器第二弹之多层装饰器
多层装饰器 #首先我们先实现一个简单的登陆与权限验证功能,注意看执行结果 USER_INFO = {} def check_login(func): def inner(*args,**kwargs) ...
- Django---CBV和FBV的使用,CBV的流程,给视图加装饰器,Request对象方法,属性和Response对象,form表单的上传
Django---CBV和FBV的使用,CBV的流程,给视图加装饰器,Request请求对象方法,属性和Response响应对象,form表单的上传 一丶CBV和FBV 在Django中存 ...
- Django:(05)类视图,装饰器和中间件
一.类视图的定义和使用 在Django中还可以通过类来定义一个视图,称为类视图. 定义一个类视图:定义一个类,需继承 Django 提供的 View 类 . from django.views.gen ...
- python 装饰器 第十一步:多层装饰器的嵌套
#第十一步:多层装饰器的嵌套 #装饰器1 def kuozhan1(func): #定义装饰之后的函数 def neweat1(): # 扩展功能1 print('1-----饭前洗手') # 调用基 ...
- django类视图的装饰器验证
django类视图的装饰器验证 django类视图的get和post方法是由View内部调用dispatch方法来分发,最后调用as_view来完成一个视图的流程. 函数视图可以直接使用对应的装饰器 ...
- 10、Flask实战第10天:视图使用装饰器
在实际开发中,我们有时候会用到自己定义装饰器并应用到函数视图或者类视图里面:比如:我们要想进入个人中心页面,首先要验证你是否登录,否则进不去,下面我们来模拟这个场景 定义一个装饰器 from func ...
随机推荐
- Struts2的核心配置文件
Struts2的详细配置: 配置的是struts2的核心配置文件:,在struts2的核心配置文件中主要有三个标签需要进行配置:package,action,result. 1. 配置package标 ...
- 什么是dockerfile?
什么是dockerfile? Dockerfile是一个包含用于组合映像的命令的文本文档.可以使用在命令行中调用任何命令. Docker通过读取Dockerfile中的指令自动生成映像. docker ...
- 使用IP在局域网内访问System.Net.HttpListenerException:“拒绝访问。”
记录一下,自己写的程序之前运行没有遇到这个问题,突然遇到这个问题,找了一圈没有找到有效的解决方案,到最后发现,以管理员身份运行程序即可.简单记录一下. 还有就是 .UseUrls("http ...
- 短篇文档兼职看过来 python 课后作业 assignment project
文档兼职 开题报告 读后感 课后作业 等 代写 编程,Java ,Python,R,等语言的,国内外课程作业指导,写作. 有经验,有作品,成交快,放心! 可联系 QQ 550987425
- Vue.js——60分钟组件快速入门(上篇)二
来源:https://www.cnblogs.com/keepfool/p/5625583.html 组件简介 组件系统是Vue.js其中一个重要的概念,它提供了一种抽象,让我们可以使用独立可复用的小 ...
- 2019-5-28-VisualStudio-扩展开发
title author date CreateTime categories VisualStudio 扩展开发 lindexi 2019-05-28 19:51:49 +0800 2018-2-1 ...
- js中跳出forEach循环
缘由:近期在项目中使用lodash.js中的_.foreach方法处理数据,需要在满足条件时结束循环并不执行后面的js代码. 因为foreach中默认没有break方法.在尝试中使用了return f ...
- intel vtune 介绍、安装和使用
intel vtune 介绍 https://software.intel.com/en-us/vtune intel vtune 安装包下载地址 https://software.intel.com ...
- Windows 进入上帝模式窗口
Win10上帝模式如何启用? 默认情况下,Win10的上帝模式是隐藏的,如果要开启的话,操作步骤也非常简单,下面就介绍两种方法. 方法一.直接运行命令行 1.使用[Win + R ]快捷键打开“运行” ...
- 树——sum-root-to-leaf-numbers(根到叶节点数字之和)
问题: Given a binary tree containing digits from0-9only, each root-to-leaf path could represent a numb ...