Update a dash datatable with callbacks

#1

Hi everyone,

I have a data-table which I have to include in my layout as I need to use it as Input for one of my callbacks

I also want to update this table with another callback.
This works fine if I update the data

But since with the new release we can format the columns I would like to update the columns too as I round some numbers depending on the data itself

If I create a callback with multiple outputs (data and columns) this fails
it seems that the only way is to separate the callbacks in this case one for data and one for columns

Is this an intended behavior? If so why?
Or is it an identified bug which will be fixed in a next release?

#2

It should be possible to output both the data and the columns in a single callback.

Here’s a working, if crude example:

# -*- coding: utf-8 -*-
import dash
import dash_html_components as html

from dash.dependencies import Input, Output, State
from dash_table import DataTable

import pandas as pd

url = "https://github.com/plotly/datasets/raw/master/" "26k-consumer-complaints.csv"
df = pd.read_csv(url)

app = dash.Dash(__name__)
app.scripts.config.serve_locally = True

columns = [
    {"id": 0, "name": "Complaint ID"},
    {"id": 1, "name": "Product"},
    {"id": 2, "name": "Sub-product"},
    {"id": 3, "name": "Issue"},
    {"id": 4, "name": "Sub-issue"},
    {"id": 5, "name": "State"},
    {"id": 6, "name": "ZIP"},
    {"id": 7, "name": "code"},
    {"id": 8, "name": "Date received"},
    {"id": 9, "name": "Date sent to company"},
    {"id": 10, "name": "Company"},
    {"id": 11, "name": "Company response"},
    {"id": 12, "name": "Timely response?"},
    {"id": 13, "name": "Consumer disputed?"},
]

app.layout = html.Div([
    html.Button(
        ['Update'],
        id='btn'
    ),
    DataTable(
        id='table',
        data=[]
    )
])

@app.callback(
    [Output("table", "data"), Output('table', 'columns')],
    [Input("btn", "n_clicks")]
)
def updateTable(n_clicks):
    if n_clicks is None:
        return df.values[0:100], columns

    return df.values[100:110], columns[0:3]

if __name__ == "__main__":
    app.run_server(port=8053)

If still experiencing issues, can you provide a simple example of the problematic behavior you’re experiencing?

Thanks!

1 Like
#3

Thank you Marc-Andre

I worked on creating a code snippet to illustrate the problem I see without any dependencies:

version 1 works I am updating only the data

version 2 I am updating the data and the columns and the table is empty as no columns are showing:

for version 2 please do comment back in the 2nd Output and return of the callback function

Thanks

This is the code :

import dash
import dash_core_components as dcc 
import dash_html_components as html 
import dash_table 
from dash_table.Format import Format, Scheme, Sign, Symbol
import dash_table.FormatTemplate as FormatTemplate
from dash.dependencies import Input, Output, State
import pandas as pd
import numpy as np

app = dash.Dash(__name__)
       
app.title = 'Futures'

app.layout = html.Div(style={'marginLeft':25,'marginRight':25},
                    children=[ 
                        html.Div(className='row',children=[
                                        html.Div(className='four columns',
                                        children =[
                                        ]),
                                html.Div(className='four columns',
                                    children =[
                                                html.H6('Order Book'),
                                                html.Hr(style={'border-color':'#cb1828'}),
                                                html.Div(children=[dash_table.DataTable(id='fut-order-table',
                                                columns=[{'id':'from_mid','name':'From Mid','type':'numeric','format':FormatTemplate.percentage(4)},
                                                {'id':'price','name':'Price'},{'id':'size','name':'Size'},{'id':'cum_size','name': 'Size Total'},
                                                {'id':'size_$','name':'Size $','type':'numeric','format':FormatTemplate.money(0)},
                                                {'id':'cum_size_$','name':'Size Total $','type':'numeric','format':FormatTemplate.money(0)},
                                                {'id':'average_fill','name':'Averge Fill'},{'id':'exc','name':'Exchange'},
                                                {'id':'side','name':'side','hidden':True}],
                                                    style_table={'border': 'thin lightgrey solid'},
                                                    style_header={'backgroundColor':'lightgrey','fontWeight':'bold'},
                                                    style_cell={'textAlign':'center','width':'12%'},
                                                    style_data_conditional=[{
                                                        'if' : {'filter':  'side eq "bid"' },
                                                        'color':'blue'
                                                                }
                                                        ]+[
                                                        {
                                                        'if' : {'filter': 'side eq "ask"' },
                                                        'color':'rgb(203,24,40)'
                                                    }]+[
                                                        { 'if': {'row_index':'odd'},
                                                        'backgroundColor':'rgb(242,242,242)'}
                                                    ]+[
                                                        {'if':{'column_id':'price'},
                                                        'fontWeight':'bold',
                                                        'border': 'thin lightgrey solid'}
                                                    ]+[{'if':{'column_id':'from_mid'},
                                                        'fontWeight':'bold'}
                                                    ],
                                                    style_as_list_view=True
                                                )
                                                ]),
                                                html.Hr(style={'border-color':'#cb1828'}),
                                                html.H6('Liquidity Metrics'),
                                                html.P(id='fut-liquidity-table'),
                                                html.H6('Slippage %'),
                                                html.P(id='fut-depth-table'),
                                                html.H6('Coin Stats'),
                                                html.P(id='fut-stat-table')
                                        ]),

                                html.Div(className='four columns',
                                    children =[html.H6('Order Management'),
                                    html.Hr(style={'border-color':'#cb1828'}),
                                    html.H6('Orders'),
                                    html.Hr(style={'border-color':'#cb1828'}),
                                    html.Div(children = [
                                        html.Button(id='update_click',children =['Refresh'],
                                        style = {'margin-top' :'10px','background-color':'#cb1828','color':'#FFF'})]),
                            
                                    ])     
                        ]),
                    ])
