Complex UI with Dash and duplicate callback output errors

Hello,
We are developping a dataviz application with Dash and our UI has many interactions so that we end up with a huge Callback with many ‘case’. We find impossible to split our callback into smaller ones mainly because of the “duplicate callback output error”.
Is there any way (and good practice) to get around this and have a better structure code ?

Regards.

2 Likes

Hi @FredGR, welcome to the forums.

You could use @Emil’s dash-extensions:

Hi, I think it was also implemented very recently or planned to be part of standard dash no? ,(Multiple callback targeting the same output)

See Duplicate Callback Outputs - Solution & API Discussion - #21 by chriddyp

Yes, but it’s not yet available @jcuypers :crossed_fingers:

If you dont want to wait, you can do something like this (if multiplexer is something you dont want to use):

Should be out in a week or so :slightly_smiling_face:

As the others have mentioned, we’ll allow multiple callbacks targeting the same output soon.

In the meantime, I’d recommend breaking your long callback out into multiple functions. Here’s an example of how to organize your code:

@app.callback(
    Output('graph', 'figure'),
    Input('reset', 'n_clicks'),
    Input('draw', 'n_clicks'),
    prevent_initial_call=True
)
def update_graph(b1, b2):
    triggered_id = callback_context.triggered[0]['prop_id']
    if 'reset.n_clicks' == triggered_id:
         return reset_graph()
    elif 'draw.n_clicks' == triggered_id:
         return draw_graph()

def draw_graph():
    df = px.data.iris()
    return px.scatter(df, x=df.columns[0], y=df.columns[1])

def reset_graph():
    return go.Figure()

You will still have the if/elif statements, but don’t include any more code within those statements than necessary… just call a separate function. This will clean up your code a fair bit. Then, if you really want to remove the if/elif, you’ll be able to refactor your code to this in a week or so:

app = Dash(__name__)

app.layout = html.Div([
    html.Button('Draw Graph', id='draw'),
    html.Button('Reset Graph', id='reset'),
    dcc.Graph(id='graph')
])


@app.callback(
    Output('graph', 'figure'),
    Input('draw', 'n_clicks'),
    prevent_initial_call=True
)
def draw_graph(_):
    df = px.data.iris()
    return px.scatter(df, x=df.columns[0], y=df.columns[1])

@app.callback(
    Output('graph', 'figure'),
    Input('reset', 'n_clicks'),
    prevent_initial_call=True
)
def reset_graph(_):
    return go.Figure()


app.run_server(debug=True)
2 Likes