视图装饰器¶
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', '') }}"/>
缓存装饰器¶
假设您有一个视图函数执行昂贵的计算,因此您希望将生成的结果缓存一段时间。装饰器对此很有用。我们假设您已经设置了缓存,如 缓存 中所述。
这是一个缓存函数示例。它从特定前缀(实际上是一个格式字符串)和请求的当前路径生成缓存键。请注意,我们正在使用一个函数,该函数首先创建装饰器,然后装饰函数。听起来很糟糕?不幸的是,它稍微复杂一些,但代码仍然应该很容易阅读。
然后,装饰后的函数将按如下方式工作
根据当前路径获取当前请求的唯一缓存键。
从缓存中获取该键的值。如果缓存返回了某些内容,我们将返回该值。
否则,将调用原始函数,并将返回值存储在缓存中以获取提供的超时(默认情况下为 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 团队在一段时间前发明了一种常见的模式,即模板装饰器。该装饰器的思想是您返回一个字典,其中包含从视图函数传递到模板的值,并且模板会自动呈现。因此,以下三个示例完全相同
@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"