Multiple Dashboards?

Is there functionality, plan to add one, or a hack to enable multiple dashboards in the same server? Right now I’m getting this in Spyre, trying to replicate in Dash

I think that the way Dash handles multiple “pages” gets you to the multiple dashboard outcome.
See this discussion:

1 Like

With latest (master) version of dash, you can build a multi-app project!

Structure

dash-project/
  app1/
     app.py
     datamodel.py
  app2/
     app.py
     datamodel.py
  mycomponents/
     ...
  server.py
  run.py

app1/app.py:

import dash
import app1.datamodel
..
from server import server

app = dash.Dash(name='app1', sharing=True, 
                server=server, url_base_pathname='/app1')

server.py

from flask import Flask
server = Flask(__name__)

run.py

from server import server as application

import app1.app
import app2.app    

Serve using uwsgi:

uwsgi --http 0.0.0.0:5000 --processes 4 --wsgi-file run.py

(updated to work with uwsgi, so you get proper multi-process management)

5 Likes

I would like to run multiple instances of the same app side by side on the same computer. I am able to pass the port number to app.run_server() so each instance uses a different port. However, when I run two instances, I get this error:

WARNING in flask_seasurf [D:\Users\212448403\AppData\Local\Continuum\Anaconda32_new\lib\site-package
s\flask_seasurf.py:282]:
Forbidden (CSRF token missing or incorrect.): /_dash-update-component

What is going wrong? Some authentication issue?

Did you put ‘csrf_protect=False’ in your app?

Like this

app = dash.Dash(‘My app’, server=server, url_base_pathname=’/’, csrf_protect=False)

That worked! Thanks!

Note that you can also create a multi-page Dash app using links and the new dash_core_components.Location component. I just wrote a tutorial on this here: https://plot.ly/dash/urls. This is what the multi-page Dash user guide (https://plot.ly/dash, github.com/plotly/dash-docs) uses.

2 Likes

This would be better than setting the csrf_protect flag to False, probably.

I was really looking forward to this, awesome!

1 Like

so for these multiple dashboards we don’t need to break up the code say what the person did above ie i mean your tutorial both pages are in the same module script can it be separated into say two module scripts and imported into a third script ?

ie can we design the code the following way:
have page 1 dropdown in one .py script called drop.py
have page 2 radio in a second ,py script called radio.py

have a run script that can run both ie run.py runs both dashboards
this way its cleaner code and not overlapping

i attempted to split the example into two separate .py scripts and call it but I failed to do so hence I am asking the question the idea Vlad posted doesn’t work for me when I attempted to split the urls example posted in the docs

@Vlad This is great - thanks for sharing it. I’m assuming your app var in app2/app.py is something like this:

import dash
import app2.datamodel

from server import server

app = dash.Dash(name='app2', sharing=True, 
            server=server, url_base_pathname='/app2')

i’m getting AssertionError: View function mapping is overwriting an existing endpoint function: serve_layout even though both my apps use unique names. Any pointers?

Thanks a lot

Will

yeah, you need to use version in github, (not available in pip install dash yet).
pip install -e git+git://github.com/plotly/dash#egg=dash

At the moment, I am trying to make a setup using UWSGI emperor mode:
http://uwsgi-docs.readthedocs.io/en/latest/Emperor.html
Stay tuned…

Oh, man. That’s a lot of hours I’ve wasted. Thanks! Will look forward to it reaching pip at some point …

Still struggling to get your version above working @Vlad. I’m wondering if my desired setup will even be possible:

url.com/apps/app1
url.com/apps/app2
url.com/apps/category/app3
url.com/apps/category/app4

We have an existing Flask app running (url.com/flask). I think it’s using uwsgi and nginx. So perhaps we could use that as the ‘runner’, with a basic template pointing to url.com/flask/app1 etc.

I can’t be the only one needing this though! Thoughts welcome

Will

@will - Does the approach outlined in https://plot.ly/dash/urls work for you? This is what I use on the Dash docs (https://plot.ly/dash, https://github.com/plotly/dash-docs).

In your case, this would look something like:

import dash
import dash_core_components as dcc
import dash_html_components as html

import app1
import app2
import app3
import app4

print(dcc.__version__) # 0.6.0 or above is required

app = dash.Dash()


app.layout = html.Div([
    # represents the URL bar, doesn't render anything
    dcc.Location(id='url', refresh=False),

    # content will be rendered in this element
    html.Div(id='page-content')
])


@app.callback(dash.dependencies.Output('page-content', 'children'),
              [dash.dependencies.Input('url', 'pathname')])
def display_page(pathname):
    if pathname == '/apps/app1':
         return html.Div('This is app 1')
    elif pathname == '/apps/app2':
         return html.Div('This is app 2')
    elif pathname == '/apps/category/app3':
         return html.Div('This is app 3')
    elif pathname == '/apps/category/app4':
         return html.Div('This is app 4')

