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框架视图多层装饰器问题的更多相关文章

  1. flask笔记(三)Flask 添加登陆验证装饰器报错,及解析

    Flask 添加登陆验证装饰器报错,及解析 写这个之前,是想到一个需求,这个是关于之前写Flask笔记(二)中的一个知识点,路由相关 需求为 : 有一些页面必须是登陆之后才能访问的,比如Shoppin ...

  2. python 多层装饰器

    25.多层装饰器:      1.原理:执行顺序从上往下,#2和#3组成一个函数假设为nf1,#1和nf1组成一个函数nnf1           f1成为ck_ty_of_us的inner函数即nf ...

  3. Django中类视图使用装饰器的方式

    类视图使用装饰器 为类视图添加装饰器,可以使用两种方法. 为了理解方便,我们先来定义一个为函数视图准备的装饰器(在设计装饰器时基本都以函数视图作为考虑的被装饰对象),及一个要被装饰的类视图. def ...

  4. python-day5-装饰器第二弹之多层装饰器

    多层装饰器 #首先我们先实现一个简单的登陆与权限验证功能,注意看执行结果 USER_INFO = {} def check_login(func): def inner(*args,**kwargs) ...

  5. Django---CBV和FBV的使用,CBV的流程,给视图加装饰器,Request对象方法,属性和Response对象,form表单的上传

    Django---CBV和FBV的使用,CBV的流程,给视图加装饰器,Request请求对象方法,属性和Response响应对象,form表单的上传 一丶CBV和FBV       在Django中存 ...

  6. Django:(05)类视图,装饰器和中间件

    一.类视图的定义和使用 在Django中还可以通过类来定义一个视图,称为类视图. 定义一个类视图:定义一个类,需继承 Django 提供的 View 类 . from django.views.gen ...

  7. python 装饰器 第十一步:多层装饰器的嵌套

    #第十一步:多层装饰器的嵌套 #装饰器1 def kuozhan1(func): #定义装饰之后的函数 def neweat1(): # 扩展功能1 print('1-----饭前洗手') # 调用基 ...

  8. django类视图的装饰器验证

    django类视图的装饰器验证 django类视图的get和post方法是由View内部调用dispatch方法来分发,最后调用as_view来完成一个视图的流程. 函数视图可以直接使用对应的装饰器 ...

  9. 10、Flask实战第10天:视图使用装饰器

    在实际开发中,我们有时候会用到自己定义装饰器并应用到函数视图或者类视图里面:比如:我们要想进入个人中心页面,首先要验证你是否登录,否则进不去,下面我们来模拟这个场景 定义一个装饰器 from func ...

随机推荐

  1. JavaSE编码试题强化练习2

    1.编写递归算法程序:一列数的规则如下: 0.1.1.2.3.5.8.13.21.34...... 求数列的第40位数是多少. public class TestRecursion { public ...

  2. spring-data-redis的使用/redis缓存

    1.导入依赖 <properties> <junit.version>4.12</junit.version> <spring.version>4.2. ...

  3. 《剑指offer》面试题15 链表中的倒数第k个节点 Java版

    书中方法:用两个节点一次遍历求得倒数第k个节点.注意头节点为空,k<=0,k大于节点个数的情况. public ListNode find(ListNode head, int k){ if(h ...

  4. JVM调优 — 命令大全(jps jstat jmap jhat jstack jinfo)(转)

    运用jvm自带的命令可以方便的在生产监控和打印堆栈的日志信息帮忙我们来定位问题!虽然jvm调优成熟的工具已经有很多:jconsole.大名鼎鼎的VisualVM,IBM的Memory Analyzer ...

  5. webpack搭建vue项目开发环境【文档向学习】

    为何有这篇文章 各个社区已经有无数篇帖子介绍如何使用webpack搭建前端项目,但无论是出于学习webpack的目的还是为了解决工作实际需要都面临着一个现实问题,那就是版本更新.别人的帖子可能刚写好版 ...

  6. BZOJ 1908. Pku2054 UVA1205 Color a Tree

    传送门 看一眼感觉 $dp$,发现状态没法维护 考虑贪心,然后就想了两个错的贪心... 正解好神啊 首先如果权值最大的点能够一步染色那么肯定要染它 意思就是,一旦父节点被染色那么它就要接着被染色 那么 ...

  7. C# DataTable 去重复数据方法

    //获取dt中Id,Value 2个字段不重复的数据 newDt = dt.DefaultView.ToTable(true, "Id","Value" );

  8. the sum of two fixed value

    the sum of two fixed value description Input an array and an integer, fina a pair of number in the a ...

  9. 2018-2-13-win10-uwp-参考

    title author date CreateTime categories win10 uwp 参考 lindexi 2018-2-13 17:23:3 +0800 2018-2-13 17:23 ...

  10. 2019-9-2-visual-studio-2015-warning-MSB3246

    title author date CreateTime categories visual studio 2015 warning MSB3246 lindexi 2019-09-02 12:57: ...