I am currently trying to run multiple Plotly Dash apps with serve layout functions (see code bellow with serve_layout1() and serve_layout2() functions). I need them because I have to include several global variables in layout so my app will be updated when someone click refresh button in a browser. Multiple Dash apps work fine without serve layout functions. However, when I try to run multiple apps with serve layout functions, things start going awry.
Here is my file structure:
- app.py
- index.py
- apps
|-- __init__.py
|-- app1.py
|-- app2.py
Where app.py:
import dash
app = dash.Dash(__name__, suppress_callback_exceptions=True)
server = app.server
index.py:
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from app import app
from apps import app1, app2
from apps.app1 import serve_layout1
from apps.app2 import serve_layout2
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':
app1.layout = serve_layout1
return app1.layout
elif pathname == '/apps/app2':
app2.layout = serve_layout2
return app2.layout
else:
return '404'
if __name__ == '__main__':
app.run_server(debug=True)
app1.py:
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from app import app
def serve_layout1():
global df
# uploading cvs as dataframe, doing some computation here and etc (skipped)
app_layout1 = 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')
])
return app_layout1
@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:
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from app import app
def serve_layout2():
global df
# uploading cvs as dataframe, doing some computation here and etc (skipped)
app_layout2 = 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 2', href='/apps/app1')
])
return app_layout2
@app.callback(
Output('app-1-display-value', 'children'),
Input('app-1-dropdown', 'value'))
def display_value(value):
return 'You have selected "{}"'.format(value)
When I run the app I am getting the following error:
Traceback (most recent call last):
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\dash\dash.py", line 1039, in add_context
response, cls=plotly.utils.PlotlyJSONEncoder
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\json\__init__.py", line 238, in dumps
**kw).encode(obj)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\_plotly_utils\utils.py", line 45, in encode
encoded_o = super(PlotlyJSONEncoder, self).encode(o)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\json\encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\json\encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\_plotly_utils\utils.py", line 115, in default
return _json.JSONEncoder.default(self, obj)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\json\encoder.py", line 180, in default
o.__class__.__name__)
TypeError: Object of type 'function' is not JSON serializable
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 1950, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 1936, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\dash\dash.py", line 1072, in dispatch
response.set_data(func(*args, outputs_list=outputs_list))
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\dash\dash.py", line 1042, in add_context
_validate.fail_callback_output(output_value, output)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\dash\_validate.py", line 256, in fail_callback_output
_validate_value(val, index=i)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\dash\_validate.py", line 251, in _validate_value
toplevel=True,
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\dash\_validate.py", line 205, in _raise_invalid
bad_val=bad_val,
dash.exceptions.InvalidCallbackReturnValue: The callback for `<Output `page-content.children`>`
returned a value having type `function`
which is not JSON serializable.
The value in question is either the only value returned,
or is in the top level of the returned list,
and has string representation
`<function serve_layout1 at 0x0000016B49DABB70>`
In general, Dash properties can only be
dash components, strings, dictionaries, numbers, None,
or lists of those.
The problem is that you are not calling your serve_layout
functions here:
@app.callback(Output("page-content", "children"), Input("url", "pathname"))
def display_page(pathname):
if pathname == "/apps/app1":
app1.layout = serve_layout1
return app1.layout
elif pathname == "/apps/app2":
app2.layout = serve_layout2
return app2.layout
else:
return "404"
Instead you should do this:
@app.callback(Output("page-content", "children"), Input("url", "pathname"))
def display_page(pathname):
if pathname == "/apps/app1":
app1.layout = serve_layout1()
return app1.layout
elif pathname == "/apps/app2":
app2.layout = serve_layout2()
return app2.layout
else:
return "404"
So you're returning a reference to a function instead of the dash components the function returns.
This is not valid as the error message you've shared is saying:
In general, Dash properties can only be dash components, strings, dictionaries, numbers, None, or lists of those.
Also in app2.py
you're referencing the wrong ids in the callback. You're referencing the ids
for app1.py
. You can't have duplicate callback outputs. So the callback in app2.py
should look like this:
@app.callback(
Output("app-2-display-value", "children"), Input("app-2-dropdown", "value")
)
def display_value(value):
return 'You have selected "{}"'.format(value)