Announcing Dash Bio 1.0.0 🎉 : a one-stop-shop for bioinformatics and drug development visualizations.

Duplicate callback error: Returning different DataTables from a dropdown menu selection

I am trying to do something so simple that I guess I am way off track:
To use a dropdown menu callback to select between two (or more) DataTables populated from entirely separate dataframes/sources.

Perhaps incorrectly, I initially generate each DataTable separately outside of the layout. This has worked for me when selecting between different figures (from different datasets) for the same Div.

I then select the relevant datatable basis the dropdown selection and the simple function: def update_data_table(value)

Note, I wondered if I was duplicating the DataTable operation by first using it outside the layout as dashtable.DataTable; and then using ddk.DataTable inside the design kit wrapper.

However, I return the same “Duplicate Callback error” if I replace ddk.DataTable with:
html.Div(id = ‘return_datatable’)

The errors returned are as follows. one for each part of the same DataTable output:
:rescue_worker_helmet:
Duplicate callback outputs
In the callback for output(s):
datatable_1.data
datatable_1.columns
Output 1 (datatable_1.columns) is already in use.
Any given output can only have one callback that sets it.
To resolve this situation, try combining these into
one callback function, distinguishing the trigger
by using dash.callback_context if necessary.
:rescue_worker_helmet:
Duplicate callback outputs
In the callback for output(s):
datatable_1.data
datatable_1.columns
Output 0 (datatable_1.data) is already in use.
Any given output can only have one callback that sets it.
To resolve this situation, try combining these into
one callback function, distinguishing the trigger
by using dash.callback_context if necessary.

Generalized code is as follows:

data_cols_1 = df_1.columns
d_columns_1 = [{‘name’: x, ‘id’: x} for x in data_cols_1]

d_table_1 = dash_table.DataTable(
# Set up the columns and data
columns=d_columns_1,
data=d_columns_1.to_dict(‘records’),
cell_selectable=False,
# Set up sort, filter and pagination
sort_action=‘native’,
filter_action=‘native’,
page_action=‘native’,
page_current=0,
page_size=12,
)

data_cols_2 = df_2.columns
d_columns_2 = [{‘name’: x, ‘id’: x} for x in data_cols_2]

d_table_2 = dash_table.DataTable(
# Set up the columns and data
columns=d_columns_2,
data=d_columns_2.to_dict(‘records’),
cell_selectable=False,
# Set up sort, filter and pagination
sort_action=‘native’,
filter_action=‘native’,
page_action=‘native’,
page_current=0,
page_size=12,
)

def serve_layout():
return [
ddk.Row(children=[
ddk.Card(children=[
ddk.CardHeader(children=[
dcc.Dropdown(
id=‘table-dropdown’,
options=[{‘label’: i, ‘value’: i}
for i in [‘Option_1’, ‘Option_2’]],
value=‘Option_1’
)
]),
ddk.DataTable(id=‘return_datatable’)
]),
]),
]

@app.callback([Output(‘return_datatable’, ‘data’), Output(‘return_datatable’, ‘columns’)],
[Input(‘table-dropdown’, ‘value’)])
def update_data_table(value):
if value == ‘Option_1’:
table = d_table_1
else:
table = d_table_2
return table

Hi,

From your code snippet, I would expect to see an error related to the fact that you are returning a component (d_table_1), while the callback expects you to return a tuple/list with the data and columns (so two lists)… However your problem seems unrelated to that. Do you have by any chance any other callbacks using the same output?

1 Like

Hi, thank you very much for the quick response.

I do not have another callback using the same output. This was the common explanation I could find online for the Duplicate Callback error, so I checked that initially.

To your point about returning a component when I should be returning a tuple/list. Do you think I could be throwing these errors because my entire approach is incorrect?

To that point, how would you select between two different dataframes to create entirely independent DataTables to fit the same Div on a layout ?

I considered concatenating the dataframes and adding a binary variable to filter each one, but this seemed clunky…

To that point, how would you select between two different dataframes to create entirely independent DataTables to fit the same Div on a layout ?

There are two different ways of doing it. The first one is to return the new data and columns as you did, but then you must return the right objects. It could be:

@app.callback(
    [
        Output(‘return_datatable’, ‘data’), 
        Output(‘return_datatable’, ‘columns’)
   ],
   [Input(‘table-dropdown’, ‘value’)]
)
def update_data_table(value):
    if value == ‘Option_1’:
         df = df_1
    else:
         df = df_2
return [
    df.to_dict(‘records’), #data
    [{‘name’: x, ‘id’: x} for x in df.columns] # columns
]   

The second approach would be to return the entire table component as single child of a div (as you mentioned). There are many disadvantages of this one, and I would use it only if you want to replace more than just the data in the table: for example, if it was a nested structure with a heading, table and some other components depending all on the selection.

That said, I still don’t see how this is related to duplicated callbacks.

@jlfsjunior thank you so much!

This concept was the basis for my non-generalized solution to the callback function.

I simplified it a bit

@app.callback(Output(‘return_datatable’, ‘children’),
              [Input(‘table-dropdown’, ‘value’)],)
def update_data_table(value):
    if value == ‘Option_1’:
        data = df_1.to_dict('records')
        columns = [{‘name’: x, ‘id’: x} for x in df_1.columns]
    else:
        data = df_2.to_dict('records')
        columns = [{‘name’: x, ‘id’: x} for x in df_2.columns]
    table = dash_table.DataTable(
                # Set up the columns and data
                columns=columns,
                data=data,
                cell_selectable=False,
                # Set up sort, filter and pagination
                sort_action=‘native’,
                filter_action=‘native’,
                page_action=‘native’,
                page_current=0,
                page_size=12,
        )
    return table

However, I am still raising duplicate callback error from the above function. I have deleted the code generating multiple DataTables and only do it within the callback function.

you were correct, any ideas ?

The new duplicate callback error:
image

Final update:
The issue was not in the generalized script posted above, but rather the import statements between pages.
I had a kind of circular reference generated by an import statement:

from page_name import *

Not entirely sure how this generated a loop however, as I never duplicated the callback statement. However, breaking the import link and only importing named elements:

from page_name import element_1, element_2 .... element_n

This fixed the Duplicate callback error.

Final note:
I also did the following, which didn’t fix the problem: made a new virtualenv and imported from pip only rather than conda-forge; generated unique dropdown IDs for dcc.Dropdown components replicated on multiple pages (which otherwise ran fine when used for graphs etc but not DataTable)