/flask-dash-app

Embed Plotly Dash into your Flask applications. Docker-based Flask project wrapping Plotly's Dash and allowing to include multiple Dash sub-apps in a single Flask app.

Primary LanguagePythonMIT LicenseMIT

Flask Dash App

This is a demo application that can serve as a starter project template.

Application Outline

It allows you to

  • serve several individual Dash apps from a single Flask application,
  • add core Flask logic, such as authentication with Flask-Login, generating assets with Flask-Assets, sending email with Flask-Mail, etc.,
  • integrate individual Dash apps in a global layout defined in Jinja templates.

Overview

What's included

Docker base image: https://github.com/tiangolo/uwsgi-nginx-flask-docker

Getting started

git clone https://github.com/tzelleke/flask-dash-app.git
cd flask-dash-app
cp .env.example .env
docker-compose up -d

Integrate your Dash app

Let us go through the steps to integrate a simple demo Dash app. Here is the source code for a rudimentary Dash app.

import dash
import dash_html_components as html

app = dash.Dash(__name__)

app.layout = html.Div(children=[html.H1(children="Hello Dash"), ])

if __name__ == "__main__":
    app.run_server(debug=True)

Save it as demo.py in the app/dash/ folder.

Change the code in demo.py to:

from .dash import Dash
import dash_html_components as html

app_layout = html.Div(children=[html.H1(children="Hello Dash"), ])


def init_dash(server):
    dash_app = Dash(server=server, routes_pathname_prefix="/demo/", )
    dash_app.layout = app_layout
    return dash_app.server


if __name__ == "__main__":
    app = Dash(__name__)
    app.run_server(debug=True)

Register the Dash app with the main Flask application in app/main.py.

...
with app.app_context():
    from .dash import demo

    app = demo.init_dash(app)
...

Add the new route to the global navbar in app/templates/partials/navbar.html

...
<a class="dropdown-item" href="/demo/">Simple Demo</a>
...

Development

Manage python dependencies (using Poetry)

docker-compose run web poetry update # ... or any other poetry command

Global layout: integration of individual dash apps

This starter integrates individual dash apps into a global layout provided by Flask/Jinja templates. This is achieved by overriding the interpolate_index method from the core Dash class.

# app/dash/dash.py
import dash
from markupsafe import Markup
from flask import render_template


class Dash(dash.Dash):
    def interpolate_index(
        self,
        metas="",
        title="",
        css="",
        config="",
        scripts="",
        app_entry="",
        favicon="",
        renderer="",
    ):
        return render_template(
            "dash.html",
            metas=Markup(metas),
            css=Markup(css),
            dash_config=Markup(config),
            scripts=Markup(scripts),
            app_entry=Markup(app_entry),
            renderer=Markup(renderer),
        )

Note: markupsafe.Markup is used to prevent Jinja from escaping the Dash-rendered markup.
Note: config is mapped to dash_config to avoid shadowing the global Flask config in the Jinja environment.

The corresponding Jinja template in app/templates/dash.html extends the base template and injects the provided markup via Jinja template blocks.

{# app/templates/dash.html #}
{% extends 'base.html' %}
{% block meta %}
  {{ super() }}
  {{ metas }}
{% endblock %}
{% block styles %}
  {{ super() }}
  {{ css }}
{% endblock %}
{% block content %}
  {{ app_entry }}
{% endblock %}
{% block scripts %}
  {{ super() }}
  {{ dash_config }}
  {{ scripts }}
  {{ renderer }}
{% endblock %}

Credits

License

See the LICENSE file for license rights and limitations (MIT).