延缓加载的视图¶
Flask 通常与装饰器一起使用。装饰器很简单,URL 就在该 URL 的视图函数旁边。然而,这种方法有一个缺点:这意味着所有使用装饰器的代码都必须预先导入,否则 Flask 根本找不到这些函数。
如果你的应用需要非常快地导入(例如在 Google App Engine 或其他类似平台上), 这种装饰器方式可能会成为瓶颈。 当你的应用变复杂、超出这种用法时,可以改用集中式的 URL 映射方式。
实现集中式 URL 映射的方式是使用 add_url_rule() 方法。 与使用装饰器不同,这种方式通过一个单独的文件来配置所有的 URL 与视图函数之间的映射。
转换到集中式 URL Map¶
设想你当前的应用看起来是这样的:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
pass
@app.route('/user/<username>')
def user(username):
pass
使用集中式方式后,你可以将视图函数写在一个文件中(如 views.py),但不加装饰器:
def index():
pass
def user(username):
pass
然后使用另一个文件来创建应用对象,并将视图函数与 URL 映射:
from flask import Flask
from yourapplication import views
app = Flask(__name__)
app.add_url_rule('/', view_func=views.index)
app.add_url_rule('/user/<username>', view_func=views.user)
延迟加载¶
目前为止,我们只是将视图函数与路由拆分了,但视图模块仍然在启动时被加载。 我们可以进一步优化,只在首次请求时再按需加载视图函数。 为此,我们可以定义一个行为类似函数的类,它在首次调用时动态导入真正的函数:
from werkzeug.utils import import_string, cached_property
class LazyView(object):
def __init__(self, import_name):
self.__module__, self.__name__ = import_name.rsplit('.', 1)
self.import_name = import_name
@cached_property
def view(self):
return import_string(self.import_name)
def __call__(self, *args, **kwargs):
return self.view(*args, **kwargs)
关键点在于你需要正确设置 __module__ 和 __name__, Flask 内部会使用这两个属性来为 URL 规则命名(如果你没有手动指定名称的话)。
接下来,你可以在集中式文件中像下面这样组合视图:
from flask import Flask
from yourapplication.helpers import LazyView
app = Flask(__name__)
app.add_url_rule('/',
view_func=LazyView('yourapplication.views.index'))
app.add_url_rule('/user/<username>',
view_func=LazyView('yourapplication.views.user'))
你还可以进一步简化代码,比如写一个辅助函数, 自动调用 add_url_rule(), 为 rule name 添加项目名前缀,同时根据需要自动将 view_func 包装为 LazyView。:
def url(import_name, url_rules=[], **options):
view = LazyView(f"yourapplication.{import_name}")
for url_rule in url_rules:
app.add_url_rule(url_rule, view_func=view, **options)
# add a single route to the index view
url('views.index', ['/'])
# add two routes to a single function endpoint
url_rules = ['/user/','/user/<username>']
url('views.user', url_rules)
需要注意的是,before request 和 after request 处理器必须写在启动时就被导入的文件中, 才能在第一次请求时正确生效。任何其他装饰器也是一样。