在Wagtail首页自定义Plotly图表:models.py中实现方式是否正确?
问题:Wagtail首页自定义Plotly图表的实现方式是否正确?能否在models.py中完成?
我正尝试在Wagtail首页制作自定义Plotly图表,目前已通过重写Page模型修改返回给模板的上下文。请问这种实现方式是否正确?能否在models.py中完成此操作?
以下是我的代码:
from django.db import models from wagtail.models import Page from wagtail.fields import RichTextField from wagtail.admin.panels import FieldPanel import psycopg2 from psycopg2 import sql import pandas as pd import plotly.graph_objs as go from plotly.offline import plot class CasPage(Page): body = RichTextField(blank=True) content_panels = Page.content_panels + [ FieldPanel('body'), ] def get_connection(self): try: return psycopg2.connect( database="xxxx", user="xxxx", password="xxxx", host="xxxxxxxxxxxxx", port=xxxxx, ) except: return False conn = get_connection() cursor = conn.cursor() strquery = (f'''SELECT t.datum, t.grwaarde - LAG(t.grwaarde,1) OVER (ORDER BY datum) AS gebruiktgas FROM XXX ''') data = pd.read_sql(strquery, conn) fig1 = go.Figure( data = data, layout=go.Layout( title="Gas-verbruik", yaxis_title="aantal M3") ) output = plotly.plot(fig1, output_type='div', include_plotlyjs=False) def get_context(self, request): context = super(CasPage, self).get_context(request) context['output'] = output return context
解答
你的实现方式存在明显问题,不应该在models.py中这么写,具体问题和优化方案如下:
核心问题
类级别代码执行时机错误:你把数据库连接、数据查询、图表生成的代码直接写在类的顶层,这些代码会在Django服务启动时就执行一次,而不是在用户访问页面时动态执行。这会导致:
- 数据库连接长期占用,无法随请求释放,容易引发连接池耗尽问题
- 图表数据永远是服务启动时的旧数据,不会实时更新
- 若启动时数据库不可用,整个Wagtail服务会直接报错
代码调用错误:你导入的是
from plotly.offline import plot,但后面用了plotly.plot,会抛出NameError异常。异常处理缺失:
get_connection返回False后,后续直接调用conn.cursor()会崩溃,没有处理连接失败的逻辑。
正确的实现方式
可以在models.py的get_context方法内完成,但必须把所有动态逻辑放在方法内部(仅在请求时执行),优化后的代码如下:
from django.db import models from wagtail.models import Page from wagtail.fields import RichTextField from wagtail.admin.panels import FieldPanel import psycopg2 import pandas as pd import plotly.graph_objs as go from plotly.offline import plot class CasPage(Page): body = RichTextField(blank=True) content_panels = Page.content_panels + [ FieldPanel('body'), ] def get_context(self, request): context = super().get_context(request) # 所有动态逻辑放在get_context内,每次请求时执行 try: # 建立数据库连接 conn = psycopg2.connect( database="xxxx", user="xxxx", password="xxxx", host="xxxxxxxxxxxxx", port=xxxxx, ) # 执行SQL查询 strquery = '''SELECT t.datum, t.grwaarde - LAG(t.grwaarde,1) OVER (ORDER BY datum) AS gebruiktgas FROM XXX''' data = pd.read_sql(strquery, conn) # 关闭连接,避免资源泄漏 conn.close() # 生成Plotly图表(需指定数据格式,原代码直接传data会报错) fig1 = go.Figure( data=[go.Scatter(x=data['datum'], y=data['gebruiktgas'])], layout=go.Layout( title="Gas-verbruik", yaxis_title="aantal M3" ) ) # 生成可嵌入的HTML代码 output = plot(fig1, output_type='div', include_plotlyjs=False) context['output'] = output except Exception as e: # 捕获异常,避免页面崩溃,返回错误提示 context['output'] = f"<p>图表加载失败:{str(e)}</p>" return context
额外建议
- 使用Django内置数据库连接:如果你的Wagtail项目已经配置PostgreSQL作为数据库,建议用
django.db.connection执行查询,无需手动管理psycopg2连接,更安全可靠。 - 缓存图表结果:若数据不是实时更新的,可通过Django缓存框架缓存生成的
output内容,减少数据库查询和图表生成的性能开销。 - 前端引入Plotly.js:因为设置了
include_plotlyjs=False,需要在模板中手动引入Plotly.js的CDN或本地文件,否则图表无法渲染。
内容的提问来源于stack exchange,提问作者HzM74
相关产品推荐
相关产品推荐

