In Dash Plotly how do I upload CSV with dcc.Upload to store using dcc.Store and then display table?

Viewed 21

haven’t found a good answer to this issue I am having. Please help!

My goal is to upload CSV via dcc.Upload, store it in dcc.Store, and then display table.

I am taking the code from dcc.Upload tutorial here and inserting a dcc.Store component. The goal is to have two callbacks. The first one would have upload data as input and store as output. The second callback would take store as input and output to a html.Div.

See the code below that is not working. Please help!

import base64
import datetime
import io

import dash
from dash.dependencies import Input, Output, State
from dash import dcc, html, dash_table

import pandas as pd

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

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([
    dcc.Upload(
        id='upload-data',
        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'
        },
        # Allow multiple files to be uploaded
        multiple=True
    ),
    dcc.Store(id='store'),
    html.Div(id='output-data-upload'),
])


#Goal here is to store the csv from upload into dcc.Store
@app.callback(Output('store', 'data'),
              Input('upload-data', 'contents'),
              State('upload-data', 'filename'),
              State('upload-data', 'last_modified'))

def update_output(contents, list_of_names, list_of_dates):
    content_type, content_string = contents.split(',')

    decoded = base64.b64decode(content_string)

    df = pd.read_csv(io.StringIO(decoded.decode('utf-8')))

    return df.to_json(date_format='iso', orient='split') #I have to convert to json format to store in dcc.Store


#Goal here is to then extract dataframe from dcc.store by reading json and output a table
@app.callback(
    Output('output-data-upload', 'children'),
    Input('store', 'data')
)

def output_from_store(stored_data):
    df = pd.read_json(stored_data, orient='split')

    return html.Div([


        dash_table.DataTable(
            df.to_dict('records'),
            [{'name': i, 'id': i} for i in df.columns]
        ),

        html.Hr(),  # horizontal line


    ])



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

I followed the dcc.Upload tutorial and tried inserting the dcc.Store into it.

1 Answers

Your code was missing a few adjustments to run as expected.

First, as sometimes the values can be inside a list you need to get only the value inside the list, in this case, it's in index 0.

content_string = contents[0].split(',')

Second, to avoid some warnings I have added a condition that will not run the element if the upload values are empty.

if [contents, list_of_names, list_of_dates] == [None, None, None]:
    return dash.no_update

You can also use it in your callback declaration:
prevent_initial_call=True

And as a tip, I would suggest you use debug=True n your development environment, so you can have better feedback about what's going wrong, making it easier to debug your code to fix the error

app.run_server(debug=True)

Full code based on your example:

import base64
import datetime
import io

import dash
from dash.dependencies import Input, Output, State
from dash import dcc, html, dash_table

import pandas as pd

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

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([
    dcc.Upload(
        id='upload-data',
        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'
        },
        # Allow multiple files to be uploaded
        multiple=True
    ),
    dcc.Store(id='store'),
    html.Div(id='output-data-upload'),
])

import dash
#Goal here is to store the csv from upload into dcc.Store
@app.callback(Output('store', 'data'),
              Input('upload-data', 'contents'),
              State('upload-data', 'filename'),
              State('upload-data', 'last_modified'),
              prevent_initial_call=True)

def update_output(contents, list_of_names, list_of_dates):

    if [contents, list_of_names, list_of_dates] == [None, None, None]:
        return dash.no_update
    
    content_type, content_string = contents[0].split(',')

    decoded = base64.b64decode(content_string)

    df = pd.read_csv(io.StringIO(decoded.decode('utf-8')))

    return df.to_json(date_format='iso', orient='split') #I have to convert to json format to store in dcc.Store


#Goal here is to then extract dataframe from dcc.store by reading json and output a table
@app.callback(
    Output('output-data-upload', 'children'),
    Input('store', 'data'),
              prevent_initial_call=True
)

def output_from_store(stored_data):
    
    df = pd.read_json(stored_data, orient='split')

    return html.Div([


        dash_table.DataTable(
            df.to_dict('records'),
            [{'name': i, 'id': i} for i in df.columns]
        ),

        html.Hr(),  # horizontal line


    ])



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

I hope it can be helpful for you.

Regards, Leonardo

Related