How to retain the uploaded data when switching between pages

I am building a multipage dash app. I have Upload component on 3 pages and entire visualisation in those pages depends on the uploaded data. But when I switch between pages, the page gets refreshed and everything gets deleted. I stored the uploaded data and retrieved it for graphs. I added persistence for all drop-downs. But still the page gets refreshed. What can I do to retain those uploaded files? Thanks a lot in advance.

Hello @Sindhuja,

Welcome to the community!

When you say, you are saving the data. Is it into a dcc.Store? Do you have it setup as local, session or memory.

Same question for your persistence.

Next, how are you navigating between pages? A dcc.Link or you are navigating manually?

If you are using an html.A, I think the page will refresh.

Hello, @jinnnyzor,

Thank you for responding.

I am storing the data using dcc.Store and in ‘session’ storage type. Same for persistence. I have tried with local and memory also, but the pages still get refreshed.

To navigate between the pages, I have created sidebar with navigation links (used dbc.NavLink for this).

Alright, great.

Would you be able to post an MRE to be able to help further?


Or, you could post your code here for specific help on what is going on.

Please find below the zip file containing the skeleton of my code. You can use the excel file in the ‘datasets’ folder to upload and check. This example consists of two pages with upload component.

I have created different .py files for different pages, which made it difficult to post my code here.

Link to file: Plotly.zip - Google Drive

Thanks again for helping

Hello @Sindhuja,

You have one main thing going on, you have your dcc.Store being registered inside the callback from uploading data. This means that the ID is non-existent when trying to pull the data from the stored dcc.Store. Once you upload a file, it connects the Session store again.

This is a fix of the app:

from dash import Dash, html, dcc
import dash_bootstrap_components as dbc
import dash

app = Dash(__name__, use_pages=True, external_stylesheets=[dbc.themes.BOOTSTRAP])
# app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
server = app.server
app.config.suppress_callback_exceptions = True

SIDEBAR_STYLE = {
    "position": "fixed",
    "top": 0,
    "left": 0,
    "bottom": 0,
    "width": "16rem",
    "padding": "2rem 1rem",
    "background-color": "#f8f9fa",
}

CONTENT_STYLE = {
    "margin-left": "18rem",
    "margin-right": "2rem",
    "margin-top": "2rem",
    "padding": "2rem 1rem",
}

sidebar = html.Div(
    [
        html.H2("DSS", className="display-4"),
        html.Hr(),
        dbc.Nav(
                [
                 dbc.NavLink("Home", href="/Home", active="exact"),
                 dbc.Col(dbc.NavLink("page1", href="/Home/page1", active="exact"),
                 width={"offset":2},
                 ),
                 dbc.Col(dbc.NavLink("page2", href="/Home/page2", active="exact"),
                 width={"offset":2},
                 ),
          ],

        vertical=True,
        pills=True,
        navbar=True,
        navbar_scroll=True,
        ),
        dcc.Store(id='stored_data_anv', data=[], storage_type='session'),
        dcc.Store(id='stored_data_ev', data=[], storage_type='session')
    ],
    style=SIDEBAR_STYLE,
)

content = html.Div([dash.page_container], style=CONTENT_STYLE)

app.layout = html.Div([dcc.Location(id="url"), sidebar, content])



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

page1.py:

import dash
from dash import html, dcc, callback, Input, Output, ALL, State, MATCH, ALLSMALLER, dash_table
import plotly.express as px
import pandas as pd
import numpy as np
import dash_bootstrap_components as dbc
import plotly
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import json
import base64
import datetime
import io
from urllib.request import urlopen
from dash.exceptions import PreventUpdate
# import geopandas as gpd

dash.register_page(
__name__,
path = '/Home/page1',
order = 3,
name = "page1",
title = "Page 1",
)

