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

Flask+Plotly渲染异常排查:代码显示而非图表加载问题

问题排查:Flask-RESTful中Plotly图表无法渲染,仅显示JS代码

我用Python+Flask+Plotly编写了两段代码,第一段基于Flask-RESTful实现的接口无法渲染图表,页面仅显示JS代码;但第二段用普通Flask路由实现的代码能正常加载图表,请协助排查第一段代码的问题。


第一段(无法正常渲染的代码)

import pandas as pd
import plotly.graph_objects as go
from flask import Flask, request, render_template_string
from flask_restful import Api, Resource, reqparse
import json

app = Flask(__name__)
api = Api(app)

def create_scatter_trace(x, y, name, color, dash_style=None):
    return go.Scatter(
        x=x,
        y=y,
        mode="lines",
        name=name,
        line=dict(shape="spline", color=color, width=2, dash=dash_style),
        hovertemplate="%{y}",
        showlegend=False
    )

def linechart_graphs(df):
    fig_occ = go.Figure()

    fig_occ.add_trace(create_scatter_trace(df['Future Months'], df["future_data"], "future_data", "red"))

    return fig_occ

class LineChartResource(Resource):
    def get(self):
        parser = reqparse.RequestParser()
        parser.add_argument('country', type=str, help='Invalid country', required=False)
        parser.add_argument('state', type=str, help='Invalid state', required=False)
        args = parser.parse_args()

        listing_id = args['country']
        pms = args['state']

        today = pd.Timestamp.today()
        future_months = [today + pd.DateOffset(days=i * 30) for i in range(12)]
        future_data = [50 + i for i in range(12)]

        data = {
            'Future Months': [dt.strftime("%Y-%m-%d") for dt in future_months],  # Convert datetime to formatted string
            'future_data': future_data,
        }

        df = pd.DataFrame(data)

        fig = linechart_graphs(df)

        chart_div = fig.to_html()

        # HTML content with embedded Plotly chart
        html_content = f"""
        <!DOCTYPE html>
        <html>
        <head>
            <title>Trends Chart</title>
            <!-- Include Plotly.js library -->
            <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
        </head>
        <body>
            <h1>Trends Chart</h1>
            <div>
                {chart_div}
            </div>
        </body>
        </html>
        """
        return render_template_string(html_content)

api.add_resource(LineChartResource, '/line_chart')

if __name__ == '__main__':
    app.run()

第二段(正常渲染的代码)

import pandas as pd
import plotly.graph_objects as go
from flask import Flask, request, render_template_string
from flask_restful import Api, Resource, reqparse

app = Flask(__name__)

def create_scatter_trace(x, y, name, color, dash_style=None):
    return go.Scatter(
        x=x,
        y=y,
        mode="lines",
        name=name,
        line=dict(shape="spline", color=color, width=2, dash=dash_style),
        hovertemplate="%{y}",
        showlegend=False
    )

def linechart_graphs(df):
    fig_occ = go.Figure()

    fig_occ.add_trace(create_scatter_trace(df['Future Months'], df["future_data"], "future_data", "red"))

    return fig_occ

@app.route('/')
def line_chart_page():
    today = pd.Timestamp.today()
    future_months = [today + pd.DateOffset(days=i * 30) for i in range(12)]
    future_data = [50 + i for i in range(12)]

    data = {
        'Future Months': [dt.strftime("%Y-%m-%d") for dt in future_months],  # Convert datetime to formatted string
        'future_data': future_data,
    }

    df = pd.DataFrame(data)

    # Create the line chart figure using the custom layout
    fig = linechart_graphs(df)

    # Convert the Plotly figure to an HTML div element
    chart_div = fig.to_html(full_html=False)

    # HTML content with embedded Plotly chart
    html_content = f"""
    <!DOCTYPE html>
    <html>
    <head>
        <title>Trends Chart</title>
        <!-- Include Plotly.js library -->
        <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
    </head>
    <body>
        <h1>Trends Chart</h1>
        <div>
            {chart_div}
        </div>
    </body>
    </html>
    """

    return render_template_string(html_content)

if __name__ == '__main__':
    app.run()

问题根源分析

两段代码的核心差异导致了渲染结果不同:

  1. fig.to_html()参数差异

    • 第一段使用默认的fig.to_html(),会生成完整的HTML文档(包含<html>、<head>等顶层标签),导致自定义HTML模板中嵌套了完整HTML结构,浏览器解析时出现冲突,JS无法执行。
    • 第二段使用fig.to_html(full_html=False),仅生成图表所需的<div>和JS代码片段,能正确嵌入自定义模板。
  2. Flask-RESTful的响应处理特性
    Flask-RESTful的Resource默认会尝试将返回值序列化为JSON,导致HTML中的特殊字符(如<、>)被转义为实体字符(&lt;、&gt;),最终页面显示的是转义后的文本而非可执行的JS代码。
    普通Flask路由则直接返回HTML字符串,不会做额外转义。


修复方案

对第一段代码做两处关键修改:

  1. 修改fig.to_html()调用,添加full_html=False参数:
    chart_div = fig.to_html(full_html=False)
    
  2. 在返回响应时指定内容类型为text/html,避免JSON序列化转义:
    return render_template_string(html_content), 200, {'Content-Type': 'text/html'}
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 21:00:06