视图装饰器

Python有一个非常有趣的特性,叫做函数修饰器。这就为Web应用程序提供了一些非常好的功能。因为flask中的每个视图都是一个函数,所以可以使用decorator向一个或多个函数注入额外的功能。这个 route() 装饰师是你可能已经用过的那个。但是有一些用例可以实现您自己的装饰器。例如,假设您有一个视图,该视图只能由登录的人使用。如果用户进入该站点但未登录,则应将其重定向到登录页面。这是一个很好的用例示例,其中装饰器是一个很好的解决方案。

需要登录的装饰器

所以让我们实现这样一个装饰器。decorator是包装和替换另一个函数的函数。由于替换了原始函数,因此需要记住将原始函数的信息复制到新函数。使用 functools.wraps() 为你处理这件事。

这个例子假设登录页面被称为``'login'并且当前用户存储在``g.user``中并且如果没有登录则是``None。:

from functools import wraps
from flask import g, request, redirect, url_for

def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if g.user is None:
            return redirect(url_for('login', next=request.url))
        return f(*args, **kwargs)
    return decorated_function

要使用装饰器,请将其作为最里面的装饰器应用于视图功能。 当应用更多装饰器时,请记住:meth:`~flask.Flask.route`装饰器是最外面的。:

@app.route('/secret_page')
@login_required
def secret_page():
    pass

备注

这个 next 值将存在于 request.args 后一 GET 请求登录页面。你必须在发送 POST 从登录表单请求。您可以使用隐藏的输入标记执行此操作,然后从 request.form 当用户登录时。::

<input type="hidden" value="{{ request.args.get('next', '') }}"/>

缓存装饰器

假设您有一个执行昂贵计算的视图函数,因此您希望将生成的结果缓存一定时间。装饰师会很乐意的。我们假设您设置了一个缓存,如中所述 高速缓存 .

下面是一个缓存函数示例。它从特定前缀(实际上是格式字符串)和请求的当前路径生成缓存键。请注意,我们使用的函数首先创建装饰器,然后装饰函数。听起来糟透了?不幸的是,它有点复杂,但代码仍然应该是直接读取的。

然后,装饰功能将按以下方式工作

  1. 根据当前路径获取当前请求的唯一缓存密钥。

  2. 从缓存中获取该键的值。如果缓存返回了某些内容,我们将返回该值。

  3. 否则,将调用原始函数,并在提供的超时时间(默认为5分钟)将返回值存储在缓存中。

这里的代码:

from functools import wraps
from flask import request

def cached(timeout=5 * 60, key='view/{}'):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            cache_key = key.format(request.path)
            rv = cache.get(cache_key)
            if rv is not None:
                return rv
            rv = f(*args, **kwargs)
            cache.set(cache_key, rv, timeout=timeout)
            return rv
        return decorated_function
    return decorator

注意,这假定一个 cache 对象可用,请参见 高速缓存 .

模板装饰

不久前,TurboGears公司的人发明了一种常见的图案,那就是模板装饰。这个decorator的思想是返回一个字典,其中包含从view函数传递给模板的值,模板将自动呈现。有了这个,下面三个例子做得完全相同:

@app.route('/')
def index():
    return render_template('index.html', value=42)

@app.route('/')
@templated('index.html')
def index():
    return dict(value=42)

@app.route('/')
@templated()
def index():
    return dict(value=42)

如您所见,如果没有提供模板名,它将使用URL映射的端点,并将点转换为斜线。+ '.html' . 否则将使用提供的模板名称。当修饰函数返回时,返回的字典将传递给模板呈现函数。如果 None 如果返回的不是字典,则假定为空字典,如果返回的不是字典,则从函数中返回的字典不变。这样,您仍然可以使用重定向函数或返回简单字符串。

这是那个装饰师的代码:

from functools import wraps
from flask import request, render_template

def templated(template=None):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            template_name = template
            if template_name is None:
                template_name = f"{request.endpoint.replace('.', '/')}.html"
            ctx = f(*args, **kwargs)
            if ctx is None:
                ctx = {}
            elif not isinstance(ctx, dict):
                return ctx
            return render_template(template_name, **ctx)
        return decorated_function
    return decorator

终结点装饰器

当您想要使用werkzeug路由系统以获得更大的灵活性时,您需要将:class:`~werkzeug.routing.Rule`中定义的端点映射到视图函数。 这个装饰器可以做到这一点。 例如::

from flask import Flask
from werkzeug.routing import Rule

app = Flask(__name__)
app.url_map.add(Rule('/', endpoint='index'))

@app.endpoint('index')
def my_index():
    return "Hello world"