Callback to return new columns to DataTable

Hello!

Here is my first-ever Plotly Dash application. I’ve looked through the posts and I am sure that someone would have asked this already, but I cannot find it.

I have a bunch of table merging, sorting and logic that I need to do in Python based on two inputs provided by the user.

The two inputs, below, shape the rest of how the table looks and operates. I have the DataTable loaded on the default value because I don’t know how to populate it after the inputs are selected.
I tried to populate it all just in the callback, but I’m obviously missing some knowledge.

How do I:

  1. Load the DataTable up only after both inputs have been made.
  2. Update the columns based on the logic that I have put into the callback (code below). You’ll probably see that what I have tried to do is overwrite the first instance of the DataTable with the new values, but that isn’t working. (Example output screenshot after the code)
# Product Master DataTable
product_master = dash_table.DataTable(
    id='dt_product_master',
    data=df_master_product.to_dict("records"),
    columns=[
        {'name': i, 'id': i, 'deletable': False,
            'selectable': True, 'hideable': False}
        if i == "prod_id" or i == "generic_id"
        else {'name': i, 'id': i, 'deletable': False, 'selectable': True, 'hideable': True}
        for i in df_master_product.columns],
    editable=False,
    filter_action="native",
    sort_action="native",
    sort_mode="multi",
    column_selectable="multi",
    row_selectable="multi",
    row_deletable=False,
    selected_columns=[],
    selected_rows=[],
    page_action="native",
    page_current=0,
    page_size=10,
    style_cell={
        'minWidth': 95, 'maxWidth': 95, 'width': 95
    }
)

# Return the total stock at the site.


@ app.callback(
    [Output('dt_product_master', 'data'),
     Output('dt_product_master', 'columns')],
    [Input('site_dropdown', 'value'),
     Input('store_multichoice', 'value')]
)
def update_product_datatable(site, store):
    global df_master_product, df_current_stock, df_bins

    # Subset the inventory based on the site and store selected in the drop down.
    inventory = df_current_stock.loc[:, ['site_sid', 'prod_id', 'loc_id', 'Current Stock total']].query(
        f'site_sid in {site}')

    bins = df_bins.query(f'site_sid == {site} and store_xid in {store}')

    # Merge Bin and Current Stock information together.
    bins_stock = pd.merge(bins, inventory, on=[
        'site_sid', 'loc_id', 'prod_id'])
    bins_stock = bins_stock.groupby(
        ['prod_id'], dropna=False).sum().reset_index()
    store_agg = pd.merge(df_master_product, bins_stock,
                         on='prod_id', how='right')

    data = store_agg.to_dict('records')
    columns = [
        {'name': i, 'id': i, 'deletable': False,
            'selectable': True, 'hideable': False}
        if i == "prod_id" or i == "generic_id"
        else {'name': i, 'id': i, 'deletable': False, 'selectable': True, 'hideable': True}
        for i in store_agg.columns
    ]

    return data, columns

UPDATE

I have been able to alter the dynamic columns within the callback. However, I’m returning no data. So I do not understand how the callback is calling on the function.

I’ve tested my queries to ensure that they should be returning values and they are. Can you see anything obvious with the data parameters of my DataTable?

Is it something like the framework doesn’t execute the function update_product_datatable() on each refresh? Does it only do it at the start or something?

# Product Master DataTable
product_master = dash_table.DataTable(
    id='dt_product_master',
    data=df_master_product.to_dict("records"),
    columns=[
        {'name': i, 'id': i, 'deletable': False,
            'selectable': True, 'hideable': False}
        if i == "prod_id" or i == "generic_id"
        else {'name': i, 'id': i, 'deletable': False, 'selectable': True, 'hideable': True}
        for i in df_master_product.columns],
    editable=False,
    filter_action="native",
    sort_action="native",
    sort_mode="multi",
    column_selectable="multi",
    row_selectable="multi",
    row_deletable=False,
    selected_columns=[],
    selected_rows=[],
    page_action="native",
    page_current=0,
    page_size=10,
    style_cell={
        'minWidth': 95, 'maxWidth': 95, 'width': 95
    }
)

# Return the total stock at the site.


@ app.callback(
    [Output('dt_product_master', 'data'),
     Output('dt_product_master', 'columns')],
    [Input('site_dropdown', 'value'),
     Input('store_multichoice', 'value')]
)
def update_product_datatable(site, store):
    global df_master_product, df_current_stock, df_bins

    print(
        f"The Global Heads for each of the datatables: {df_master_product.head(), df_current_stock.head(), df_bins.head()}")

    if store == None:
        return [], []

    # Subset the inventory based on the site and store selected in the drop down.
    inventory = df_current_stock.loc[:, ['site_sid', 'prod_id', 'loc_id', 'Current Stock total']].query(
        f'site_sid == {site}')

    print(f"Iventory Head: {inventory.head()}")

    bins = df_bins.query(f'site_sid == {site} and store_xid in {store}')
    print(f"bins head: {bins.head()}")

    # Merge Bin and Current Stock information together.
    bins_stock = pd.merge(bins, inventory, on=[
        'site_sid', 'loc_id', 'prod_id'])

    print(f"bins_stock head: {bins_stock.head()}")

    bins_stock = bins_stock.groupby(
        ['prod_id'], dropna=False).sum().reset_index()
    store_agg = pd.merge(df_master_product, bins_stock,
                         on='prod_id', how='right')

    store_agg.drop('bin_cost', inplace=True, axis=1)

    print(
        f"Store Product Aggregate in Master Product Callback: {store_agg.head()}")

    data = store_agg.to_dict('records')

    columns = [
        {'name': i, 'id': i, 'deletable': False,
            'selectable': True, 'hideable': False}
        if i == "prod_id" or i == "generic_id"
        else {'name': i, 'id': i, 'deletable': False, 'selectable': True, 'hideable': True}
        for i in store_agg.columns
    ]

    return data, columns

Hello @MrMadium,

Welcome to the community!

Is there any reason why you are declaring global variables in the df_master_product, etc? I think this may be conflicting with what you are trying to do. Your dataframes should not be empty during the callback.