Duplicate callback outputs which output id is unique

Hi,

Getting error message.

Error message:

Duplicate callback outputs

In the callback for output(s):
  mer_test.data
Output 0 (mer_test.data) is already in use. To resolve this, set `allow_duplicate=True` on duplicate outputs, or combine the outputs into one callback function, distinguishing the trigger by using `dash.callback_context` if necessary.



I had multiple input for one output and this pattern for multiple pages.
All the table id is unique with the same children.
However, keep on getting the same error message even tried to change the id name.

All the dropdown value is stored in the dcc.store.

CODE

dropdown.py

from .query import get_date


df = get_date()

year_cat = list(df['year'].unique())

corp_cat=[
    {'label': 'Corp A', 'value': 'corp1'},
    {'label': 'Corp B', 'value': 'corp2'},
    ]

top_cat = [
    {'label': '15', 'value': 15},
    {'label': '50', 'value': 50},
    {'label': '100', 'value': 100},
    ]




dropdown = dbc.Container ([
   dbc.Row([
       dbc.Col([
           html.H6('Year:'),
           dcc.Dropdown(id='year_dd', 
                        value= df['year'].max(),
                        options = [{'label':x, 'value':x} for x in year_cat],
                        searchable = True, 
                        search_value='',
                        placeholder= 'Please select ...',
                                                
                          ),
           
           ], width=3, md=3 ),
       
       
       dbc.Col([            
           html.H6('Month:'),
           
           dcc.Dropdown(id='month_dd', 
                        value= '',                         
                        searchable = True, 
                        search_value='',
                        placeholder= 'Please select ...',
                        className = 'mchanly-dropdown-list'
                         
                         ),
           html.Br(),
           
           ], width=3, md=3),
           
           
         dbc.Col([
           html.H6('Date:'),
           
             dcc.Dropdown(id='date_dd', 
                          value='',
                          searchable = True, 
                          search_value='',
                          placeholder= 'Please select ...',

                          ),
                 
             html.Br(),
             
             ], width=3, md=3),
         
         dbc.Col([
             html.H6('Corporation:'),
             
             dcc.Dropdown(id='meranly_corp_dd',
                          options=corp_cat,
                          value='tpa',
                          searchable = True, 
                          search_value='',
                          placeholder= 'Please select ...',
 
                          
                          ),
             ], width=3, md=3),
         
          
          
          
        
        ]),
    
    
    ])
    
    
 
top = dbc.Container ([
            dbc.Row([
                dbc.Col([
                    html.P('Top:'),
                    
                    dcc.Dropdown(id='top_dd',
                                 options=top_cat,
                                 value= 15,
                                 searchable = True, 
                                 search_value='',
                                 placeholder= 'Please select ...',
 
                                 ),
                    ]),
            ]),
    ])










@callback(
    Output('month_dd','options'),
    Input('year_dd', 'value')
    )


def update_dd (year_dd):
       
    year_month= df.drop_duplicates(['mthtyr'], inplace= False)
    
    relevant_month= year_month[ df['year'] == year_dd]['monthname'].values.tolist()

    month_option= [dict(label=x,value=x)for x in relevant_month]
    
    
    return month_option



@callback(
    Output('month_dd','value'),
    Input('month_dd', 'options')
    )


def default_value(latest_month):
    month_value = latest_month[-1]['value']
    
    return month_value



# #date dropdown based on filtered month
@callback(
    Output('date_dd','options'),
    Input('month_dd', 'value'),
    State('year_dd', 'value')
    )


def update_dd (month_dd, year_dd):
       
    month_date= df.drop_duplicates(['Date'], inplace= False)
    
    relevant_date= month_date[ (df['monthname'] == month_dd) & (df['year'] == year_dd)]['Date'].values.tolist()
    date_option= [dict(label=x,value=x)for x in relevant_date]
    
    
    return date_option



@callback(
    Output('date_dd','value'),
    Input('date_dd', 'options')
    )


def default_value(latest_date):
    date_value = latest_date[-1]['value']
    
    return date_value





@callback(
    Output('storedate','data'),
    Input('date_dd', 'value'),
    State('year_dd', 'value'),
    State('month_dd', 'value'),

    )


def stored_selected_value (date, year, month):
    return {'date': date, 'year': year, 'month': month} 




@callback(
    Output('storecorp','data'),
    Input('corp_dd', 'value'),

    )


def stored_selected_value (corp):
    
    return {'corp': corp} 




@callback(
    Output('storetop','data'),
    Input('top_dd', 'value'),

    )


def stored_selected_value (top):
    
    return {'top': top} 




`store.py`
import dash_core_components as dcc



# Define the dcc.Store component
store = dcc.Store(id='storedate', storage_type='session')

corp_store = dcc.Store(id='storecorp', storage_type='session')

top_store = dcc.Store(id='storetop', storage_type='session')



