Frontend and Backend
- Load the components in the layout on server side
- Implement callbacks with default inputs on server side
- Open a session on web browser
- Load the components in the layout from server side to client side with outputs from default inputs
- Receive inputs from user interaction on client side, implment corresponding callbacks on server side, then return outputs to client side
Global Variable
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server
shared = None
app.layout = html.Div([
dcc.Input(value=shared, id = 'input'),
html.H1(id = 'content'),
], className='container')
@app.callback(
Output('content', 'children'),
Input('input', 'value')
)
def update_output_div(value):
global shared
temp = shared
shared = value
return temp
app.scripts.config.serve_locally = True
if __name__ == '__main__':
app.run_server(debug=True)
Sessions share the same copy of the global variable
# single worker
python app.py
or
gunicorn app:server --workers 1
Each worker keeeps a copy of the global variable
May a different worker responses the request every time a session sends a request
As though a random global copy is returned
# multiple workers
gunicorn app:server --workers 8
Sharing Data Between Callbacks within a Single Session
Option 1. Each session access an independent file (database)
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
from os import path
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server
app.layout = html.Div([
dcc.Input(id = 'session_name'),
dcc.Input(id = 'input'),
html.H1(id = 'content'),
html.Button('Click', id = 'button')
], className='container')
@app.callback(
Output('content', 'children'),
Input('button', 'n_clicks'),
State('input', 'value'),
State('session_name', 'value')
)
def update_output_div(n_clicks, value, value_2):
if not n_clicks:
raise PreventUpdate
if not path.exists(value_2):
f = open(value_2, "w")
f.write('Init')
f.close()
output_str = 'None'
else:
f = open(value_2, "r")
output_str = f.readline()
f.close()
f = open(value_2, "w")
f.write(str(value))
f.close()
return output_str
if __name__ == '__main__':
app.run_server(debug=True)
Option 2. In the user's browser session via dcc.Store
- Store the data in memory in the browser client
- Data will be transported over the network between each callback
- It's generally safe to store up to 2MB in most environments, and 5~10MB in most desktop-only applications
import dash
from dash.dependencies import Input, Output
import dash_html_components as html
import dash_core_components as dcc
import pandas as pd
df = pd.DataFrame({
'x': [1, 2, 3, 4, 5, 6],
'y': [3, 1, 2, 3, 5, 6],
'z': ['A', 'A', 'B', 'B', 'C', 'C']
})
app = dash.Dash()
app.layout = html.Div([
dcc.Dropdown(
id='dropdown',
options=[{'label': i, 'value': i} for i in ['A', 'B', 'C']],
value='A'
),
dcc.Graph(
id='graph'
),
dcc.Store(id='cache')
])
@app.callback(Output('cache', 'data'), Input('dropdown', 'value'))
def update_cache(value):
filtered_df = df[df['z'] == value]
return filtered_df.to_json()
@app.callback(Output('graph', 'figure'), Input('cache', 'data'))
def update_graph(cached_data):
filtered_df = pd.read_json(cached_data)
print(type(filtered_df))
print(filtered_df)
return {
'data': [{
'x': filtered_df['x'],
'y': filtered_df['y'],
'type': 'bar'
}]
}
if __name__ == '__main__':
app.run_server(debug=True)
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
import json
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server
app.layout = html.Div([
dcc.Input(id = 'input_1'),
dcc.Input(id = 'input_2'),
html.Div(id = 'content'),
html.Button('Click', id = 'button'),
dcc.Store(id='cache')
], className='container')
@app.callback(
Output('cache', 'data'),
Input('button', 'n_clicks'),
State('input_1', 'value'),
State('input_2', 'value')
)
def update_cache_div(n_clicks, value, value_2):
d = {'input_1': value, 'input_2': value_2}
return d
@app.callback(
Output('content', 'children'),
Input('cache', 'data'),
)
def update_output_div(d):
dict_object = d
return str(dict_object['input_1']) + ' '+str(dict_object['input_2'])
if __name__ == '__main__':
app.run_server(debug=True)
Option 3. Save in hidden div via the browser's DOM
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
import json
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server
app.layout = html.Div([
dcc.Input(id = 'input_1'),
dcc.Input(id = 'input_2'),
html.Div(id = 'content'),
html.Button('Click', id = 'button'),
html.Div(id='cache', style={'display': 'none'})
], className='container')
@app.callback(
Output('cache', 'children'),
Input('button', 'n_clicks'),
State('input_1', 'value'),
State('input_2', 'value')
)
def update_cache_div(n_clicks, value, value_2):
d = {'input_1': value, 'input_2': value_2}
return json.dumps(d)
@app.callback(
Output('content', 'children'),
Input('cache', 'children'),
)
def update_output_div(d):
dict_object = json.loads(d)
return str(dict_object['input_1']) + ' '+str(dict_object['input_2'])
if __name__ == '__main__':
app.run_server(debug=True)
Option 4. storing “global variables” in the cache on the server-side
- The expensive function will be run one time, the return will be saved in a global cache
- further calling will access the return from the global cache directly without repeating the expensive process
import time
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
from os import path
from flask_caching import Cache
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server
cache = Cache(app.server, config={
'CACHE_TYPE': 'filesystem',
'CACHE_DIR': 'cache',
'CACHE_THRESHOLD': 200, # The maximum number of items the cache will store
'CACHE_DEFAULT_TIMEOUT': 60 # The default timeout, unit of time is seconds
})
app.layout = html.Div([
dcc.Input(id = 'input'),
html.Hr(),
dcc.Loading(html.H1(id = 'content')),
html.Button('Click', id = 'button'),
html.Hr(),
dcc.Loading(html.H1(id = 'content_2')),
html.Button('Click 2', id = 'button2')
], className='container')
# for the same parameter value, first call save results to cache
# second and later calls will reuse the results
# chaning the same parameter value, a new cache will be created
@cache.memoize()
def compute_expensive_data(value):
time.sleep(5)
return str(value)
@app.callback(
Output('content', 'children'),
Input('button', 'n_clicks'),
State('input', 'value')
)
def update_output_1(n_clicks, value):
if not n_clicks:
raise PreventUpdate
return compute_expensive_data(value)
@app.callback(
Output('content_2', 'children'),
Input('button2', 'n_clicks'),
State('input', 'value')
)
def update_output_2(n_clicks, value):
if not n_clicks:
raise PreventUpdate
return compute_expensive_data(value)
if __name__ == '__main__':
app.run_server(debug=True)
Reference