You have already assigned a callback to the output

#Timeline comparables table
@app.callback(Output(‘memory-output-comparables’, ‘data’),
[Input(‘my-dropdown’, ‘value’)])
def filter_countries(deal_ids_selected):
if not deal_ids_selected:
# Return all the rows on initial load/no selected.
return comparablesList.iloc[-1:].loc[:, comparablesList.columns != ‘deal_id’].to_dict(‘rows’)

filtered = comparablesList.query('deal_id in @deal_ids_selected')
filtered = filtered.loc[:, filtered.columns != 'deal_id']

return filtered.to_dict('rows')

@app.callback(Output(‘comparablesTable’, ‘data’),
[Input(‘memory-output-comparables’, ‘data’)])
def on_data_set_table(data):
if data is None:
raise PreventUpdate

return data

#calculate delta
@app.callback(
Output(‘comparablesTable’, ‘data’),
[Input(‘comparablesTable’, ‘data_timestamp’)],
[State(‘comparablesTable’, ‘data’)])
def update_columns(timestamp, rows):
for row in rows:
try:
row[‘delta’] = float(row[‘lastPrice’]) ** 2
except:
row[‘delta’] = ‘NA’
return rows

You have already assigned a callback to the output
with ID “memory-output-timeline” and property “data”. An output can only have
a single callback function. Try combining your inputs and
callback functions together into one function.

Is there any way I can output two callbacks towards the same object? As in above… I cannot have two callbacks that assign something to comparablesTable

Only one callback per object/parameter pair is allowed. You can have one callback that handles setting the data property of memory-output-timeline and another that handles setting the value of another property of memory-output-timeline. If you have multiple paths that would dictate the data output to memory-output-timeline, you need to set some conditional logic in one callback that would return the values accordingly.

Great thanks
Are you able to elaborate on conditional logic?

I have a datatable and for the callback there it fills the table with part of an SQL database
I also want a button to add additional rows afterwards, and I need to have calculations in the datatable as well

All these things require access to the datatable ‘data’ parameter

In another forum post (can’t recall which one), I found the following code snippet.

dash.callback_context.triggered[0]['prop_id'].split('.')[0]

This returns the ID of the input that triggered the callback. Here is a snippet that shows this:

@app.callback(
    output=Output('data-container', 'children'),
    inputs=[Input('SubmitButton', 'n_clicks'),
            Input('OptionsButton', 'n_clicks')],
def poll_noaa_website_for_hilo_data(submit_clicks, options_click):
    user_click = dash.callback_context.triggered[0]['prop_id'].split('.')[0]

    callback_states=dash.callback_context.states.values()
    callback_inputs=dash.callback_context.inputs.values()

    if not user_click or user_click != 'SubmitButton':
       # do something
    else:
      # do something else

Also, dash.callback_context.states.values() provides current values of the states when the callback was triggered. dash.callback_context.inputs.values() is the equivalent for inputs. I eventually combined all dash.callback_context calls into a function for readability.

This is how it turned out

@app.callback(
    Output('comparablesTable', 'data'),
    [Input('addComparable', 'n_clicks'),
     Input('memory-output-comparables', 'data')],
    [State('comparablesTable', 'data'),
     State('comparablesTable', 'columns')])
def route_comparables(data, n_clicks, rows, columns):
    user_click = dash.callback_context.triggered[0]['prop_id'].split('.')[0]
    
    callback_states = dash.callback_context.states.values()
    callback_inputs = dash.callback_context.inputs.values()
    
    if not user_click or user_click != 'addComparable':
        if n_clicks > 0:
            rows.append({i['id']: '' for i in columns})
        return rows
    else: 
        if data is None:
            raise PreventUpdate
        return data

It works for the most part except the adding of rows below, which is quite buggy. Only adds 5 rows at a time and deletes the data input from memory-output-comparables

if n_clicks > 0:
    rows.append({i['id']: '' for i in columns})
return rows

The order of your input parameters are mismatched. Based on the ordering of @app.callback, your function definition should be def route_comparables(n_clicks, data, rows, columns).

Also, if you are not using callback_states and callback_inputs, you can remove these lines…I just included them to illustrate how to check their status should this be helpful.

Hmmm doing that prevents the data to be loaded in the first place. I.e. the memory-output-comparables input doesn’t do anything

ok…Since your entire app code is not provided I can’t provide any further input. I just know the ordering of the callback params needs to match.

Just figured it out… had the incorrect input in my if statement
needed to have memory-output-comparables

1 Like