analysis.py

from .pages.dropdown import dropdown, top
from .pages.nav import nav
from .pages.store import store,corp_store , top_store
from .pages.bestperform import best_perform



dash.register_page(__name__,
                   path='/testting',  # '/' is home page and it represents the url
                   name=' Analysis',  # name of page, commonly used as name of link
                   title='analysis',  # title that appears on browser's tab
                   icon="bi bi-clipboard-data",
)


require_login(__name__, 
              access_level= package2
              )



def layout():
    if not current_user.is_authenticated:
        return html.Div(["Please ", dcc.Link("login", href="/login"), " to continue"])

    return dbc.Container ([
        mcht_store, 
        mcht_corp_store, 
        mcht_top_store,
           
        dbc.Row([
            dbc.Col ([
                html.H2 ('Analysis')
                ],width=12)
            ], className = ''),
        
        dbc.Row(dropdown, className = ''),
        
        dbc.Row(nav, className = ''),
        
        html.Hr(className = 'r'),
        
        dbc.Row(test1, className = ''),
        
        ])
            

best_perform.py

from .dropdown import top
from .query import get_best_perform
from .view import (
    comm_table,
    comm_header,
    comm_cell,
    cell_con1,
    comm_data_con,
    data_con1_green,
    data_con2_red,
    data_con3_orange,
    data_con4_grey,
    
    )




best_perform = dbc.Container([

    dbc.Row([
        dbc.Col([
            html.H5("BEST PERFORMANCE"),
            
            dbc.Row (top),
            
            
            dash_table.DataTable(id='mer_test', 
                                  columns=[
                                      {'name': 'ID', "id": 'id'},
                                      {'name': 'Registration Name', "id": 'registration_name'},
                                      {'name': 'Rank3', "id": 'rank3'},
                                      {'name': 'Last 3-mth', "id": 'lmtd3'},
                                      {'name': 'Rank2', "id": 'rank2'},
                                      {'name': 'Last 2-mth', "id": 'lmtd2'},
                                      {'name': 'Rank1', "id": 'rank1'},
                                      {'name': 'Last 1-mth', "id": 'lmtd'},
                                      {'name': 'This mth', "id": 'tmtd'},
                                      {'name': 'Diff(%)', "id": 'variance'},
                                      ],
                                    export_format='csv',
                                    export_headers='display',
                                    page_size= 50,
                                   
                                    style_table = comm_table(),
                                   
                                    style_header= comm_header(),
                                                                      
                                    style_cell=comm_cell(),
                                                                      
                                    style_cell_conditional = cell_con1(),
                                   
									style_data_conditional= comm_data_con() + data_con1_green() + 
                                         data_con2_red() + data_con3_orange()+ data_con4_grey(),
                                   
                                    ),
            
            ], className = '')
        ], className = ''), 
    
    html.Br(),html.Br(),html.Br()

    
    
    
    
    ])







@callback(
    [Output('mer_test', 'data')], 
    
    [
     Input('storedate','data'),
     Input('storecorp','data'),
     Input('storetop', 'data')
     ],

    )

    


def update_table1(selection, corp_selection, top_filter):
    if not selection or not corp_selection or not top_filter:
        return dash.no_update
    
    else:   
               
        selection = pd.to_datetime(selection['date'], format = '%Y-%m-%d')
        corp_selection = corp_selection['corp']
        top_filter = top_filter['top']
        
        dff = get_best_perform(selection, corp_selection)
        
        dff= dff.head(top_filter)
        
        dt = dff
        
        
        
        dt['tmtd']=dt['tmtd'].fillna(0)
        dt['lmtd']=dt['lmtd'].fillna(0)
        dt['lmtd2']=dt['lmtd2'].fillna(0)
        dt['lmtd3']=dt['lmtd3'].fillna(0)
        
        
        dt['variance'] = ((dt['tmtd'] - dt['lmtd']) / dt['lmtd']) * 100
        
        
        dt['rank2'] = dt['rank2'].fillna('-')
        dt['rank3'] = dt['rank3'].fillna('-')
        
        dt['variance'] = dt['variance'].apply(lambda x: float(x))
        dt['variance'] = dt['variance'].round()
        
        dt['tmtd']=dt['tmtd'].map('{:,.2f}'.format)
        dt['lmtd']=dt['lmtd'].map('{:,.2f}'.format)
        dt['lmtd2']=dt['lmtd2'].map('{:,.2f}'.format)
        dt['lmtd3']=dt['lmtd3'].map('{:,.2f}'.format)

       
        dt['variance']=dt['variance'].map('{:.0f}%'.format)
        dt['variance']=dt['variance'].replace('inf%', '-')
        

        columns = dt[['id', 'registration_name',
                        'rank3',
                        'lmtd3',
                        'rank2',
                        'lmtd2',
                        'rank1',
                        'lmtd',
                        'tmtd' ,
                        'variance'
                      ]]
        


        data= columns.to_dict('records')



       
        return data




