视图装饰

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', '') }}"/>

缓存装饰

试想你有一个运算量很大的函数,而且您希望能够将生成的结果在一段时间内 缓存起来,一个装饰器将会非常适合用于干这种事。 装饰师会很好的。 我们假定您已经参考 缓存 中提到的内容配置好了缓存功能。

这里是一个示例缓存函数。 这里有一个用作例子的缓存函数,它从一个指定的前缀(通常是一个格式化字符串) 和当前请求的路径生成一个缓存键。 请注意我们创建了一个这样的函数: 它先创建 一个装饰器,然后用这个装饰器包装目标函数。 听起来很复杂? 不幸的是,这的确 有些难,但是代码看起来会非常直接明了。

被装饰器包装的函数将能做到如下几点:

  1. 以当前请求和路径为基础生成缓存时使用的键。
  2. 从缓存中取出对应键的值,如果缓存返回的不是空,我们就将它返回回去。 如果非字典类型的对象被返回,函数将照原样 将那个对象再次返回。
  3. 如果缓存中没有这个键,那么最初的函数将会被执行,并且返回的值在指定时间 (默认5分钟内)被缓存起来。

代码如下:

from functools import wraps
from flask import request

def cached(timeout=5 * 60, key='view/%s'):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            cache_key = key % 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

请注意,这假设实例化的缓存对象可用,有关详细信息,请参见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 = 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"