Flask Blueprint Initialization - 初始化一些全局变量

Flask Blueprint Initialization - Initializing some global variables

我是 Flask 的新手,我正准备编写一个更大的应用程序。到目前为止,我已经将功能划分为蓝图。现在我希望能够在初始化期间从蓝图中设置一些全局变量(因此在请求上下文之外)。基本上,我想为某些蓝图自动初始化一个导航列表,所以每个蓝图都需要告诉我的基础应用程序它想要如何命名以及它的默认路由是什么。

我的目标是其他人只需将他们的自定义蓝图放入我应用程序的 "plugin" 文件夹即可扩展我的应用程序。在这种情况下,我的应用程序不知道它们的路由或名称。它需要在加载特定蓝图时自动学习它...

换句话说:我有一个主应用程序,其中包含一些作为蓝图实现的子应用程序。主应用程序应包含一个导航栏,引用所有子应用程序(蓝图)。蓝图如何在主菜单导航变量中注册一些东西(例如在初始化时)?

(我没有找到从蓝图中访问 "self.parent" 或应用程序上下文的方法。蓝图没有类似构造函数的东西吗?)

so each blueprint needs to tell my base application how it want's to be named and what it's default route is.

当您创建蓝图时,您已经在第一个参数中传递了它的名称:

simple_page = Blueprint('simple_page')

您也可以将 url_prefix 值传递给构造函数

simple_page = Blueprint('simple_page', url_prefix='/pages')

I have a main application containing some sub applications implemented as blueprints. The main application should hold a navigation bar referencing to all sub applications (blueprints)

这是一个 python 模块中的示例,您应该将每个蓝图拆分到自己的模块中。

from flask import Flask, Blueprint, render_template

# ADMIN
admin = Blueprint('admin', __name__, url_prefix='/admin')

@admin.route('/')
def admin_index():
    return 'Admin module'

@admin.route('/settings')
def settings():
    return 'Admin Settings'


# USER
user = Blueprint('user', __name__, url_prefix='/user')

@user.route('/')
def user_index():
    return 'User module'

@user.route('/profile')
def profile():
    return 'User Profile'


app = Flask(__name__)
app.register_blueprint(admin)
app.register_blueprint(user)

@app.route('/')
def index():
    return render_template('index.html', blueprints=app.blueprints)

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True, port=7000)

Jinja2 模板。请记住将此文件放在根项目的 templates 文件夹中,这是 flask 默认搜索模板的地方。

<ul>
  {% for bp_name, bp in blueprints.iteritems() %}
    <li><a href="{{ bp.url_prefix }}">{{ bp.name }}</a></li>
  {% endfor %}
</ul>

从 flask 主应用程序 object,在本例中 app,您可以访问已注册的蓝图列表; app.blueprints,这是一个 python 字典,其中包含您在构造函数中传递的蓝图的名称。因此,您将此 object 传递给您的模板并在其上循环以在菜单中呈现名称和 URL。

Furthermore, what if I want to enable blueprint authors to pass more data in a standardized way (e.g. in which color to display the link to his piece, or whatever...)

由于这是蓝图特定数据,我认为一个好的解决方案是扩展蓝图 class 并实现自定义方法以标准化方式保存额外数据,然后从主应用程序访问 object.

custom.py

from flask import Blueprint

class MyBlueprint(Blueprint):

    bp_data = {}

    def set_data(data):
        # here you can make extra task like ensuring if 
        # a minum group of value were provided for instance
        bp_data = data

admin.py

from .custom import MyBlueprint

admin = MyBlueprint('admin', __name__, url_prefix='/admin')
admin.set_data({'color': '#a569bd', 'enabled': true})

# all the blueprint's routes
...

app.py

from admin import admin

app = Flask(__name__)
app.register_blueprint(admin)

@app.route('/')
def index():
    for a, b in app.blueprints:
        print b.bp_data['color']
        print b.bp_data['enabled']
    ...

当然,我的自定义蓝图 class 需要做更多的工作,比如验证它传递的数据类型,或者如果没有所需的值则抛出错误,比如; 标题、require_auth 等。从这一点开始,您必须定义蓝图必须提供给主应用程序才能正常工作的最低要求数据是什么。

这里给大家介绍一个我常用的可扩展应用模式。事实证明可以正常工作。

目录结构:

YourApp
|- plugins (will contain all the user's plugins)
     |- __init__.py (empty)
     |-plugin1
          |- __init__.py (empty)
          |- loader.py
|- app.py

app.py

from flask import Flask
import os

def plugin_loader(app):
  """ Function for discover new potencial plugins.
  After checking the validity (it means, check if it implements
  'register_as_plugin', load the user defined blueprint"""

  plugins = [x for x in os.listdir('./plugins')
             if os.path.isdir('./plugins/' + x)]
  for plugin in plugins:
    # TODO: Add checking for validation
    module = __import__('plugins.' + str(plugin) + '.loader', fromlist=['register_as_plugin'])

    app = module.register_as_plugin(app)

  return app

# Creates the flask app
app = Flask(__name__)
# Load the plugins as blueprints
app = plugin_loader(app)

print(app.url_map)


@app.route('/')
def root():
  return "Web root!"

app.run()

plugins/plugin1/loader.py

from flask import Blueprint


plugin1 = Blueprint('plugin1', __name__)

@plugin1.route('/version')
def version():
  return "Plugin 1"

def register_as_plugin(app):
  app.register_blueprint(plugin1, url_prefix='/plugin1')

  return app

因此期货插件应该在 loader.py 文件中实现 register_as_plugin 函数,在它们的 plugins/ 目录中。

有关目录发现的更多信息,您可以阅读 https://docs.python.org/2/library/os.path.html。 有关模块动态导入的更多信息,您可以阅读 https://docs.python.org/2/library/functions.html#import.

已通过 Python 2.7.6、Flask 0.10.1 和 Unix 平台证明。