I would recommend giving this a read: Duplicate Callback Outputs - Solution & API Discussion. I think your issue has something to do with the way your callbacks are set up. Be careful on which inputs and outputs you want to change

1 Like

Hello @beginof !

From Dash 2.9 you can use duplicate outputs. Check it here: Duplicate Callback Outputs | Dash for Python Documentation | Plotly

Hi,

I am testing 2 pattern and both are using the same content.


  1. app.py > analysis.py > best_perform.py
if the website is go thru like this, will have error message :

 Duplicate callback outputs
In the callback for output(s):
  mer_test.data
Output 0 (mer_test.data) is already in use.
To resolve this, set `allow_duplicate=True` on
duplicate outputs, or combine the outputs into
one callback function, distinguishing the trigger
by using `dash.callback_context` if necessary.

app.py will act as master and link multiple page, 
so the analysis.py is one of the content, 
best_perform.py is one of the page under analysis.py


  1. app.py > page.py
if the website is go thru like this, will not have the error message,
 and callback able to perform

app.py will have the content of the analysis.py (mentioned in previous pattern) 
and best_perform.py is one of the page



So. what is the reason cause the callback duplicate? Currently, only one callback.

Hello @beginof,

It looks like you have assigned this function twice update_dd, maybe try giving it a different name. Looking through, it seems that you dont have duplicate callbacks.

I had something like this (I think) when I accidentally had @app.callback instead of @callback in a multipage app or I was referring to app twice anywhere.

HI,

But my app.py is without callback. The @callback is only in best_perform.py. :joy: :joy:

app.py

app.layout = dbc.Container([
    dcc.Location(id="url"),
    
    html.Br(),     
        
    html.Div([
        
        dbc.Button(html.I(className ="bi bi-list menu-btn-img " ),
                   id="open-offcanvas", n_clicks=0, className = 'menu-btn'),
        
        dbc.Offcanvas([
            sidebar(),    
            content
            ],
            
            id="offcanvas",
            # title="Welcome to ...",
            is_open=False,
            scrollable = True,
            backdrop="static",
            className="offcanvas-style"
            ),
        ],
        style={'position': 'fixed'}, 
        ),   

    
    html.Div(id="user-status-header"),

    dbc.Row([
        dash.page_container
        ], 
        
        )
    
    ], fluid=True)




@app.callback(
    Output("offcanvas", "is_open"),
    Output("offcanvas","children"),
    Input("open-offcanvas", "n_clicks"),
    [State("offcanvas", "is_open")],
)
def toggle_offcanvas(n1, is_open):
    if n1:
        return not is_open, [sidebar(), content]
    return is_open, dash.no_update



@app.callback(
    Output("user-status-header", "children"),
    Output('url','pathname'),
    Output('open-offcanvas','style'),
    Input("url", "pathname"),
    Input({'index': ALL, 'type':'redirect'}, 'n_intervals')
)


def update_authentication_status(path, n):
    ### logout redirect
    if n:
        if not n[0]:
            return '', dash.no_update, {'display':'none'}
        else:
            return '', '/login', {'display':'none'}


    ### test if user is logged in
    if current_user.is_authenticated:
        if path in restricted_page:
              if 'access_level' in restricted_page[path]:
                   # if (VALID_USERNAME_PASSWORD[current_user.id]['role'] not in restricted_page[path]['access_level']):  
                   if (VALID_USERNAME_PASSWORD[current_user.id]['role_id'] not in restricted_page[path]['access_level']):  

                      return (dcc.Link("",id = 'logout', href="/logout", style={'position': 'fixed'})), '/permission', {'display':'initial'}
        
        
        if path == '/login':
            return (dcc.Link("" , id = 'logout', href="/logout", style={'position': 'fixed'}), '/', {'display':'initial'})
        return (dcc.Link("",id = 'logout', href="/logout", style={'position': 'fixed'}), dash.no_update, {'display':'initial'})
    
    
    else:
        ### if page is restricted, redirect to login and save path
        if path in restricted_page:
            session['url'] = path
            return dcc.Link("login", href="/login"), '/login', {'display':'none'}


    ### if path not login and logout display login link
    if current_user and path not in ['/login', '/logout']:
        return dcc.Link("login", href="/login"), dash.no_update, {'display':'initial'}

    ### if path login and logout hide links
    if path in ['/login', '/logout']:
        return '', dash.no_update, {'display':'none'}



if __name__ == "__main__":
    app.run_server(debug=True, port=1018)

Well, I just wanted to add another thought to this since you still have problems.

What I would do is

from dash import callback

and use @callback for all callbacks.

Not sure if that is going to change anything, though.

My app is already using from dash import callback, but the issue still happened. :sob: :sob: