背景

Plotly Dash 是一个Python web利用框架,它能帮忙咱们疾速建设难看的,响应式的,可交互的,数据可视化页面。惋惜的是,一个只能展现数据的利用并不总是非常有用。如果咱们须要一个残缺的web利用,那就不得不想方法利用它的后端 Flask.

问题

只管Dash借了Flask的壳,但这个Flask运行在sandbox里,而且和一般的Flask相比也少了很多性能。比方以下的性能要不没有要不须要降级到Dash enterprise版本。

  • 数据库集成
  • 认证
  • 多页面、多路由反对
  • 自定义格调
    等等

设计

为了克服以上的所有问题,与其利用Dash自带的Flask,咱们齐全能够创立一个根底的Flask利用,而后将咱们的Dash利用置于其上。

准则

  • 灵便 -- 可能用Dash和Flask创立任意类型的利用。
  • 全功能 -- Dash和Flask都必须是全功能的,不能有性能上的限度。
  • 可定制 -- 可能自定义利用的款式。
  • 简洁 -- 可能简略快捷的创立一个Dash利用。

解决方案

总的来说,咱们能够有两种形式实现目标。

  • __子利用__: 创立一个根底的Flask利用,用这个Flask作为parent server初始化Dash利用,将Dash利用用自定义路由注册为子利用。
  • __iframe__: 创立一个根底的Flask利用,将Dash利用放在一个iframe中,再用Flask去加载这些iframe.

哪个更好

和将Dash利用放在iframe中相比,只管这种形式看起来最简略,然而因为iframe齐全隔离的个性,反而会引入其余问题:

  • __难于定制__: iframe不能通过css/js扭转iframe内的利用。
  • __不统一__: iframe因为和main frame有着不同的路由零碎,点击一个iframe外部的链接并不会触发main frame的跳转。
  • __无奈扩大__: iframe计划不反对多页面的Dash利用。

基于以上理由,咱们认为应用子利用是一个更灵便且更普适的计划。

代码构造

根底Flask的代码构造如下:

├── app│   ├── dash_apps               -- 所有的Dash利用都在这个目录│   │   ├── custom_dash_app.py│   │   ├── experiment_detail_dash_app.py│   │   ├── experiment_list_dash_app.py│   │   └── __init__.py│   ├── __init__.py│   ├── static│   │   └── styles.css  -- Custom CSS styles│   ├── templates│   │   ├── dash_layout.html    -- Dash利用的layout│   │   ├── header.html         -- Header│   │   ├── index.html          -- 主页面│   │   └── layout.html         -- 主页面的layout│   └── views.py                -- Flask路由和主页面导航菜单定义├── config.py├── poetry.lock├── poetry.toml├── pyproject.toml├── README.md├── scripts│   ├── fix.sh│   ├── run.sh│   └── setup.sh├── setup.cfg└── wsgi.py

残缺demo实现请参考github。

实现细节

为了实现子利用,咱们须要实现如下几个要害性能。

  • 创立根底利用的blueprint
  • 在Flask中注册根底利用的blueprint
  • 将Dash和根底利用关联起来,并定义路由和layout

创立根底利用的blueprint:

Blueprint是Flask用来实现模块化利用的组件。

app/views.py

from flask import Blueprint, render_templatebase_app = Blueprint("base_app", __name__)@base_app.route("/")def index():    """Landing page."""    return render_template("index.html", top_menu_items=get_top_menu_items("/"))

在Flask中注册根底利用的blueprint

在Flask中,这叫作application factory模式,创立一个create_app函数,返回application对象。Flask会调用这个函数来解决申请。

app/__init__.py

from flask import Flaskfrom app.views import base_appdef create_app(test_config=None):    """Create and configure Flask app"""    app = Flask(__name__)    app.register_blueprint(base_app)    return app

将Dash和根底利用关联起来,并定义路由和layout

创立一个应用根底利用的Dash利用。

app/dash_apps/__init__.py

def customize_index_string(app, url):    """Custom app's index string"""    app.index_string = env.get_template("dash_layout.html").render(        top_menu_items=get_top_menu_items(url)    )def add_route(app, url):    """Add route to the app"""    app.server.add_url_rule(url, endpoint=url, view_func=app.index)    app.routes.append(url)def create_dash_app(server, url_rule, url_base_pathname):    """Create a Dash app with customized index layout    :param server: base Flask app    :param url_rule: url rule as endpoint in base Flask app    :param url_base_pathname: url base pathname used as dash internal route prefix    """    app = dash.Dash(name=__name__, server=server, url_base_pathname=url_base_pathname)    customize_index_string(app, url_rule)    add_route(app, url_rule)    return app

app/dash_apps/custom_dash_app.py

from app.dash_apps import create_dash_app# endpoint of this pageURL_RULE = "/custom-app"# dash internal route prefix, must be start and end with "/"URL_BASE_PATHNAME = "/dash/custom-app/"def create_dash(server):    """Create a Dash view"""    app = create_dash_app(server, URL_RULE, URL_BASE_PATHNAME)    # dash app definitions goes here    ...    return app.server

如何在根底利用里增加更多Dash利用

在根底利用里增加Dash总共须要2步。

第一步:创立Dash利用

1-1: 在app/dash_apps目录创立一个.py文件。

1-2: 依照以下代码构造创立Dash利用。

from app.dash_apps import create_dash_app# endpoint of this pageURL_RULE = "/custom-app"# dash internal route prefix, must be start and end with "/"URL_BASE_PATHNAME = "/dash/custom-app/"def create_dash(server):    """Create a Dash view"""    app = create_dash_app(server, URL_RULE, URL_BASE_PATHNAME)    # dash app definitions goes here, same as what you would do in normal Dash application    ...    return app.server

第二步: 在app/views.py为Dash利用增加主页面导航菜单(可选)

top_menus = [    {"path": "/", "title": "Home"},    {"path": "/experiments", "title": "Experiments"},    {"path": "/custom-app", "title": "Custom App"},    ...]

如何运行

执行以下脚本启动利用,如果设置了FLASK_ENV=development,利用会以开发模式运行。

#!/bin/bashsource .venv/bin/activateif [[ "${FLASK_ENV}" == "development" ]]; then    flask run --host=0.0.0.0 --port 8050else    gunicorn wsgi:app \        --bind 0.0.0.0:8050 \        --log-level debug \        --workers 2 \        --threads 4fi