使用flask+echarts+jinja2ajax画图的方法,画一个牛奶和面包的散点图

'# 使用flask+echarts+jinja2ajax画图的方法,画一个牛奶和面包的散点图

一、背景与问题

在现代Web开发中,动态数据可视化是常见的需求。传统做法是将静态图表嵌入页面,但这种模式无法应对动态数据更新场景。本文探讨如何结合Flask后端、ECharts图表库和Jinja2模板引擎,通过Ajax实现动态散点图的绘制。

核心问题包括:

  • 如何在Flask中处理异步数据请求
  • 如何在Jinja2模板中动态渲染ECharts配置
  • 如何通过Ajax实现前后端数据交互
  • 如何在浏览器端生成动态图表

二、基本原理

Flask作为Python Web框架,负责处理HTTP请求并返回JSON数据。ECharts通过JavaScript在浏览器端渲染图表,其核心是通过配置对象定义图表类型、数据、坐标轴等。Jinja2模板引擎在服务器端将动态数据插入HTML模板,最终生成包含ECharts配置的完整HTML页面。

Ajax请求的核心是通过JavaScript的fetch API或jQuery.ajax方法,从后端获取图表数据。ECharts的配置对象需要包含:

  • 图表类型('scatter')
  • 数据数组(包含x/y坐标)
  • 坐标轴配置
  • 图例设置

三、环境准备

pip install flask flask-cors

需要安装的库:

  • Flask:Web框架
  • flask-cors:解决跨域问题
  • ECharts:JavaScript图表库(需在HTML中引入)

目录结构建议:

myapp/
│
├── app.py          # Flask主程序
├── templates/
│   └── index.html   # 前端模板
└── static/
    └── echarts.js   # ECharts库(可选)

四、核心实现

1. Flask后端路由设计

from flask import Flask, jsonify, render_template
import random

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/data')
def get_data():
    # 模拟生成牛奶/面包数据
    data = []
    for _ in range(50):
        milk = random.uniform(0, 100)
        bread = random.uniform(0, 100)
        data.append([milk, bread])
    return jsonify(data)

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

关键点:

  • / 路由返回前端模板
  • /data 路由返回JSON格式的散点图数据
  • 使用jsonify确保响应为JSON格式

2. 前端模板(Jinja2)渲染

<!DOCTYPE html>
<html>
<head>
    <title>牛奶面包散点图</title>
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js"></script>
</head>
<body>
    <div id="main" style="width: 600px;height:400px;"></div>
    <script>
        // 通过Ajax获取数据
        fetch('/data')
            .then(response => response.json())
            .then(data => {
                // 初始化ECharts
                const chart = echarts.init(document.getElementById('main'));
                
                // 构建配置对象
                const option = {
                    title: {
                        text: '牛奶 vs 面包散点图'
                    },
                    tooltip: {
                        trigger: 'axis'
                    },
                    xAxis: {
                        name: '牛奶量(单位)'
                    },
                    yAxis: {
                        name: '面包量(单位)'
                    },
                    series: [{
                        name: '散点',
                        type: 'scatter',
                        data: data
                    }]
                };
                
                // 渲染图表
                chart.setOption(option);
            });
    </script>
</body>
</html>

关键点:

  • 使用fetch API发起异步请求
  • 构建包含所有ECharts配置项的option对象
  • 通过setOption方法渲染图表

3. 增强数据处理的扩展

@app.route('/data')
def get_data():
    # 增加数据过滤和格式化
    data = []
    for _ in range(50):
        milk = random.uniform(0, 100)
        bread = random.uniform(0, 100)
        # 增加分类标签
        category = '高消费' if milk > 80 and bread > 80 else '普通消费'
        data.append({
            'x': milk,
            'y': bread,
            'category': category
        })
    return jsonify(data)

关键点:

  • 添加分类字段增强数据维度
  • 使用字典格式便于前端处理
  • 可用于后续的饼图/柱状图扩展

五、完整案例

项目结构

myapp/
├── app.py
├── templates/
│   └── index.html
└── static/
    └── echarts.js

完整代码

app.py

from flask import Flask, jsonify, render_template
import random

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/data')
def get_data():
    data = []
    for _ in range(50):
        milk = random.uniform(0, 100)
        bread = random.uniform(0, 100)
        category = '高消费' if milk > 80 and bread > 80 else '普通消费'
        data.append({
            'x': milk,
            'y': bread,
            'category': category
        })
    return jsonify(data)

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

index.html

<!DOCTYPE html>
<html>
<head>
    <title>牛奶面包散点图</title>
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js"></script>
</head>
<body>
    <div id="main" style="width: 600px;height:400px;"></div>
    <script>
        fetch('/data')
            .then(response => response.json())
            .then(data => {
                const chart = echarts.init(document.getElementById('main'));
                
                const option = {
                    title: {
                        text: '牛奶 vs 面包散点图'
                    },
                    tooltip: {
                        trigger: 'axis'
                    },
                    xAxis: {
                        name: '牛奶量(单位)',
                        type: 'value'
                    },
                    yAxis: {
                        name: '面包量(单位)',
                        type: 'value'
                    },
                    series: [{
                        name: '散点',
                        type: 'scatter',
                        data: data.map(item => [item.x, item.y]),
                        label: {
                            show: true,
                            formatter: '{b}'
                        }
                    }]
                };
                
                chart.setOption(option);
            });
    </script>
</body>
</html>

运行后访问 http://localhost:5000 即可查看散点图。

六、源码解析

1. 数据处理逻辑

