background

Plotly Dash is a Python web application framework that helps us quickly build beautiful, responsive, interactive, data visualization pages. Unfortunately, an app that can only display data is not always very useful. If we need a complete web application, we have to figure out a way to use its backend Flask .

question

Although Dash borrows the Flask shell, this Flask runs in a sandbox and has a lot less functionality than normal Flask. For example, the following functions do not need to be upgraded to the Dash enterprise version.

  • Database integration
  • Certification
  • Multi-page, multi-route support
  • custom style
    etc

design

To overcome all of the above problems, instead of using Dash's own Flask, we can create a basic Flask app and put our Dash app on top of it.

in principle

  • -- Ability to create any type of application with Dash and Flask.
  • Full-featured -- Both Dash and Flask must be full-featured, with no functional limitations.
  • Customizable -- Ability to customize the styles applied.
  • Concise -- Create a Dash application quickly and easily.

solution

In general, we can achieve our goal in two ways.

  • __Sub-application__: Create a basic Flask application, use this Flask as the parent server to initialize the Dash application, and register the Dash application as a sub-application with a custom route.
  • __iframe__: Create a basic Flask application, put the Dash application in a iframe , and use Flask to load these iframe .

which is better

Compared with placing the Dash application in iframe , although this method looks the simplest, it will introduce other problems because of the completely isolated characteristics of iframe :

  • __Difficult to customize__: iframe cannot change the application in iframe through css/js.
  • iframe : Because 0622b64043692c has a different routing system from the main frame, clicking a link inside iframe will not trigger the main frame to jump.
  • __Cannot extend__: iframe scheme does not support multi-page Dash applications.

For the above reasons, we believe that using sub-applications is a more flexible and general solution.

code structure

The code structure of the basic Flask is as follows:

├── 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

For the complete demo implementation, please refer to github .

implementation details

In order to implement the sub-application, we need to implement the following key functions.

  • Create a blueprint of the basic application
  • Register the blueprint of the base app in Flask
  • Associate Dash with the base application and define routes and layouts

Create a blueprint of the basic application:

Blueprint is a component that Flask uses to implement modular applications.

app/views.py

from flask import Blueprint, render_template

base_app = Blueprint("base_app", __name__)

@base_app.route("/")
def index():
    """Landing page."""
    return render_template("index.html", top_menu_items=get_top_menu_items("/"))

Register the blueprint of the base app in Flask

In Flask, this is called the application factory mode, creating a create_app function that returns the application object. Flask will call this function to process the request.

app/__init__.py

from flask import Flask

from app.views import base_app

def create_app(test_config=None):
    """Create and configure Flask app"""

    app = Flask(__name__)

    app.register_blueprint(base_app)

    return app

Associate Dash with the base application and define routes and layouts

Create a Dash application that uses the base application.

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 page
URL_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

How to add more Dash apps to the base app

Adding Dash to the base application requires a total of 2 steps.

Step 1: Create a Dash application

1-1: Create a .py file in the app/dash_apps directory.

1-2: Create a Dash application according to the following code structure.

from app.dash_apps import create_dash_app

# endpoint of this page
URL_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

Step 2: Add main page navigation menu for Dash application at app/views.py (optional)

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

how to run

Execute the following script to start the application, if FLASK_ENV=development is set, the application will run in development mode.

#!/bin/bash

source .venv/bin/activate

if [[ "${FLASK_ENV}" == "development" ]]; then
    flask run --host=0.0.0.0 --port 8050
else
    gunicorn wsgi:app \
        --bind 0.0.0.0:8050 \
        --log-level debug \
        --workers 2 \
        --threads 4
fi

BetaRabbit
1.4k 声望22 粉丝

全栈,猫奴