@app.callback(
            Output('fut-order-table','data'),#, Output('fut-order-table','columns')],
            [Input('update_click','n_clicks')],)

def update_page(n_clicks):
    step = .0001
    # order table columns
    r = int(np.ceil(-np.log(step)/np.log(10)))-2
    columns_ob=[{'id':'from_mid','name':'From Mid','type':'numeric','format':FormatTemplate.percentage(r)},
                {'id':'price','name':'Price'},{'id':'size','name':'Size'},{'id':'cum_size','name': 'Size Total'},
                {'id':'size_$','name':'Size $','type':'numeric','format':FormatTemplate.money(0)},
                {'id':'cum_size_$','name':'Size Total $','type':'numeric','format':FormatTemplate.money(0)},
                {'id':'average_fill','name':'Averge Fill'},{'id':'exc','name':'Ex'},
                {'id':'side','name':'side','hidden':True}],
    # order table data
    df =  pd.DataFrame(np.random.randn(24,9),columns=['from_mid','price','size','cum_size','size_$','cum_size_$','average_fill','exc','side'])
    data_ob = df.to_dict('rows')
    return (data_ob)#,columns_ob) 

if __name__ == '__main__' :
    app.run_server(port=8055,debug=True)
#4

I suspect this related to the FormatTemplate objects being not Json

#5

Any solution for this one already?

#6

If the problem is the datatable why not just storing your data in another hidden div? This way you don’t have to deal with the table itself but just a json stored in an html component, update it via first callback and let this div be the input that fires callbacks for other components in your page https://dash.plot.ly/sharing-data-between-callbacks.

#8

@Marc-Andre
This totally works

the problem was a coma at the end of my columns definition
columns = [{},{},{}] ,
which had made my columns variable a tuple instead of a list
so columns are updatable by callbacks including FormatingTemplate

The new debuging tool of Dash == 0.43.0 made this solution possible
Thanks

1 Like
#9

Hello Marc-Andre,
it seems to me that I’ve similar problem like mentioned here.
I try to update the table in general by the value selected in the dropdown.
In the layout, I define the table in this way:
html.Div(dtab.DataTable
(id = ‘exch_table’,
data=[]
),

The call back is like:
@app.callback([Output(‘exch_table’, ‘data’), Output(‘exch_table’, ‘columns’)],
[Input(‘exch_dropdown’, ‘value’)])

def update_table(selected_value):
df = pd.read_sql(selected_value, conn)
columns=[{“name”: i, “id”: i} for i in df.columns],
data=df.to_dict(‘records’)
return data, columns

However, the outputs are no dash.Output.

Any feedback is very appreciated.
Kind regards,
Vaclav

#10

Hi, this is my solution:
as the Das h v39 enables multiple out put (update is necessary) the layout is:
html.Div(dtab.DataTable(id = ‘exch_table’)

The call back is:
def update_table(selected_value):
df = pd.read_sql(selected_value, conn)
columns=[{“name”: i, “id”: i} for i in df.columns]
data=df.to_dict(‘records’)
return columns, data

It should work. The dash datatable is being updated based on the new pandas dataframe.

Good luck.
Vaclav