Flask Login集成Plotly Dash时@callback正确放置与月份筛选实现
核心问题点
- 你将月份筛选的
update_graph回调嵌套在load_data函数内,导致回调仅在第一次调用load_data时注册,绑定的是第一个登录用户的数据,后续用户登录/刷新时要么复用旧数据,要么出现回调重复注册、找不到组件的错误,最终返回空白页 - 用户数据
df以闭包形式传递给回调,不会随登录用户切换更新,导致所有用户只能看到第一个登录用户的数据 - 回调
Output指定的是figure属性,但你返回的是html.Div组件,类型不匹配会触发渲染错误
修正后代码
import dash import dash_core_components as dcc import dash_html_components as html import dash_bootstrap_components as dbc from dash.dependencies import Input, Output, State import pandas as pd from app.auth import routes from app.models import User def create_dashboard(server): """Create a Plotly Dash dashboard embedded within Flask""" dash_app = dash.Dash( server=server, routes_pathname_prefix="/dashapp/", external_stylesheets=[dbc.themes.CERULEAN], # 新增配置:避免回调校验报错,因为动态生成的组件初始不在layout里 suppress_callback_exceptions=True ) dash_app.layout = html.Div( children=[ html.Div( [ html.Div( [ html.H1(id="my-header", className="text-center", style={'color': 'black', 'fontSize': 10}), ], className="col-md-12", ) ], className="row", ), html.A("Login", id="login-link", href="/login"), html.Div(id="my-div", className="text-center"), html.Button( id="submit-button-state", n_clicks=1, children="Submit", style={"display": "none"}, ), ] ) @dash_app.callback( [ Output(component_id="my-header", component_property="children"), Output(component_id="my-div", component_property="children"), Output(component_id="login-link", component_property="style") ], [Input(component_id="submit-button-state", component_property="n_clicks")], ) def get_user_name(n_clicks): """ The dashboard only loads if a user is authenticated """ if routes.get_user().is_authenticated: welcome_msg = "Welcome back, " + routes.get_user().username user_data = load_data() link_style = {"display": "none"} return welcome_msg, user_data, link_style return "Your Princess is in another castle", "", {"display": "block"} def load_data(): """生成用户专属的页面组件,不再嵌套回调""" u = routes.get_user() df = pd.DataFrame([(d.month, d.userid, d.amount) for d in u.data_for_user.all()],columns=['month', 'userid', 'amount']) # 月份选择器 month_Selector = dcc.Dropdown(id='Month_dropdown', options=[{'label': i, 'value': i} for i in df['month'].sort_values(ascending=False).unique()], placeholder="Select Month") header = html.Div([ html.H1(df['userid'].iloc[0], style={'color': 'darkblue', 'fontSize': 20}), # 注意这里Output是children,返回值是文本/组件即可 html.Div(id='object'), html.Div(month_Selector, style={'width': '200px'}) ]) return header # 回调统一注册在dash_app下,和load_data平级 @dash_app.callback( Output(component_id='object', component_property='children'), Input(component_id='Month_dropdown', component_property='value'), # 新增触发条件:页面加载时submit按钮的点击事件,确保用户已登录 State(component_id="submit-button-state", component_property="n_clicks"), prevent_initial_call=True ) def update_graph(selected_month, n_clicks): """回调执行时实时获取当前登录用户的数据""" # 先校验用户登录状态 if not routes.get_user().is_authenticated: return "" # 实时拉取当前用户的全量数据 u = routes.get_user() df = pd.DataFrame([(d.month, d.userid, d.amount) for d in u.data_for_user.all()],columns=['month', 'userid', 'amount']) # 无选中月份时返回全量总和,选中时返回对应月份总和 if not selected_month: filtered_sum = df['amount'].sum() else: filtered_sum = df[df['month'] == selected_month]['amount'].sum() # 返回和Output属性匹配的内容 return html.Div(filtered_sum, style={'color': 'darkblue', 'fontSize': 20}) return dash_app.server
关键改动说明
- 新增
suppress_callback_exceptions=True配置,解决动态生成组件不在初始layout中的回调校验报错 - 所有回调统一注册在
dash_app实例下,和create_dashboard内的其他函数平级,避免嵌套导致的重复注册、数据绑定错误 update_graph回调执行时实时拉取当前登录用户的数据,不依赖闭包缓存的旧数据,确保用户切换后数据正确- 修正了Output属性和返回值不匹配的问题,同时新增登录状态校验,避免未登录状态下回调报错
- 修正了原代码中DataFrame索引、列名拼写不一致的语法错误
内容的提问来源于stack exchange,提问作者Galat
相关产品推荐
相关产品推荐

