Frontend and Backend
  1. Load the components in the layout on server side
  2. Implement callbacks with default inputs on server side
  3. Open a session on web browser
  4. Load the components in the layout from server side to client side with outputs from default inputs
  5. 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
  • 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
  • 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
  • Flask-Caching
  • Hidden Div