Datatable callback data

Hi,

First post here. I have this piece of code, where I create a table and the user can select the rows, and submit the changes to SQL. Following the update to SQL via the callback I would like for:

  1. The datatable to be shown without the rows that were submitted to SQL
  2. The selection of the rows to be cleared.

When pressing submit all the rows selected are updated in SQL, but only 1 (one) row is deleted from the ‘url-table’ ‘data’.

Any suggestions for solving these will be highly appreciated.

data_list = bottle_url_list.to_dict('records')
columns = [
            dict(id='Distributor', name='Distributor'),
            dict(id='Title', name='Title'),
            dict(id='Name', name='Producer Name'),
            dict(id='Year', name='Year', type='numeric', format=Format(precision=0, scheme=Scheme.fixed)),
            dict(id='Title_url', name='Title_url'),
            dict(id='Type', name='Type'),
            dict(id='ProducerID', name='ProducerID'),

            ]
app = Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
app.title = "Vinprisen - URL Master Update"
app.layout = dbc.Container([
    html.Br(),
    html.H1("URLs with Producer"),
    html.Br(),
    
    html.Button('Submit', id='submit-val', n_clicks=0),
    html.Div(id='container-button-basic', children='Update selected columns in URL Master SQL Table'),
    html.Br(),
    html.H2("URL Data"),
    dash_table.DataTable(
        id='url-table',
        columns=columns,
        data=data_list,
        editable=True,
        filter_action="native",
        sort_action="native",
        sort_mode="multi",
        row_selectable="multi",
        column_selectable="single",
        row_deletable=True,
        selected_rows=[],
        style_table={'overflowX': 'auto'},
         style_cell={
        'overflow': 'hidden',
        'textOverflow': 'ellipsis',
        'maxWidth': 0
        },
        page_size=30,
        style_as_list_view=True,
        style_header={
        'backgroundColor': 'burgundy',
        'fontWeight': 'bold'
        },
       
        
    ),
    #html.Div(id='dd-output-container')
])

@callback(
    Output('container-button-basic', 'children'),
    Output('url-table', 'data'),
    Input('submit-val', 'n_clicks'),
    State('url-table', 'selected_rows'),
    State('url-table', 'data'),
    prevent_initial_call=True
)
def update_output(n_clicks, selected_rows, data):
    if n_clicks > 0:
        selected_rows_data = []
        update_data = pd.DataFrame(data)
        for i in selected_rows:
            title_url = data[i]['Title_url']
            year = data[i]['Year']
            producer_ID = data[i]['ProducerID']
            selected_rows_data.append({'Title_url': title_url, 'Year': year, 'Producer_ID': producer_ID})
            update_data.drop(update_data.index[i], inplace=True) 
        sql_ready_df = pd.DataFrame(selected_rows_data)
        sql_ready_df.to_sql('Url_MasterData', engine, if_exists='append', index=False)
        return f"Updated {len(selected_rows)} rows in the database.", update_data.to_dict('records')

I think the issue may be in these lines.

At the start of the function update_output() you create update_data as a dataframe of data.

Let’s say update_data (and thus data) has 10 rows, and selected_rows=[4,5,6]. Now, in the first iteration of the loop, you drop row 4. Since you are modifying update_data inplace, after this operationupdate_data now only contains 9 rows.

In the next loop iteration, you remove row 5 of the dataframe in which you just dropped a row. So you are actually removing row 6 of the original data! This is probably not what you intended. And so on in the next loop iteration.

Generally you should watch out if you are looping over something that you are also modifying inside the loop.

thank you. it was exactly what you mentioned and I needed to move the update_data outside the for loop.