21

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.

Stan
  • 786
  • 1
  • 9
  • 25

2 Answers2

4

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")
         )
        ])
miked
  • 61
  • 1
  • Thanks Miked. I will try the code and keep you posted. – Stan Apr 25 '19 at 03:13
  • Mked, I tried both the versions, by keeping my code intact and returning a tuple and also as the modified code which you pasted as TableDiv, children. Nothing worked. The only thing is that this time, I am not getting any error and the output file is also generated ( which is created when somefunc function is called) , but on the hitting of submit button, the page goes off and no table is shown. – Stan Apr 25 '19 at 14:14
  • That means the error that you asked about is fixed. There must be other problems in your code somewhere else. You should consider marking this question as fixed and asking a different question. – miked Apr 25 '19 at 16:17
  • Thanks I have been able to fix it. Thanks for the help – Stan Apr 25 '19 at 23:10
0

If I've understood you correctly, then you can simply create another callback which outputs the updated value for the columns prop. You could also use a multi-output callback to update both at the same time.

@app.callback(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):
    mydata = someFunc(ID, pattern_desc, file_path)
    # here you would use the dataframe columns to create the new column values
    return new_column_values
coralvanda
  • 6,431
  • 2
  • 15
  • 25
  • Thank you coral. I used something like this, but still not working. Do I need to put anything in the layout where I defined the table: – Stan Apr 22 '19 at 22:35
  • def update_table(n_clicks, ID, pattern_desc, file_path): mydata = someFunc(ID, pattern_desc, file_path) mydata2 = mydata.to_dict("rows") mycols = [{'name': i, 'id': i} for i in mydata.columns] return mycols, mydata2 – Stan Apr 22 '19 at 22:36
  • The new callback does not have a comma after the output list in your decorator. Are you getting any errors when running the app? – coralvanda Apr 24 '19 at 22:44
  • That is a typo, which is not in the actual code. When I run the program, I get the error in the current form. – Stan Apr 24 '19 at 22:51
  • Ok, yes. I see you have the output wrapped in a `div`, but you should have it look like this: `return df.to_dict("rows"), mycolumns` – coralvanda Apr 25 '19 at 00:13
  • Thank you! I tried to do that too. But got the same error. Is my html Div in-app layout correct? – Stan Apr 25 '19 at 00:21
  • It looks like it is. You have a single component (a `div` in this case) wrapping it all, which is good. Are you getting a new error message when you try changing the callback output? – coralvanda Apr 25 '19 at 03:06