Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can we create data columns in Dash Table dynamically using callback with a function providing the dataframe

I am trying to create dash table on Web using Inputs. However the issue is that the data is created from database from the callback and a priori, I do not know the names of the columns unless the pandas dataframeis created using the callback function. I have checked that I getting correct data. However not able to display it. I have used multiple output options (using Dash 0.41)

My code looks as follows: ( I have not provided the details of the function which generates the pandas dataframe in the callback someFunc, as that was not important for the purpose of this Dash code TroubleShooting.

 import dash_table as dt

 def someFunc(ID, pattern_desc, file_path):

       ## do something 
      return df # pandas dataframe
#
 external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

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

 server = app.server

 app = dash.Dash(__name__)

 app.config.suppress_callback_exceptions = True
 app.css.config.serve_locally = True
 app.scripts.config.serve_locally = True


 app.layout = html.Div(
      children = [
      html.Div(
      id = 'title',
      children = appTitle,
      className = 'titleDiv'  
   ),
 html.Div(
    children = [
        html.Div(
            children = "Enter ID:",
            className = 'textDiv'
        ),
        dcc.Input(
            id = 'ID',
            type = 'text',
            value = 'ABCER1',
            size = 8),

        html.Div(
            children = "Enter Test Pattern",
            className = 'textDiv'
        ),
        dcc.Input(
            id = 'pattern_desc',
            type = 'text',
            value = 'Sample',
            size = 20),

         html.Div(
            children = "Enter File OutPut Path:",
            className = 'textDiv'
        ),
        dcc.Input(
            id = 'file_path',
            type = 'text',
            value = '',
            size = 30),

        html.Button(
            id = 'submit',
            n_clicks = 0,
            children = 'Search'
        )
    ]
),

    html.Div(
        id = 'tableDiv',
        children = dash_table.DataTable(
        id = 'table',
        style_table={'overflowX': 'scroll'},
        style_as_list_view=True,
        style_header={'backgroundColor': 'white','fontWeight': 
            'bold'},
         ),
        className = 'tableDiv'
    )
  ]
)

  # callback to update the table
  @app.callback([Output('table', 'data'),Output('table', 'columns')]
          [Input('submit', 'n_clicks')],
          [State('ID', 'value'),  State('pattern_desc', 'value'), 
        State('file_path', 'value')])
   def update_table(n_clicks, ID, pattern_desc, file_path):

         df = someFunc(ID, pattern_desc, file_path)
    mycolumns = [{'name': i, 'id': i} for i in df.columns]
        return html.Div([
                dt.DataTable(
            id='table',
            columns=mycolumns,
            data=df.to_dict("rows")
         )
        ])

So in this case the function someFunc which takes the 3 input arguments returns a pandas dataframe which can have different columns based on the inputs. Thus the app layout should display those columns as given by the output of the callback function dynamically based on the inputs. I should be getting the webpage populated with table and columns, But instead getting an error. When I run this, I am getting the data generated through the function to the file, but dash is not able to generated the table on webpage. I get the following error:

dash.exceptions.InvalidCallbackReturnValue: The callback ..table.data...table.columns.. is a multi-output. Expected the output type to be a list or tuple but got Div([DataTable(columns=[{'name': 'pattern_desc', 'id': 'pattern_desc'}, ......

Not Sure How I can achieve that. Any help will be appreciated.

like image 590
Stan Avatar asked Apr 22 '19 22:04

Stan


People also ask

How does callback work in dash?

Whenever an input property changes, the function that the callback decorator wraps will get called automatically. Dash provides this callback function with the new value of the input property as its argument, and Dash updates the property of the output component with whatever was returned by the function.

Can you have multiple callbacks in dash?

As of dash v1. 19.0 , you can create circular updates within the same callback. Circular callback chains that involve multiple callbacks are not supported.

Is @app_callback is the callback decorator?

2 . @app_callback is the callback decorator .

What is a dash table?

Dash DataTable is an interactive table component designed for viewing, editing, and exploring large datasets. This component was written from scratch in React. js specifically for the Dash community. Its API was designed to be ergonomic and its behavior is completely customizable through its properties.


1 Answers

In your Dash callback you are supposed to be returning 2 separate values to the 2 separate outputs: [Output('table', 'data'),Output('table', 'columns')]

You are returning:

return html.Div([
                dt.DataTable(
            id='table',
            columns=mycolumns,
            data=df.to_dict("rows")
         )
        ])

which is only 1 output.

Dash expects 2 return values in either a list, or a tuple like so:

return("output1" , outputVariable2)

or

return[ Html.Div("text") , "output Text 2"]

in order to fix the problem, either return 2 values in a tuple or list, or edit your output requirements so only one value is necessary.

From the looks of it you are trying to return a Div with a Datatable in it, so you could just make the following changes:

    html.Div(
        id = 'tableDiv',
        className = 'tableDiv'
    )

...

  @app.callback([Output('tableDiv', 'children')]
          [Input('submit', 'n_clicks')],
          [State('ID', 'value'),  State('pattern_desc', 'value'), 
        State('file_path', 'value')])
   def update_table(n_clicks, ID, pattern_desc, file_path):

         df = someFunc(ID, pattern_desc, file_path)
    mycolumns = [{'name': i, 'id': i} for i in df.columns]
        return html.Div([
                dt.DataTable(
            id='table',
            columns=mycolumns,
            data=df.to_dict("rows")
         )
        ])
like image 127
miked Avatar answered Sep 18 '22 15:09

miked