You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 16:06:08