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:
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')
from flask import Flask
server = Flask(__name__)
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)
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.
This would be better than setting the csrf_protect
flag to False
, probably.
I was really looking forward to this, awesome!
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)
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
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
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).