✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
⚡️ Concerned about the grid? Kyle Baranko teaches how to predicting peak loads using XGBoost. Register for the August webinar!

Multi-page persistence not working

Dear Forum,
I’m working on a multi-page Dash app, where the user selects a .csv from a drop-down (home page), and this is supposed to generate graphs, etc. on the home page as well as other pages. The problem I’m encountering is that as soon as I move off any given “page”, all data is lost and I have to re-select (i.e. reload) the file from the list. I’ve attempted to use the “hidden div” hack, but that didn’t seem to work. I’m quite certain I’m doing something wrong or missing something. I’m posting my code below for each page and hopefully, someone has gone through this before.

the .csv test file contains two columns: “Brand” with x number of categories (i.e. Microsoft, IBM, etc.), and another column called “var_1” with a numerical “score” for each brand.

Thank you in advance for your assistance!

app.py

import dash

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server
app.config.suppress_callback_exceptions = True

Index.py

import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from os import listdir
from os.path import isfile, join
from dash.exceptions import PreventUpdate
import pandas as pd
from app import app
from layouts import layout1, layout2
import callbacks

pathway = '/yourpathtotheappfolder...'
files = [f for f in listdir(pathway) if isfile(join(pathway, f))]  #this iterates through the files in the folder

app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='intermediate-value', style={'display': 'none'}),  #hidden div, doesn't seem to work 
    html.Div([
        dcc.Link('Home', href='/app1', className="tab first",),  #link to page 1 "Home"
        dcc.Link('Page 1', href='/app2', className="tab",), #link to another page "Page 1"
        ], className="row all-tabs",
    ),
    dcc.Dropdown(
        id='file_select',
        options=[
                     {'label': i, 'value': i} for i in files
                 ],
        multi=False
    ),
    # code for the brand select dropdown
    dcc.Dropdown(
    id="select_brand",
    options=[],
    multi=False,
    ),
    html.Div(id='page-content')
])

@app.callback(Output('intermediate-value', 'children'), [Input('file_select', 'value')])
def clean_data(value):
     # going to just upload that csv as a dataframe to the hidden div!
     cleaned_df = pd.read_csv(value)
     return cleaned_df.to_json() # or, more generally, json.dumps(cleaned_df)


@app.callback(Output('page-content', 'children'),
              [Input('url', 'pathname')])
def display_page(pathname):
    if pathname == '/app1':
         return layout1
    elif pathname == '/app2':
         return layout2
    else:
        return '404'



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

Callbacks.py

# Specify the callbacks for the app
from dash.dependencies import Input, Output
from app import app
import dash_table
import pandas as pd
import numpy as np
import plotly.graph_objs as go
import plotly



# this is for the dynamic dropdown field to select brand (which for now does nothing)

@app.callback(Output('select_brand', 'options'),
             [Input('file_select','value')])
def update_dropdown(file):
    if file is not None:
        df = pd.read_csv(file)
        columns = df.Brand.values.tolist()
        columns = list(set(columns))
        if df is not None:
            return [{'label': x, 'value': x} for x in columns]
        else:
            return []
    else:
        return []

#This populates the graph

@app.callback([Output('table', 'children'),
               Output('graph3', 'figure')],
             [Input('intermediate-value', 'children')])
def update_graph(data):
    #dff = pd.read_csv(data)
    dff = pd.read_json(data) # or, more generally json.loads(jsonified_cleaned_data)
    y = dff.groupby('Brand', as_index=False)['var_1'].mean().rename(columns={'var_1':'total'})
    x = y['Brand']
    table = dash_table.DataTable(
                            data=dff.to_dict('records'),
                            columns=[{'name': i, 'id': i} for i in dff.columns])
    trace = go.Bar(x=x, y=y['total'])
    layout = go.Layout(
        #title='A good title goes here',
        yaxis=dict(range=[0, 5],title='Avg Score'),
        xaxis=dict(title='Brand'),
        barmode='group',
        bargap=0.15, # gap between bars of adjacent location coordinates.
        bargroupgap=0.1 # gap between bars of the same location coordinate.
    )
    
    fig = go.Figure(data = [trace], layout=layout)
    return table, fig

Layouts.py

import dash_core_components as dcc
import dash_html_components as html

layout1 = html.Div([
    html.H3('App 1'),
    
    html.Div(
        [html.Div(id='table')]
    ),
    html.Div(id='app-1-display-value'),
    dcc.Link('Go to App 2', href='/app2')
])

layout2 = html.Div([
    html.H3('App 2'),
    
    html.Div(
        [dcc.Graph(id='graph3')]
    ),

    html.Div(id='app-2-display-value'),
    dcc.Link('Go to App 1', href='/app1')
])

Going to a different page reloads all the div values so the hidden div won’t work.

I often store things like this in pickle files on disk on the backend and have a function that loads it in again on page load. I’ve also built brain-plasma https://github.com/russellromney/brain-plasma to store larger objects and share between parts of the webpage. I find that sqlitedict is better for smaller things https://github.com/RaRe-Technologies/sqlitedict.

I am running into this issue where all data stored (on initial file upload landing page when user uploaded files) is done when i move to another page. In my case data is secure and cannot be stored on server. So hidden div was the best option until i realized this is happening. Any way to copy information from specific DIV s fro one page to another? Thanks in advance.

dcc.Store will do this for you :slight_smile:

But going by the documentation, it has very limited memory allocated(2MB to 10MB).
Any other elegant solution. Looking at this as a default, DASH is not designed for multi-page apps.