Flask 路由的对应关系之mapping is overwriting

在Flask中,路由通过@app.route()装饰器来生成对应关系,此时如果我们再在这个装饰器的基础上再加装饰器,可能会出现如下的错误,这里对这个错误做一点解读,为什么会出现这种情况:

Traceback (most recent call last):
  File "code.py", line 4, in <module>
    app = Flask(__name__, template_foler='templates')
TypeError: __init__() got an unexpected keyword argument 'template_foler'
(fla) root@Andy963:/mnt/c/Users/lg/Desktop# python code.py
Traceback (most recent call last):
  File "code.py", line 21, in <module>
    @auth
  File "/root/env/fla/lib/python3.6/site-packages/flask/app.py", line 1314, in decorator
    self.add_url_rule(rule, endpoint, f, **options)
  File "/root/env/fla/lib/python3.6/site-packages/flask/app.py", line 98, in wrapper_func
    return f(self, *args, **kwargs)
  File "/root/env/fla/lib/python3.6/site-packages/flask/app.py", line 1283, in add_url_rule
    "existing endpoint function: %s" % endpoint
AssertionError: View function mapping is overwriting an existing endpoint function: inner

意思是endpoint重复了,endpoint在flask中是别名,类似于django中的name

我们先看下装饰器的一个特性,可以参考我的另一文章:http://www.jingang.ga/article/47/

def auth(func):
    def inner(*args, **kwargs):
        return func(*args, **kwargs)
    return inner

@auth
def login():
    pass


@auth
def index():
    pass


print(login.__name__)
print(index.__name__)

输出:

inner
inner

在没有使用functools的情况下,被装饰的函数的__name__都变成了inner.

我们再加上flask中的装饰器:

@app.route('/login')
@auth
def login():
    pass

@app.route('/index')
@auth
def index():
    pass

这样,传给@app.route()的函数名都是Inner,那我们看看router的源码:

   def route(self, rule, **options):
        """Like :meth:`Flask.route` but for a blueprint.  The endpoint for the
        :func:`url_for` function is prefixed with the name of the blueprint.
        """

        def decorator(f):
            endpoint = options.pop("endpoint", f.__name__)
            self.add_url_rule(rule, endpoint, f, **options)
            return f

默认情况下endpoint(别名)即为函数名,得到endpoint之后,添加url与endpoint的对应关系。

比如:

‘/login' -------> login

'/index' ------> index

在没有使用functools的情况下,login,index函数传入的都是inner函数,就形成了下面的情况:

‘/login' -------> inner

'/index' ------> inner

我们通过 print(app.url_map)查看下:分开执行,分别 得到如下 结果 :

Map([<Rule '/login' (HEAD, OPTIONS, GET) -> inner>,
 <Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>])

 Map([<Rule '/index' (HEAD, OPTIONS, GET) -> inner>,
 <Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>])

 

这导致了文章开头的那个错误,解决办法是在写装饰器时使用functools,即:

def auth(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        return func(*args, **kwargs)
    return inner

因为使用functools时,__name__属性都是函数名本身,而不会变成inner.

上一篇:Flask 基础

下一篇:Flask 蓝图