Black Lives Matter. Please consider donating to Black Girls Code today.
Dash HoloViews is now available! Check out the docs.

Updates on page load and loading newest data?

Using Dash, I would like to make live updates of changing data.

I have read the docs at https://dash.plot.ly/live-updates and there are basically two options: 1) predefined intervals using dcc.Interval, or (2) updates on page load, setting the App.layout to a function def serve_layout(). I only want new data to be loaded when the user refreshes the page (using refresh button in the browser), not when one interacts with a dash core component such as a dropdown menu.

The provided code below shows an interactive lineplot, but upon pushing the refresh button it is unsuccessful at loading new data. My question is: what to do to make it load new data and update graphs everytime the refresh button is hit? Is this possible using the serve_layout() function?

The file df.csv is an entirely fictive simplification.
The object df looks like this:

A B C
x 2 2
x 4 8
y 1 9
y 4 10
y 5 6

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
import datetime
import json

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.config['suppress_callback_exceptions']=True

df = pd.read_csv('/mydirectory/df.csv')

def serve_layout():

    slayout = html.Div([
        html.Div([
            dcc.Dropdown(
                id='dropdown',
                options=[{'label': i, 'value': i} for i in ['x','y']],
                value='x'
            ),
            html.Div(id='intermediate-value'),
        ]),

        html.Div([
            dcc.Graph(
                id='igraph1',
                style={'width': 1200}
            ),
        ])
    ])
    return slayout

## CALLBACKS

@app.callback(Output('intermediate-value', 'children'),
              [Input('dropdown', 'value')])
def clean_data(nvalue):
    df_1 = df[df['A'] == nvalue]
           
    datasets = {
        'df_1' : df_1.to_json(orient='split', date_format='iso'),
    }
    return json.dumps(datasets)

@app.callback(
    Output('igraph1', 'figure'),
    [Input('intermediate-value', 'children')])
def update_fig1(cleaned_data):
    datasets = json.loads(cleaned_data)
    dff = pd.read_json(datasets['df_1'], orient='split')
    
    return {
        'data' : [go.Scatter(
            x=dff['B'], 
            y=dff['C'],
            name='test',
            marker={'size':10, 'opacity':0.5, 'line':{'width':2}}
        )],
        
        'layout' : go.Layout(
            xaxis={'title':'Time'},
            yaxis={'title':'Values'},
            margin={'l':40, 'b':40, 't':40, 'r':0},
            hovermode='closest'
        )
    }

app.layout = serve_layout

if __name__ == '__main__':
    app.run_server(debug=True)

Thank you very much in advance for your time. Your help is very much appreciated !

You’ll need to read df inside each function, instead of doing it at the top-level (right now, df is only loaded once, when the app starts).

1 Like

Thank you for your prompt reply!
Do you mean directly inside the callback functions?
For example the following has the consequence that the data is read everytime when there is an interaction with the dropdown menu. This is problematic with big data, which takes time loading. Is there some other solution, for ex. sending data between callbacks?

def clean_data(nvalue):
    df = pd.read_csv('/mydirectory/df.csv')

If it doesn’t matter which user triggered a page load and therefore a reloading of your dataframe, then you could potentially bend the no-global modifying rule, and make your DataFrame a global and have it be updated within the function that returns the Dash layout (and which is run dynamically as you mentioned).

1 Like

Hi Nedned, making the DataFrame a Global works! Could you explain what is potentially the problem of doing this, as you seem to imply by saying “if it does not matter which user triggered a page load”.

When creating callbacks, you generally don’t want to modify globals. Reading them is fine, but modifying them is often a bad idea as it means you have turned your app from a stateless one into one that is statefull. See “why Global variables will break your app” in the Dash Docs. For example, if a user uploads a CSV and that updates a global dataframe, then all subsequent requests from all users would get the new dataframe.

But if you actually want all users to get the new dataframe (which maybe you do) then this could be a time when this is ok. This would mean that you’re basically exploiting the page load of each user as an opportunity to update the global dataframe.

Actually now I think about it, this still could be a bad idea, as a user who has already loaded the app with a specific dataframe, could get data from an updated dataframe during a callback, which could lead to data from inconsistent dataframes. I guess this depends on how your app works and maybe you could construct to avoid this issue, but in general you are opening yourself up to these kinds of gotchas, which is why it’s recommended to avoid this approach.

1 Like