Upload to table - data persistence

Hey everyone,
I’m coding a multi-page application with an upload component and a table that displays the contents of my uploaded file.

The table component gets the data store as input. It works when you uploaded a file once, but when we refresh or change the page, it doesn’t work anymore, even if the data is stored correctly in the store component.

I want the table to remain displayed as soon as there is data stored in the store
It works well with another component chart in another page.

Could you help me please?
The code for my page :

from dash import html, dcc, Output, Input, callback, dash_table
import pandas as pd

# Définir la page d'upload
layout = html.Div([
    html.Div([
        dcc.Upload(
            id='upload-data',
            children=html.Div([
                'Glissez et déposez un fichier ici ou ',
                html.A('cliquez pour sélectionner un fichier')
            ]),
            style={
                'width': '100%',
                'height': '100px',
                'lineHeight': '100px',
                'borderWidth': '1px',
                'borderStyle': 'dashed',
                'borderRadius': '5px',
                'textAlign': 'center',
                'margin': '10px'
            },
            multiple=False
        ),
    ]),
    html.Div(id='output-data-upload')
])

@callback(Output('output-data-upload', 'children'),
              Input('store', 'data'))
def display_table(data):
    print(data)
    if data is not None:
        df = pd.DataFrame(data)
        # Convertir les données en tableau
        table = html.Div(
            [
                dash_table.DataTable(
                    id='table',
                    page_size=10,
                    columns=[{"name": i, "id": i} for i in df.columns],
                    data=df.to_dict('records'),
                    persistence = True,
                    persisted_props = ['data'],
                    persistence_type = 'session',
                    filter_action="native",
                    sort_action="native",
                    style_table={'overflowX': 'auto'},
                    style_header={
                        'backgroundColor': 'rgb(230, 230, 230)',
                        'fontWeight': 'bold'
                    },
                    style_cell={
                        'minWidth': '0px', 'maxWidth': '180px',
                        'whiteSpace': 'normal',
                        'overflow': 'hidden',
                        'textOverflow': 'ellipsis'
                    },
                ),
            ],
        )
        return table

And my main app.py page :

import base64
import io

import dash
import pandas as pd
from dash import dcc, html, Input, Output, callback
import dash_bootstrap_components as dbc

from components import Sidebar
from pages import home, upload, chart

from dash.exceptions import PreventUpdate

# Initialiser l'application Dash avec le thème Bootstrap
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP], suppress_callback_exceptions=True)

# Créer le contenu principal de la page
content = html.Div(id='page-content', style={'marginLeft': '18rem'})

# Définir la disposition de l'application
app.layout = dbc.Container([
    dcc.Location(id='url', refresh=False),
    Sidebar.sidebar,
    content,
    # dcc.Store inside the user's current browser session
    dcc.Store(id='store', data=[], storage_type='local') # 'local' or 'session'
])

# Callback pour afficher le contenu de la page correspondante en fonction de l'URL
@app.callback(Output('page-content', 'children'),
              Input('url', 'pathname'))
def display_page(pathname):
    if pathname == '/upload':
        return upload.layout
    elif pathname == '/chart':
        return chart.layout
    elif pathname == '/':
        return home.layout
    else:
        return html.Div(['404'])

# Callback to store data
@callback(
    Output('store', 'data'),
    Input('upload-data', 'contents')
)
def store_data(contents):
    if contents is None:
        raise PreventUpdate
    content_type, content_string = contents.split(',')
    decoded = base64.b64decode(content_string)
    try:
        df = pd.read_csv(io.StringIO(decoded.decode('utf-8')))
    except Exception as e:
        print(e)
        return html.Div([
            'Erreur lors du chargement du fichier'
        ])
    return df.to_dict('records')
    # 2. or save as string like JSON
    # return dataset.to_json(orient='split')

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

hi @averger42 Adrien,
I think the data and table don’t persist after refresh because the callback in one of your pages doesn’t get triggered. Does the print(data) print anything after you uploaded the csv sheet and refreshed the page?

One way to get around this is to put a dcc.Interval into that page where the callback has store as an Input argument.

layout = html.Div([
    # trigger once a day (or less frequently) because callback only needs to be triggered once when page refreshes
    dcc.Interval(id='the-trigger', interval=1000*60*60*24) 
    html.Div([
        dcc.Upload(...)
    ])
])


@callback(
    Output('output-data-upload', 'children'),
    Input('store', 'data'),
    Input('the-trigger', 'n_intervals')
)
def display_table(data, _):
    print(data)
    # rest of the code

It works now :slight_smile: with the dcc.Interval.
Indeed it looks like the callback was not called at page change
Thanks !!

1 Like