惰性载入视图

Flask 通常使用装饰器。装饰器简单易用,只要把 URL 放在相应的函数的前面就 可以了。但是这种方式有一个缺点:使用装饰器的代码必须预先导入,否则 Flask 就无法真正找到你的函数。

当你必须快速导入应用时,这就会成为一个问题。在 Google App Engine 或其他 系统中,必须快速导入应用。因此,如果你的应用存在这个问题,那么必须使用 集中 URL 映射。

add_url_rule() 函数用于集中 URL 映射,与使用装饰器不 同的是你需要一个设置应用所有 URL 的专门文件。

转换为集中 URL 映射

假设有如下应用:

  1. from flask import Flask
  2. app = Flask(__name__)
  3. @app.route('/')
  4. def index():
  5. pass
  6. @app.route('/user/<username>')
  7. def user(username):
  8. pass

为了集中映射,我们创建一个不使用装饰器的文件( views.py ):

  1. def index():
  2. pass
  3. def user(username):
  4. pass

在另一个文件中集中映射函数与 URL:

  1. from flask import Flask
  2. from yourapplication import views
  3. app = Flask(__name__)
  4. app.add_url_rule('/', view_func=views.index)
  5. app.add_url_rule('/user/<username>', view_func=views.user)

延迟载入

至此,我们只是把视图与路由分离,但是模块还是预先载入了。理想的方式是按 需载入视图。下面我们使用一个类似函数的辅助类来实现按需载入:

  1. from werkzeug.utils import import_string, cached_property
  2. class LazyView(object):
  3. def __init__(self, import_name):
  4. self.__module__, self.__name__ = import_name.rsplit('.', 1)
  5. self.import_name = import_name
  6. @cached_property
  7. def view(self):
  8. return import_string(self.import_name)
  9. def __call__(self, *args, **kwargs):
  10. return self.view(*args, **kwargs)

上例中最重要的是正确设置 __module__ 和 __name__ ,它被用于在不提供 URL 规则的情况下正确命名 URL 规则。

然后可以这样集中定义 URL 规则:

  1. from flask import Flask
  2. from yourapplication.helpers import LazyView
  3. app = Flask(__name__)
  4. app.add_url_rule('/',
  5. view_func=LazyView('yourapplication.views.index'))
  6. app.add_url_rule('/user/<username>',
  7. view_func=LazyView('yourapplication.views.user'))

还可以进一步优化代码:写一个函数调用 add_url_rule() , 加上应用前缀和点符号。:

  1. def url(import_name, url_rules=[], **options):
  2. view = LazyView(f"yourapplication.{import_name}")
  3. for url_rule in url_rules:
  4. app.add_url_rule(url_rule, view_func=view, **options)
  5. # add a single route to the index view
  6. url('views.index', ['/'])
  7. # add two routes to a single function endpoint
  8. url_rules = ['/user/','/user/<username>']
  9. url('views.user', url_rules)

有一件事情要牢记:请求前和请求后处理器必须在第一个请求前导入。

其余的装饰器可以同样用上述方法改写。