查看装饰器 — Flask 文档
查看装饰器
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 中提到的那样设置了一个缓存。
这是一个示例缓存函数。 它根据特定前缀(实际上是格式字符串)和请求的当前路径生成缓存键。 请注意,我们使用的函数首先创建装饰器,然后再装饰该函数。 听起来很可怕? 不幸的是,它有点复杂,但代码应该仍然易于阅读。
装饰后的函数将按如下方式工作
- 根据当前路径获取当前请求的唯一缓存键。
- 从缓存中获取该键的值。 如果缓存返回了一些东西,我们将返回那个值。
- 否则将调用原始函数并将返回值存储在提供的超时(默认为 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
请注意,这里假设实例化的 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 = 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"