Multiple Dashboards?

@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: https://plot.ly/dash/deployment

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

A post was split to a new topic: URLs with a url_base_pathname

Thanks Chris. Your post for setting up the multipage app using separate files was very useful to me. However, I am having problems getting dash_auth.BasicAuth working with this project structure. Using a similar project tree as you have listed above, I have the following:

app.py

import dash
import dash_auth

VALID_USERNAME_PASSWORD_PAIRS = [
    ['bob', 'loblaw']
]

app = dash.Dash('auth')
auth = dash_auth.BasicAuth(
    app,
    VALID_USERNAME_PASSWORD_PAIRS
]
server = app.server
app.config.suppress_callback_exceptions = True

Upon trying to run from index.py, I get the following error:

File ".../site-packages/dash_auth/auth.py", in line 16, in _overwrite_index
   original_index = self.app.server.view_functions['index']
KeyError: 'index'

which happens regardless of the order of the app.server and BasicAuth lines. Any thoughts?

A post was split to a new topic: Multiple Page App - Google App Engine

@chriddyp i was wondering if you or anyone else on this message board could assist me. I tried going the way of the tutorial you posted but to no avail {on how to create a multi-page Dash App} so I’m currently trying the other way {the method Vlad posted higher on this board}

My structure looks like this:

dash-project/
app1/
dash_1.py

app2/
dash_2.py

 ...

server.py
run.py

My run.py code is

from app import server as application
from app import app

import app_1.dash_1
import app_2.dash_2

My app.py code is:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bootstrap import Bootstrap
from flask_mail import Mail
from flask_moment import Moment
from flask_login import LoginManager
from flask_pagedown import PageDown
from flask_migrate import Migrate, MigrateCommand
from flask_sslify import SSLify
from werkzeug.wsgi import DispatcherMiddleware
import os
import pandas as pd
import dash
import flask

import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objs as go
from analysis import *

server=flask.Flask(name)
app = dash.Dash(name)
app.config.suppress_callback_exceptions = True

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

My dash_1.py code is

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import pandas as pd
import plotly.graph_objs as go
from analysis import *

from app import app, server

app = dash.Dash(name=‘dash_1’, sharing=True, url_base_pathname=’/dash_1’, csrf_protect=False)

app.config[‘suppress_callback_exceptions’]=True

df = pd.read_csv(’/Users/WilliamStevens/Documents/deplorable_snowflake/ds/app_1/main.csv’)

layout = html.Div([
dcc.Graph(
id='Senators ',
figure={
‘data’: [
go.Scatter(
x=df[df[‘candidate_name’] == i][‘contributor_individual’],
y=df[df[‘candidate_name’] == i][‘contributor_pac’],
#z=df[df[‘candidate_name’] == i][‘contributor_total’],
text=df[df[‘candidate_name’] == i][‘contributor_total’],
mode=‘markers’,
opacity=0.7,
marker={
‘size’: 15,
‘line’: {‘width’: 0.5, ‘color’: ‘white’}
},
name=i
) for i in df.candidate_name.unique()
],
‘layout’: go.Layout(
xaxis={‘title’: ‘Contributor’},
yaxis={‘title’: ‘Contributor’},
#margin={‘l’: 40, ‘b’: 40, ‘t’: 10, ‘r’: 10},
#legend={‘x’: 0, ‘y’: 1},
hovermode=‘closest’
)
}
)
])

layout = html.Div(children=[
html.H1(children='Senators '),

html.Div(children='''
    God Bless .
'''),

dcc.Graph(
    id='main.csv',
    figure={
        'data': [
        {'x':df['x'], 'y': df['sector'], 'type': 'bar'},
        {'x':df['y'], 'y': df['industr'], 'type': 'bar'},
        {'x':df['z'], 'y': df['contributor'], 'type': 'bar'},
        ],
        'layout': {
            'title': 'Let Them Eat Cake ...'
        }
    }
)

])

My dash_2.py code is

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

from app import app, server

app = dash.Dash(name=‘dash_2’, sharing=True, url_base_pathname=’/dash_2’, csrf_protect=False)

app.config[‘suppress_callback_exceptions’]=True

df = pd.read_csv(’/Users/WilliamStevens/Documents/deplorable_snowflake/ds/app_2/slice.csv’)

layout = html.Div(children=[
html.H1(children=‘Barstool’),

html.Div(children='''
    One .
'''),

dcc.Graph(
    id='slice.csv',
    figure={
        'data': [
        {'x':df['Slice'], 'y': df['Score'], 'type': 'bar'},
        ],
        'layout': {
            'title': 'Bar'
        }
    }
)

])

When I run the app, all my other pages work except the /dash_1 & /dash_2 pages. Was wondering if you or anyone else could help out

Hi
I have the same problem i don’t know where i have to write this uwsgi --http 0.0.0.0:5000 --processes 4 --wsgi-file run.py

hi
where should i write this :uwsgi --http 0.0.0.0:5000 --processes 4 --wsgi-file run.py

@chriddyp, this is awesome. Thanks!
One question, how could I also render an html template if the pathname == ‘/about’

I’ve tried to add this to the index.py file:

elif pathname == ‘/about’:
@server.route(’/about’)
def about():
return render_template(“about.html”)
else:
return ‘404’

It doesn’t return me an error, but simply the blank page.
Is there any solution by chance?

Hey! Did you manage to make it work ? I’m in the same situation, impossible to make it work.

Hi, you can see my example code for handle multiple dashboards.:slightly_smiling_face:

Good morning @bullybear .
Have you ever managed to get it to work? Also, any example of your main.csv and slice.csv files please? Thanks! :slight_smile:

three reasons why you should buy plotly pro: support open source, get great support, host your plots and dashboards online