Or, if you want to put these apps in separate files and import them, it would look something like:

- app.py
- index.py
- apps
   |-- __init__.py
   |-- app1.py
   |-- app2.py

app.py

import dash

app = dash.Dash()
server = app.server
app.config.supress_callback_exceptions = True

app.css.append_css({
    "external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"
})

index.py

from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html

from app import app
from apps import app1, app2


app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='page-content')
])


@app.callback(Output('page-content', 'children'),
              [Input('url', 'pathname')])
def display_page(pathname):
    if pathname == '/apps/app1':
         return app1.layout
    elif pathname == '/apps/app2':
         return app2.layout
    else:
        return '404'

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

__init__.py

# empty file

app1.py

from dash.dependencies import Input, Output
import dash_html_components as html
import dash_core_components as dcc

from app import app

layout = html.Div([
    html.H3('App 1'),
    dcc.Dropdown(
        id='app-1-dropdown',
        options=[
            {'label': 'App 1 - {}'.format(i), 'value': i} for i in [
                'NYC', 'MTL', 'LA'
            ]
        ]
    ),
    html.Div(id='app-1-display-value'),
    dcc.Link('Go to App 2', href='/apps/app2')
])


@app.callback(
    Output('app-1-display-value', 'children'),
    [Input('app-1-dropdown', 'value')])
def display_value(value):
    return 'You have selected "{}"'.format(value)

app2.py

from dash.dependencies import Input, Output
import dash_html_components as html
import dash_core_components as dcc

from app import app

layout = html.Div([
    html.H3('App 2'),
    dcc.Dropdown(
        id='app-2-dropdown',
        options=[
            {'label': 'App 2 - {}'.format(i), 'value': i} for i in [
                'NYC', 'MTL', 'LA'
            ]
        ]
    ),
    html.Div(id='app-2-display-value'),
    dcc.Link('Go to App 1', href='/apps/app1')
])


@app.callback(
    Output('app-2-display-value', 'children'),
    [Input('app-2-dropdown', 'value')])
def display_value(value):
    return 'You have selected "{}"'.format(value)

4 Likes

Awesome, @chriddyp. I’ve actually tried something similar yesterday but yours is neater - and works well for simple apps. But for one of my more complicated one (multiple figures, callbacks; about 20 radios/dropdowns) I get the same wsgi issue:

Error on request:
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/werkzeug/serving.py", line 209, in run_wsgi
    execute(self.server.app)
  File "/usr/local/lib/python2.7/site-packages/werkzeug/serving.py", line 200, in execute
    write(data)
  File "/usr/local/lib/python2.7/site-packages/werkzeug/serving.py", line 168, in write
    self.send_header(key, value)
  File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/BaseHTTPServer.py", line 412, in send_header
    self.wfile.write("%s: %s\r\n" % (keyword, value))
IOError: [Errno 32] Broken pipe
127.0.0.1 - - [21/Aug/2017 20:59:08] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2017 20:59:08] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2017 20:59:08] "GET /favicon.ico HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2017 20:59:08] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2017 20:59:09] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2017 20:59:09] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2017 20:59:09] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2017 20:59:09] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2017 20:59:09] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2017 20:59:09] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2017 20:59:09] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2017 20:59:09] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2017 20:59:09] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2017 20:59:09] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2017 20:59:09] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2017 20:59:09] "POST /_dash-update-component HTTP/1.1" 200

The 200 response is repeated 6-10 times per second. I tried adding threaded=True to app.run_server() without luck; there must be something fundamentally wrong in my app. I’d share it here but it’s too sensitive unfortunately. I may pm you though, in case this is a common issue which may affect others (doubt it!)

Think your approach is on the right lines though. Will just have to start the app afresh

1 Like

For more complex, production-level apps, I recommend running the app behind gunicorn instead of the flask dev server. Like threading=True, this will run the app in a way that can process many requests in parallel. Unlike threading=True, it’s more robust and performant.

So:

$ gunicorn app:server

install gunicorn with pip install gunicorn. In this case, app refers to app.py and server refers to the variable server instead app.py. This is adapted from the instructions in deployment: Deploy Your Dash App | Dash for Python Documentation | Plotly

3 Likes

Thanks Chris. I did try with Gunicorn yesterday, but no joy. I’m going to forget about using this particular app inside a multi-page multi-URL app; it’s clearly too heavy / unwieldy / poorly written. Plan is to fire uwsgi once to handle the simpler apps, and again for this more complicated one.

Ideally, I believe we want to make it work with something like this:
http://uwsgi-docs.readthedocs.io/en/latest/Emperor.html

Combined with lazy start and timeout process termination - this seams to be a perfect setup, where each app is running in a separate python process, therefore avoiding potential memory issues, with one heavy app blocking everything else.
So far - I had only some luck with this setup (caused by some url path issues).

2 Likes