layout = html.Div(children=[
    html.H1(children='PAGE 1'),
    html.Hr(),
    html.Div([
        dcc.Tabs([
            dcc.Tab(label='Data and Visualisation', children = [
                html.Div("Upload your dataset in the csv format. Numercial and categorical variables can be uploaded. For more instructions check the pdf?"),
                html.Br(),
                html.Div([
                    dcc.Upload(
                        id='upload_data_anv',children=html.Div([
                            'Drag and Drop or ', html.A('Select Files')
                        ]),
                        style={
                            'width': '100%','height': '60px','lineHeight': '60px','borderWidth': '1px','borderStyle': 'dashed',
                            'borderRadius': '5px','textAlign': 'center','margin': '10px'
                            }, multiple= True
                    ),
                    html.Div(id='output_datatable_anv'),
                    html.Div(children=[
                    dbc.Button('Add Chart', id='add_chart_anv', color="primary", n_clicks=0),
                    ]),
                    html.Br(),
                    html.Div(id='container_up_anv', children=[])
                ]),
            ]),

            dcc.Tab(label='Spatial Analysis', children = [
            ]),
        ])
    ])
])

######################################## reading uploaded data ########################################
def contents(contents, filename):
    content_type, content_string = contents.split(',')
    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            df = pd.read_csv(
                io.StringIO(decoded.decode('utf-8')), low_memory=False)
        elif 'xls' in filename:
            df = pd.read_excel(io.BytesIO(decoded))
    except Exception as e:
        print(e)
        return html.Div(['There was an error processing this file.'])

    return html.Div([
        html.P("Data Columns"),
        dcc.Dropdown(id='columns',
                     options=[{'label':x, 'value':x} for x in df.columns], placeholder= "Column names in the uploaded data"),
        html.Hr(),
        dash_table.DataTable(
            id='table_data_anv',
            data=df.to_dict('records'),
            columns=[{'name': i, 'id': i} for i in df.columns],
            page_size=15,
        )
    ])

@dash.callback(
            Output('output_datatable_anv', 'children'),
            Input('upload_data_anv', 'contents'),
            Input('upload_data_anv', 'filename'))

def update_output_anv(list_of_contents, list_of_names):
    if list_of_contents is not None:
        children = [
            contents(c, n) for c, n in
            zip(list_of_contents, list_of_names)]
        return children

@dash.callback(Output('stored_data_anv', 'data'), Input('table_data_anv', 'data'))
def update_store(d):
    if d:
        return d

####################################### read & represent - upload data ########################################
@dash.callback(
    Output('container_up_anv', 'children'),
    [Input('add_chart_anv', 'n_clicks')],
    [State('container_up_anv', 'children')],
    [State('stored_data_anv', 'data')],
)

def display_graphs_anv(n_clicks, div_children, data):
    if data:
        df_up = pd.DataFrame(data)
        new_child = html.Div(
            style={'width': '45%', 'display': 'inline-block', 'outline': 'thin lightgrey solid', 'padding': 10},
            children=[
                dcc.Graph(
                    id={
                        'type': 'dynamic-graph-anv',
                        'index': n_clicks
                    },
                    figure={}
                ),
                dcc.RadioItems(
                    id={
                        'type': 'dynamic-choice-anv',
                        'index': n_clicks
                    }, persistence=True,
                    options=[{'label': 'Bar Chart', 'value': 'bar'},
                             {'label': 'Line Chart', 'value': 'line'},
                             {'label': 'Pie Chart', 'value': 'pie'}],
                    value='bar',
                ),
                dcc.Dropdown(
                    id={
                        'type': 'dynamic-dpn-d-anv',
                        'index': n_clicks
                    },persistence=True,
                    options=[{'label': s, 'value': s} for s in np.sort(df_up["Geo_region_Names"].unique())],
                    multi=True
                ),

                dcc.Dropdown(
                    id={
                        'type': 'dynamic-dpn-var-anv',
                        'index': n_clicks
                    },persistence=True,
                    options=[{'label': n, 'value': n} for n in (df_up.select_dtypes(include=['int64','float64'])).columns.values.tolist()],
                    clearable=False
                ),
                dcc.Dropdown(
                    id={
                        'type': 'dynamic-dpn-ctg-anv',
                        'index': n_clicks
                    },persistence=True,
                    options=[{'label': 'Absolute Variable', 'value': 'Absolute Variable'},
                            {'label': 'Percentage Variable', 'value': 'Percentage Variable'},
                            {'label': 'Categorical Variable', 'value': 'Categorical Variable'}],
                    value='Absolute Variable',
                    clearable=False
                )
        ])
        div_children.append(new_child)
        return div_children
    raise PreventUpdate

