延迟加载视图

Flask 通常与装饰器一起使用。装饰器简单易用,URL 紧挨着为该特定 URL 调用的函数。但是这种方法有一个缺点:这意味着使用装饰器的所有代码都必须预先导入,否则 Flask 永远无法找到你的函数。

如果你的应用程序必须快速导入,这可能是一个问题。它可能必须在 Google 的 App Engine 或其他系统等系统上执行此操作。因此,如果你突然发现你的应用程序超出了此方法的范围,你可以退回到集中式 URL 映射。

启用集中式 URL 映射的系统是 add_url_rule() 函数。你无需使用装饰器,只需使用一个文件即可使用所有 URL 设置应用程序。

转换为集中式 URL 映射

想象一下当前应用程序看起来像这样

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() 的函数,可以进一步优化所需键击数量,方法是在项目名称和点之前加上一个字符串,并根据需要将 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)

需要注意的一点是,请求处理程序之前和之后必须位于一个文件中,该文件在第一次请求时预先导入才能正常工作。任何类型的剩余装饰器也是如此。