data = []
for _ in range(50):
    milk = random.uniform(0, 100)
    bread = random.uniform(0, 100)
    category = '高消费' if milk > 80 and bread > 80 else '普通消费'
    data.append({
        'x': milk,
        'y': bread,
        'category': category
    })
  • 使用random.uniform生成随机数
  • 根据数值区间划分消费等级
  • 使用字典结构存储多维度数据

2. ECharts配置对象

const option = {
    title: {
        text: '牛奶 vs 面包散点图'
    },
    tooltip: {
        trigger: 'axis'
    },
    xAxis: {
        name: '牛奶量(单位)',
        type: 'value'
    },
    yAxis: {
        name: '面包量(单位)',
        type: 'value'
    },
    series: [{
        name: '散点',
        type: 'scatter',
        data: data.map(item => [item.x, item.y]),
        label: {
            show: true,
            formatter: '{b}'
        }
    }]
};
  • xAxis/yAxis配置坐标轴类型
  • series定义散点图数据
  • label显示数据点标签
  • data需要是二维数组格式

3. Ajax请求处理

fetch('/data')
    .then(response => response.json())
    .then(data => {
        const chart = echarts.init(document.getElementById('main'));
        chart.setOption(option);
    });
  • 使用fetch发起异步请求
  • 通过.json()方法解析响应
  • 创建ECharts实例并设置配置

七、进阶使用

1. 动态更新图表

setInterval(() => {
    fetch('/data')
        .then(response => response.json())
        .then(newData => {
            const chart = echarts.init(document.getElementById('main'));
            chart.setOption({
                series: [{
                    data: newData.map(item => [item.x, item.y])
                }]
            });
        });
}, 5000);

2. 增加交互功能

const option = {
    // ...原有配置
    legend: {
        data: ['高消费', '普通消费']
    },
    series: [{
        name: '高消费',
        type: 'scatter',
        data: data.filter(item => item.category === '高消费').map(item => [item.x, item.y]),
        label: { show: true, formatter: '{b}' }
    },
    {
        name: '普通消费',
        type: 'scatter',
        data: data.filter(item => item.category === '普通消费').map(item => [item.x, item.y]),
        label: { show: true, formatter: '{b}' }
    }]
};

3. 不同图表类型切换

const chart = echarts.init(document.getElementById('main'));
chart.setOption({
    series: [{
        type: 'line',
        data: data.map(item => [item.x, item.y])
    }]
});

八、性能与工程实践

1. 性能优化策略

  • 数据分页:对于大数据集使用分页处理
  • 缓存机制:对静态数据进行缓存
  • 压缩传输:使用Gzip压缩响应数据
  • 懒加载:按需加载图表数据
from flask import send_from_directory
import os

@app.route('/data/<int:page>')
def get_paginated_data(page=1):
    per_page = 10
    start = (page - 1) * per_page
    end = start + per_page
    data = [ ... ]  # 模拟大数据集
    return jsonify(data[start:end])

2. 安全考虑

  • CSRF保护:启用Flask-WTF进行表单保护
  • 数据验证:对用户输入进行校验
  • XSS防护:对用户输入进行转义处理
  • CORS配置:使用flask-cors处理跨域请求
from flask_cors import CORS

app = Flask(__name__)
CORS(app, resources={r"/*": {"origins": "*"}})

3. 方案比较

方案优点缺点
原生Flask+Ajax简单易用图表更新需要重建实例
Flask-RESTful更好的API设计需要额外学习
前端框架(Vue/React)更强的交互性学习成本较高
使用WebSocket实时更新需要处理连接管理

九、常见问题与踩坑

1. 跨域问题

错误现象:浏览器提示No 'Access-Control-Allow-Origin' header is present on the requested resource

解决方法:

  • 使用flask-cors中间件
  • 在响应头中添加Access-Control-Allow-Origin: *
from flask_cors import CORS
CORS(app, resources={r"/*": {"origins": "*"}})

2. 图表不显示

常见原因:

  • 未正确引入ECharts库
  • 配置对象格式错误
  • DOM加载时机问题

解决方法:

  • 确保引入CDN链接
  • 在window.onload或DOMContentLoaded事件中初始化
  • 检查控制台错误信息

3. 数据格式错误

错误示例:

data: data.map(item => item.x)

错误原因:ECharts需要二维数组格式

正确方式:

data: data.map(item => [item.x, item.y])

十、最佳实践

  1. 使用CORS中间件:确保跨域请求安全
  2. 数据验证:对所有用户输入进行校验
  3. 分页处理:避免一次性传输大量数据
  4. 缓存机制:对静态数据进行缓存
  5. 错误处理:添加完善的错误处理逻辑
  6. 性能监控:使用Sentry等工具监控异常
  7. 安全防护:防止XSS和CSRF攻击
  8. 图表类型选择:根据数据特征选择合适图表类型

十一、总结

本文深入探讨了如何使用Flask、ECharts和Jinja2实现动态散点图的绘制。通过分析核心原理、代码实现和常见问题,我们理解了这种技术组合的优势和适用场景。在实际开发中,这种方案适合需要动态数据更新的Web应用,如实时监控系统、数据分析仪表盘等。

需要注意的是,这种方案不适用于:

  • 静态数据展示场景
  • 需要复杂交互的高级可视化
  • 对性能要求极高的大数据处理

在实现过程中,需要特别注意跨域问题、数据格式规范以及安全防护。通过合理的架构设计和性能优化,可以构建出稳定可靠的动态数据可视化系统。

最后修改于:2026年10月03日 01:48

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日