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

Jupyter Notebook中Python Dash框架无内容显示问题排查求助

问题描述

完成学校项目时,使用Dash框架构建前端仪表盘,需填充预设代码的FIXME部分并调用MongoDB的CRUD函数。完成仪表盘文件后,运行时页面无任何显示。教授提示大数据量的MongoDB可能需要数分钟加载,但运行超30分钟仍无效果。

已尝试的操作

  • 尝试卸载重装Dash框架,但受限于学校虚拟机权限无法操作;
  • 删除代码中重复函数,页面仍显示空白;
  • 添加独特HTML标题html.H1("AnimalShelter - Adam Resemius"),页面仍无显示;
  • 将AnimalShelter.py改为.ipynb格式,出现ModuleNotFoundError: No module named 'AnimalShelter'错误;
  • 断开MongoDB服务器运行,出现错误,确认数据库已正常连接;
  • 使用参数化查询仅拉取少量文档,页面仍无输出。

相关代码

AnimalShelter.py

from pymongo import MongoClient
from bson.objectid import ObjectId


# Creating class AnimalShelter

class AnimalShelter(object):
    # Enter CRUD operations here
    def __init__(self, username, password):
        try:
            self.client = MongoClient('mongodb://%s:%s@localhost:38761/AAC' % (username, password))
            self.database = self.client['AAC']
        except Exception as e:
            raise Exception("Unable to connect to database: {}".format(str(e)))

    # Implementing create functionality
    def create(self, data):
        try:
            if data is not None:
                self.database.animals.insert_one(data)
                return True
            else:
                raise ValueError("Nothing to save, because data parameter is empty")
        except Exception as e:
            raise Exception("Unable to save data: {}".format(str(e)))

    # Implementing read functionality
    def read(self, data):
        try:
            if data is not None:
                result = self.database.animals.find(search, {"_id":False})
                return list(result)
            else:
                raise ValueError("Nothing to read, because data parameter is empty")
        except Exception as e:
            raise Exception("Unable to read data: {}".format(str(e)))

    # Implementing update functionality
    def update(self, query, data):
        try:
            if query is not None and data is not None:
                result = self.database.animals.update_one(query, {"$set": data})
                return result
            else:
                raise ValueError("Unable to update data, because data query or data parameter is empty.")
        except Exception as e:
            raise Exception("Unable to update data: {}".format(str(e)))

    # Implementing delete functionality
    def delete(self, query):
        try:
            if query is not None:
                count = self.database.animals.count_documents(query)
                if count > 0:
                    self.database.animals.delete_one(query)
                    return True
                else:
                    return False, "No documents found to be deleted"
            else:
                raise ValueError("Unable to delete data, because query parameter is empty")
        except Exception as e:
            raise Exception("Unable to delete data: {}".format(str(e)))

Dash.ipynb

from jupyter_plotly_dash import JupyterDash
import dash
import dash_leaflet as dl
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
import dash_table
from dash.dependencies import Input, Output

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pymongo import MongoClient

from AnimalShelter import AnimalShelter


###########################
# Data Manipulation / Model
###########################

username = "aacuser"
password = "Password1"
shelter = AnimalShelter(username, password)


# class read method must support return of cursor object and accept projection json input
df = pd.DataFrame.from_records(shelter.read({'name': 'Lucy'}))




#########################
# Dashboard Layout / View
#########################
app = JupyterDash('SimpleExample')


app.layout = html.Div([
    html.Div(id='hidden-div', style={'display':'none'}),
    html.Center(html.B(html.H1('SNHU CS-340 Dashboard'))),
    html.Hr(),
    html.H1("AnimalShelter - Adam Resemius"),
    dash_table.DataTable(
        id='datatable-id',
        columns=[
            {"name": i, "id": i, "deletable": False, "selectable": True} for i in df.columns
        ],
        data=df.to_dict('records'),

        # Creating filter functionality:
        filter_action='custom',
        # Since there is a lot of data values, I am chosing to use a scrollwheel with pagination 
        page_size=100, #using initial size of 100 to see how it looks
        style_table={"height": "300px", "overflowY": "auto"},

        # Adding striped rows for visuals for the rows 
        style_data_conditional=[
                {
                    'if': {'row_index': 'odd'},
                    'backgroundColor': 'rgb(248, 248, 248)'
                }
            ],
            style_header={
                'backgroundColor': 'rgb(230, 230, 230)',
                'fontWeight': 'bold'
            },
            style_cell={
                'textAlign': 'left',
                'minWidth': '0px', 'maxWidth': '180px',
                'whiteSpace': 'normal'
            }
        # Adding Filters

    ),
    html.Br(),
     html.Hr(),
     html.Div(
            id='map-id',
            className='col s12 m6',
            )
])

#############################################
# Interaction Between Components / Controller
#############################################
#This callback will highlight a row on the data table when the user selects it
@app.callback(
    Output('datatable-id', 'style_data_conditional'),
    [Input('datatable-id', 'selected_columns')]
)
def update_styles(selected_columns):
    return [{
        'if': { 'column_id': i },
        'background_color': '#D2F3FF'
    } for i in selected_columns]
# Callback for the filter function
@app.callback(
    Output('datatable-id', 'derived_viewport_data'),
    Input('datatable-id', 'viewport_data'),
    Input('datatable-id', 'sort_by'),
    Input('datatable-id', 'filter_query'))  # add filter_query
