Search code examples
pythondataframeplotly-dashplotly-python

Dash datatable calculations using active cell callback trigger to update the source datatable


I have a data table whose goal is to perform excel like computations and display results in a column within the same table.

Using the active cell trigger I am able to perform the computation in pandas by getting the table data dict but I am however having trouble with updating the datatable in question. I have been getting the nonetype error when the callback attempts to update the datatable. Below is my datatable and callback code any help will be appreciated.

dash_table.DataTable(
                            editable=True,  
                            row_deletable=True, 
                            sort_action="native", 
                            sort_mode="single",  
                            # filter_action="native", 
                            column_selectable="single",
                            row_selectable="multi",
                            page_action="none",  .
                            style_table={
                                "margin": "auto%",
                                "width": "80%",
                                "overflowY": "auto",
                            },
                            id="request_table",
                            data=df.to_dict("records"),
                            columns=[
                                {
                                    "name": "Item_ID",
                                    "id": "Item_ID",
                                    "deletable": False,
                                    "renamable": False,
                                    "editable": True,
                                },
                                {
                                    "name": "Price",
                                    "id": "Price",
                                    "deletable": True,
                                    "renamable": True,
                                    "editable": True,
                                    "type": "numeric",
                                },
                                {
                                    "name": "Quantity",
                                    "id": "Quantity",
                                    "deletable": False,
                                    "renamable": False,
                                    "editable": True,
                                    "type": "numeric",
                                },
                                {
                                    "name": "Line Total",
                                    "id": "Line_Total",
                                    "deletable": False,
                                    "renamable": False,
                                    "editable": False,
                                    "type": "numeric",
                                },
                            ]
                            # active_cell=initial_active_cell,
                        ),
@app.callback(
    [Output("request_table", "data")],
    [Output("request_table", "columns")],
    Input("request_table", "active_cell"),
    State("request_table", "data"),
)
def getActiveCell(active_cell, rows):
    if active_cell:
        datacalc = datax.from_dict(rows)
        datacalc["Line_Total"] = datacalc["Price"] * datacalc["Quantity"]
        data = datacalc.to_dict("records")

        # data = list(itertools.chain(datacalc))

        return data

Solution

  • I modified an example from the dash document. The callback rewrite the data using the defined rule and it can be used as the output. Just need to be cautious that the row values would become string so data type needs change before calculation.

    from dash import Dash, dash_table, html
    from dash.dependencies import Input, Output
    
    app = Dash(__name__)
    
    app.layout = html.Div([
        dash_table.DataTable(
            id='editing-table-data',
            columns=[{
                'name': 'Column {}'.format(i),
                'id': 'column-{}'.format(i)
            } for i in range(1, 5)],
            data=[
                {'column-{}'.format(i): i for i in range(1, 5)}
                for j in range(5)
            ],
            editable=True
        )
    ])
    
    @app.callback(Output('editing-table-data', 'data'),
                  Input('editing-table-data', 'data'),
                  prevent_initial_call=True)
    def display_output(rows):
        for row in rows:
            # update the data using a newly defined rule
            if all([cell != '' for cell in row.values()]):
                row['column-2'] = int(row['column-1']) * 10 + 1
    
        return rows
    
    if __name__ == '__main__':
        app.run_server(debug=True)