@dash.callback(
    Output({'type': 'dynamic-graph-anv', 'index': MATCH}, 'figure'),
    [Input(component_id={'type': 'dynamic-dpn-d-anv', 'index': MATCH}, component_property='value'),
     Input(component_id={'type': 'dynamic-dpn-var-anv', 'index': MATCH}, component_property='value'),
     Input(component_id={'type': 'dynamic-dpn-ctg-anv', 'index': MATCH}, component_property='value'),
     Input({'type': 'dynamic-choice-anv', 'index': MATCH}, 'value'),
     Input('stored_data_anv', 'data')]
)

def display_graph_anv(dis_value, var_value, cat_value, chart_choice, data):
    # print(type(data))
    df_up = pd.DataFrame(data)
    # print(df_up.head())
    # print(type(df_up))
    frames_up={}
    for i in range(len(dis_value)):
        dff = df_up[df_up['Geo_region_Names'].isin([dis_value[i]])]
        frames_up[i] = dff

    if chart_choice == 'bar':
        if cat_value == "Absolute Variable":
            fig = px.bar(x=dis_value, y= [frames_up[i][var_value].sum() for i in range(len(frames_up))])
            fig.update_layout(legend=dict( yanchor="bottom", y=-1,xanchor="left",x=0))
            return fig

page2.py

import dash
from dash import html, dcc, callback, Input, Output, ALL, State, MATCH, ALLSMALLER, dash_table
import plotly.express as px
import pandas as pd
import numpy as np
import dash_bootstrap_components as dbc
import plotly
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import json
import base64
import datetime
import io
from urllib.request import urlopen
from dash.exceptions import PreventUpdate
# import geopandas as gpd

dash.register_page(
__name__,
path = '/Home/page2',
order = 3,
name = "page2",
title = "Page 2",
)


layout = html.Div([
    html.H1(children='PAGE 2'),
    html.Br(),
    html.Div([
        dcc.Tabs([
            dcc.Tab(label='Data and Visualisation', children = [
                html.Div("Upload your dataset in the csv format. Numercial and categorical variables can be uploaded. For more instructions check the pdf?"),
                html.Br(),
                html.Div([
                    dcc.Upload(
                        id='upload_data_ev',children=html.Div([
                            'Drag and Drop or ', html.A('Select Files')
                        ]),
                        style={
                            'width': '100%','height': '60px','lineHeight': '60px','borderWidth': '1px','borderStyle': 'dashed',
                            'borderRadius': '5px','textAlign': 'center','margin': '10px'
                            }, multiple= True
                    ),
                    html.Div(id='output_datatable_ev'),
                    html.Div(children=[
                    dbc.Button('Add Chart', id='add_chart_ev', color="primary", n_clicks=0),
                    ]),
                    html.Br(),
                    html.Div(id='container_up_ev', children=[])
                ]),
            ]),

            dcc.Tab(label='Spatial Analysis', children = [
            ]),
        ])
    ])
])

######################################## reading uploaded data ########################################
def contents(contents, filename):
    content_type, content_string = contents.split(',')
    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            df = pd.read_csv(
                io.StringIO(decoded.decode('utf-8')))
        elif 'xls' in filename:
            df = pd.read_excel(io.BytesIO(decoded))
    except Exception as e:
        print(e)
        return html.Div(['There was an error processing this file.'])

    return html.Div([
        html.P("Data Columns"),
        dcc.Dropdown(id='columns',
                     options=[{'label':x, 'value':x} for x in df.columns], placeholder= "Column names in the uploaded data"),
        html.Hr(),
        dash_table.DataTable(
            id='table_data_ev',
            data=df.to_dict('records'),
            columns=[{'name': i, 'id': i} for i in df.columns],
            page_size=15,
        )
    ])

@dash.callback(
            Output('output_datatable_ev', 'children'),
            Input('upload_data_ev', 'contents'),
            Input('upload_data_ev', 'filename'))

def update_output_ev(list_of_contents, list_of_names):
    if list_of_contents is not None:
        children = [
            contents(c, n) for c, n in
            zip(list_of_contents, list_of_names)]
        return children

@dash.callback(Output('stored_data_ev', 'data'), Input('table_data_ev', 'data'))
def update_store(d):
    if d:
        return d
####################################### read & represent - upload data ########################################
@dash.callback(
    Output('container_up_ev', 'children'),
    [Input('add_chart_ev', 'n_clicks')],
    [State('container_up_ev', 'children')],
    [State('stored_data_ev', 'data')],
)

