查看装饰器 — Flask 文档

来自菜鸟教程
Flask/docs/2.0.x/patterns/viewdecorators
跳转至:导航、​搜索

查看装饰器

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

需要登录的装饰器

所以让我们实现一个这样的装饰器。 装饰器是包装和替换另一个函数的函数。 由于原来的函数被替换了,所以需要记住把原来的函数的信息复制到新的函数中。 使用 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

要使用装饰器,请将其作为最里面的装饰器应用于视图函数。 在应用更多装饰器时,请始终记住 route() 装饰器是最外层的。

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

笔记

GET 请求登录页面后,next 值将存在于 request.args 中。 从登录表单发送 POST 请求时,您必须传递它。 您可以使用隐藏的输入标签执行此操作,然后在用户登录时从 request.form 中检索它。

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

缓存装饰器

想象一下,您有一个执行昂贵计算的视图函数,因此您希望将生成的结果缓存一段时间。 一个装饰器会很好。 我们假设您已经像 Caching 中提到的那样设置了一个缓存。

这是一个示例缓存函数。 它根据特定前缀(实际上是格式字符串)和请求的当前路径生成缓存键。 请注意,我们使用的函数首先创建装饰器,然后再装饰该函数。 听起来很可怕? 不幸的是,它有点复杂,但代码应该仍然易于阅读。

装饰后的函数将按如下方式工作

  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 对象可用,请参阅 Caching


模板装饰器

TurboGears 的家伙们不久前发明的一种常见模式是模板装饰器。 该装饰器的想法是您返回一个字典,其中包含从视图函数传递给模板的值,并且模板会自动呈现。 因此,以下三个示例完全相同:

@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 路由系统以获得更大的灵活性时,您需要将 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"