def update_table(viewport_data, sort_by, filter_query):
    # convert filter query to filter dictionary
    if filter_query:
        filtering_expressions = filter_query.split(' && ')
        d = {}
        for filter_part in filtering_expressions:
            col_name, operator, filter_value = \
                filter_part.split(' ')
            if operator in ('eq', 'ne', 'lt', 'le', 'gt', 'ge'):
                # these operators match pandas series operator method names
                d[col_name] = getattr(pd.Series, operator)(filter_value)
            elif operator == 'contains':
                d[col_name] = filter_value
        filter_dict = d

        # apply filters to data
        if filter_query:
            data = df.query(' & '.join(f"{k} == '{v}'" for k, v in filter_dict.items()))
        else:
            data = df

        # apply sorting to data
        if len(sort_by):
            data = data.sort_values(
                [col['column_id'] for col in sort_by],
                ascending=[
                    col['direction'] == 'asc'
                    for col in sort_by
                ],
                inplace=False
            )

        # return viewport data
        return data.iloc[
            viewport_data['row_start']:viewport_data['row_end'],
            viewport_data['column_start']:viewport_data['column_end']
        ].to_dict('records')
    
#FIXME Add in the code for your geolocation chart
@app.callback(
    Output('map-id', "children"),
    [Input('datatable-id', "derived_viewport_data")]
)
def update_map(viewData):
    dff = pd.DataFrame.from_dict(viewData)
    
    markers = []
    for i in range(len(dff)):
        animal = dff.iloc[i]
        marker = dl.Marker(
            position=[animal["location_lat"], animal["location_long"]],
            children=[
                dl.Tooltip(animal["breed"]),
                dl.Popup([
                    html.H1("Animal Name"),
                    html.P(animal["animal_id"])
                ])
            ]
        )
        markers.append(marker)

    return dl.Map(
        style={'width': '1000px', 'height': '500px'}, 
        center=[dff["location_lat"].mean(), dff["location_long"].mean()], 
        zoom=10, 
        children=[
            dl.TileLayer(),
            *markers
        ]
    )
shelter.read({})



app

问题排查与修复方案

1. AnimalShelter类read方法致命错误

在AnimalShelter.py的read函数中,使用了未定义的变量search,需替换为传入的参数data:

# 原错误代码
result = self.database.animals.find(search, {"_id":False})
# 修复后
result = self.database.animals.find(data, {"_id":False})

该错误会导致调用read方法时直接抛出异常,无法获取任何数据,进而导致Dash页面无内容渲染。

2. Dash回调函数逻辑缺陷

(1)update_table回调修复

原回调存在未初始化变量访问、过滤逻辑错误等问题,修复后代码:

@app.callback(
    Output('datatable-id', 'derived_viewport_data'),
    [Input('datatable-id', 'filter_query'),
     Input('datatable-id', 'sort_by')],
    [dash.dependencies.State('datatable-id', 'data')]
)
def update_table(filter_query, sort_by, original_data):
    df = pd.DataFrame(original_data)
    
    # 处理过滤逻辑
    if filter_query:
        filtering_expressions = filter_query.split(' && ')
        for expr in filtering_expressions:
            col_name, operator, filter_value = expr.split(' ', 2)
            filter_value = filter_value.strip("'")
            
            if operator == 'eq':
                df = df[df[col_name] == filter_value]
            elif operator == 'ne':
                df = df[df[col_name] != filter_value]
            elif operator in ('lt', 'le', 'gt', 'ge'):
                try:
                    filter_value = float(filter_value)
                    df = df[getattr(df[col_name], operator)(filter_value)]
                except ValueError:
                    pass
            elif operator == 'contains':
                df = df[df[col_name].str.contains(filter_value, na=False)]
    
    # 处理排序逻辑
    if sort_by:
        sort_cols = [col['column_id'] for col in sort_by]
        sort_dirs = [col['direction'] == 'asc' for col in sort_by]
        df = df.sort_values(by=sort_cols, ascending=sort_dirs)
    
    return df.to_dict('records')

(2)update_map回调空数据处理

当查询无结果时,原代码会因计算均值抛出异常,需添加空数据判断:

def update_map(viewData):
    if not viewData:
        return dl.Map(
            style={'width': '1000px', 'height': '500px'},
            center=[30.2672, -97.7431],  # 默认奥斯汀坐标(对应AAC数据集)
            zoom=10,
            children=[dl.TileLayer()]
        )
    
    dff = pd.DataFrame.from_dict(viewData)
    markers = []
    
    for _, animal in dff.iterrows():
        # 检查经纬度是否有效
        if pd.notna(animal["location_lat"]) and pd.notna(animal["location_long"]):
            marker = dl.Marker(
                position=[animal["location_lat"], animal["location_long"]],
                children=[
                    dl.Tooltip(animal["breed"]),
                    dl.Popup([
                        html.H3(f"动物名称: {animal.get('name', '未知')}"),
                        html.P(f"动物ID: {animal['animal_id']}")
                    ])
                ]
            )
            markers.append(marker)

    return dl.Map(
        style={'width': '1000px', 'height': '500px'}, 
        center=[dff["location_lat"].mean(), dff["location_long"].mean()], 
        zoom=10, 
        children=[dl.TileLayer(), *markers]
    )

3. 数据加载验证

在Dash.ipynb的数据加载部分添加验证代码,确认是否获取到数据:

# 验证数据加载状态
print(f"加载到{len(df)}条数据")
print(df.head())

若输出为空,可先改用shelter.read({})获取全部数据测试,确认MongoDB查询条件是否正确。

4. JupyterDash运行配置

在代码末尾添加运行配置,开启调试模式并内嵌显示:

app.run_server(mode='inline', debug=True)

调试模式会显示详细错误信息,帮助快速定位问题。


内容的提问来源于stack exchange,提问作者adamresemius

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 17:47:34