def display_graphs_ev(n_clicks, div_children, data):
    if data:
        df_up = pd.DataFrame(data)
        new_child = html.Div(
            style={'width': '45%', 'display': 'inline-block', 'outline': 'thin lightgrey solid', 'padding': 10},
            children=[
                dcc.Graph(
                    id={
                        'type': 'dynamic-graph-ev',
                        'index': n_clicks
                    },
                    figure={}
                ),
                dcc.RadioItems(
                    id={
                        'type': 'dynamic-choice-ev',
                        'index': n_clicks
                    },persistence=True,
                    options=[{'label': 'Bar Chart', 'value': 'bar'},
                             {'label': 'Line Chart', 'value': 'line'},
                             {'label': 'Pie Chart', 'value': 'pie'}],
                    value='bar',
                ),
                dcc.Dropdown(
                    id={
                        'type': 'dynamic-dpn-d-ev',
                        'index': n_clicks
                    },persistence=True,
                    options=[{'label': s, 'value': s} for s in np.sort(df_up["Geo_region_Names"].unique())],
                    multi=True
                ),

                dcc.Dropdown(
                    id={
                        'type': 'dynamic-dpn-var-ev',
                        'index': n_clicks
                    },persistence=True,
                    options=[{'label': n, 'value': n} for n in (df_up.select_dtypes(include=['int64','float64'])).columns.values.tolist()],
                    clearable=False
                ),
                dcc.Dropdown(
                    id={
                        'type': 'dynamic-dpn-ctg-ev',
                        'index': n_clicks
                    },persistence=True,
                    options=[{'label': 'Absolute Variable', 'value': 'Absolute Variable'},
                            {'label': 'Percentage Variable', 'value': 'Percentage Variable'},
                            {'label': 'Categorical Variable', 'value': 'Categorical Variable'}],
                    value='Absolute Variable',
                    clearable=False
                )
        ])
        div_children.append(new_child)
        return div_children
    raise PreventUpdate

@dash.callback(
    Output({'type': 'dynamic-graph-ev', 'index': MATCH}, 'figure'),
    [Input(component_id={'type': 'dynamic-dpn-d-ev', 'index': MATCH}, component_property='value'),
     Input(component_id={'type': 'dynamic-dpn-var-ev', 'index': MATCH}, component_property='value'),
     Input(component_id={'type': 'dynamic-dpn-ctg-ev', 'index': MATCH}, component_property='value'),
     Input({'type': 'dynamic-choice-ev', 'index': MATCH}, 'value'),
     Input('stored_data_ev', 'data')]
)

def display_graph_ev(dis_value, var_value, cat_value, chart_choice, data):
    if not dis_value:
        raise PreventUpdate
    
    df_up = pd.DataFrame(data)
    frames_up={}
    for i in range(len(dis_value)):
        dff = df_up[df_up['Geo_region_Names'].isin([dis_value[i]])]
        frames_up[i] = dff

    if chart_choice == 'bar':
        if cat_value == "Absolute Variable":
            fig = px.bar(x=dis_value, y= [frames_up[i][var_value].sum() for i in range(len(frames_up))])
            fig.update_layout(legend=dict( yanchor="bottom", y=-1,xanchor="left",x=0))
            return fig

Please note, when troubleshooting, it really helps to keep debug=True.

What you needed to do was bring the stores to the app level in order to register the ID’s with Dash, the session stores existed already. Then inside each page, you needed to add id’s to the tables in order to allow for updating the uploaded data to the dcc.

There are some other issues with some of how your chart building goes. Having debug=True will help you with those things. :slight_smile:
I also turned suppress_callback_exceptions=True

1 Like

Thank you very much for helping me out with this.

  1. I shifted all my stores to the app level
  2. I have debugged all the issues based on the errors messages

I have made significant progress based on your advise. Thanks again!

If you do not mind, I have one more query.

Though the uploaded data is getting retained on the pages, the graphs and maps (on multiple tabs in multiple pages) are disappearing. But I am able to regenerate the maps and graphs without re-uploading the data!

Am I doing something wrong with persistence? How do I retain the maps & graphs, and how do I stay on the same tab in a page (even after switching between pages)?

1 Like