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
相关产品推荐
相关产品推荐

