Python Dash框架多回调绑定同一输出无触发无报错问题求助
问题根因
你遇到的回调不触发问题本质是违反了Dash的基础规则:同一个组件的同一个属性,不能同时作为多个回调的输出。你定义的两个回调都把Output('layout', 'children')作为输出目标,这种冲突下Dash不会抛出明确报错,只会出现随机失效、仅其中一个回调生效、或全部回调不触发的情况,和你使用的Dash版本、是否加State参数都没有关联。
解决方案
方案1:合并两个回调为单个回调
如果两个输入触发后需要执行的逻辑相似,可以把两个输入都放在同一个回调的Input列表中,通过callback_context判断触发源,执行对应逻辑即可:
from dash import callback_context # 合并后的单个回调 @app.callback([Output('layout', 'children')], [Input('input_database_name_SELL_vs_BUY', 'value'), Input('input_database_name', 'value')], [State('layout', 'children')]) def update_layout(se_buy_db_name, intensity_db_name, old_output): trigger_id = callback_context.triggered[0]['prop_id'].split('.')[0] if trigger_id == 'input_database_name_SELL_vs_BUY': print('1111') # 原#1回调逻辑 elif trigger_id == 'input_database_name': print('2222') # 原#2回调逻辑 return dash.no_update
方案2:拆分输出目标,避免冲突
如果你两个回调需要修改的是布局中不同的区块,不要直接输出整个layout的children,给两个业务区块分别定义独立的容器组件,每个回调仅输出对应容器的属性即可:
# 修改layout,给两个区块加独立容器 app.layout = html.Div( id='layout', children=[ # 强度图区块容器 html.Div(id='intensity_container', children=[ html.H1(id="intensity_div", children='Intensity scale plot'), html.H3(children='Plot the intensity for the day'), html.Table( [html.Tr([html.Td(html.Table( # Header [html.Tr([html.Th('Option'), html.Th('Value')])] + # Body [html.Tr([html.Td('database name'), dcc.Input(id="input_database_name", type='text', placeholder="database name", list='datalist_of_databases', min=1, debounce=False)])] + [html.Tr([html.Td('day input'), dcc.Input(id="input_day", type='text', min=1, debounce=True, list='datalist_of_tables_intensity')])] + [html.Tr([html.Td('seconds input'), dcc.Input(id="input_seconds", type='number', placeholder="input seconds interval", min=1, debounce=True, value=1)])] + [html.Button(id='button_сalculate', disabled=False, children='Calculate')] + [html.Tr([html.Td('AVG ALL =', id='avg_all'), html.Td('AVG UPDATE =', id='avg_upd')])] + [html.Tr([html.Td('AVG DEL =', id='avg_del'), html.Td('AVG INSERT =', id='avg_ins')])] )), dcc.Graph(id='intensity_graph',)]) ], style={'width': "100%"}), ]), # 买卖对比区块容器 html.Div(id='sell_buy_container', children=[ html.Table( [html.Tr([html.Td(html.Table( # Header [html.Tr([html.Th('Option'), html.Th('Value')])] + # Body [html.Tr([html.Td('database name'), dcc.Input(id="input_database_name_SELL_vs_BUY", type='text', placeholder="database name", list='datalist_of_databases', min=1, debounce=False)])] + [html.Tr([html.Td('day input'), dcc.Input(id="input_day_SELL_vs_BUY", type='text', min=1, debounce=True, list='datalist_of_tables_sell_vs_buy')])] + [html.Tr([html.Td('number of levels'), dcc.Input(id="number_of_levels_SELL_vs_BUY", type='number', placeholder="500", min=0, debounce=True, step=500, value=500)])] + [html.Tr([html.Td('step size of level'), dcc.Input(id="step_size_of_level_SELL_vs_BUY", type='number', placeholder="0.5", min=0.5, debounce=True, step=0.5, value=0.5)])] + [html.Button(id='button_calculate_SELL_vs_BUY', disabled=False, children='Calculate')] )), dcc.Graph(id='SELL_vs_BUY_graph',)]) ], style={'width': "100%"}), ]) ]) #1 回调仅输出买卖区块的children @app.callback([Output('sell_buy_container', 'children')], [Input('input_database_name_SELL_vs_BUY', 'value')], [State('sell_buy_container', 'children')]) def get_list_of_tables_sell_vs_buy(input_database_name, old_output): print('1111') if input_database_name: pass return dash.no_update #2 回调仅输出强度图区块的children @app.callback([Output('intensity_container', 'children')], [Input('input_database_name', 'value')], [State('intensity_container', 'children')]) def get_list_of_tables_intensity(input_database_name, old_output): print('2222') if input_database_name: pass return dash.no_update
补充:如果你使用Dash 2.0及以上版本,确实可以通过给Output加
allow_duplicate=True参数实现多回调绑定同一个输出,但这个方案会增加状态管理的复杂度,更推荐使用上面两种方案解决问题。
内容的提问来源于stack exchange,提问作者Vernadsky
相关产品推荐
相关产品推荐

