使用python-flask和echarts完成數據可視化
一、工具介紹
flask是一個Python實現的Web開發微框架,類似的還有django/dash等。這篇文章是一個講述如何用它實現數據可視化的詳細教程。
echarts是一個純JavaScript的數據可視化圖標庫,兼容絕大部分的瀏覽器。
本文利用Python Flask框架與echarts相結合,展示了一個從建立數據庫,到Python封裝數據庫信息為json格式數據,前端接受json格式數據並執行響應,最終展示數據的到頁面的一個完整的流程。以下是項目的文件結構:
項目結構
Goods文件夾
|-static
||-js
|||-jquery.min.js
|||-echarts.js
||-css
|||-style.css
|-templates html在該文件夾下
||-index.html
|-app.py
二、代碼講解
1. sqlalchemy
使用 SQLAlchemy 的首要原因是,它將你的代碼從底層數據庫及其相關的 SQL 特性中抽象出來。下面是sqlalchemy的數據庫鏈接與查詢(注意需要提前構建好基類才能進行查詢)
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, INTEGER, String
from sqlalchemy.orm import sessionmaker
HOST_NAME = 'localhost' # 數據庫所在服務器ip,因為我是本地數據庫所以這里是127.0.0.1
HOST_PORT = '3306' # 數據庫端口
DATABASE_NAME = 'flasktest' # 數據庫名
USER_NAME = 'root' # 鏈接數據的用戶名
PWD = 'root' # 鏈接數據庫的密碼
DB_URI = 'mysql+pymysql://{0}:{1}@{2}:{3}/{4}?charset=utf8'.format(USER_NAME,PWD,HOST_NAME,HOST_PORT,DATABASE_NAME)
# 創建數據庫連接
engine = create_engine(DB_URI)
# 操作數據庫基類
Base = declarative_base(engine)
class UserModule(Base):
"""
創建一個用戶的數據模型
"""
__tablename__ = 'flasktest'
id = Column(INTEGER, primary_key=True, autoincrement=True, comment='用戶id')
Goods_name = Column(String(30), nullable=False, unique=True, comment='商品名')
Goods_sales_volume = Column(INTEGER, nullable=False, comment='產量')
Goods_inventory = Column(INTEGER, nullable=False, comment='銷量')
def __repr__(self):
return 'User(id={id}, Goods_name={Goods_name}, Goods_sales_volume={Goods_sales_volume}, money={Goods_inventory})'.format(
id=self.id, Goods_name=self.Goods_name, Goods_sales_volume=self.Goods_sales_volume, Goods_inventory=self.Goods_inventory)
def get_db():
Session = sessionmaker(bind=engine)
session = Session() # 實例化了一個會話(或叫事務),之后的所有操作都是基於這個對象的
inventQuery = session.query(UserModule.Goods_inventory).all()
scores = [c[0] for c in inventQuery]
salesQuery = session.query(UserModule.Goods_sales_volume).all()
money = [c[0] for c in salesQuery]
nameQuery = session.query(UserModule.Goods_name).all()
names = [c[0] for c in nameQuery]
return names, scores, money
2. flask框架
render_template
返回對應的html鏈接。
兩個viewdata路由用於分別對兩個表格傳輸數據,完成后端對前端的數據回傳。
jsonify
將dict數據打包成json格式,方便前端讀取。
from flask import Flask, request
from flask import render_template
from flask import jsonify
@app.route('/')
def index():
return render_template("index.html")
# 前端會到這個url請求數據,通過ajax動態刷新數
@app.route("/viewdata", methods=["GET"])
def viewdata():
if request.method == "GET":
names, scores, money = get_db()
return jsonify(name = [x for x in names],
score = [x for x in scores],
money = [x for x in money])
@app.route("/viewdata2", methods=["GET"]) # url匹配ajax
def viewdata2():
if request.method == "GET":
names, scores, money = get_db()
return jsonify(name = [x for x in names],
score = [x for x in money],
money = [x for x in scores])
if __name__ == '__main__':
app.run(debug=True, port=5000) #port:端口,默認端口為5000,訪問地址為localhost:5000/
3. index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>ECharts3 Ajax</title>
<script src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
<script src="{{ url_for('static', filename='echarts.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" >
</head>
<body>
<!--為ECharts准備一個具備大小(寬高)的Dom-->
<div id="main" style="height:500px;border:1px solid #ccc;padding:10px;"></div>
<div id="example" style="height:500px;border:1px solid #ccc;padding:10px;"></div>
<div id="view"></div>
<script type="text/javascript">
var myChart = echarts.init(document.getElementById('main'));
var exChart = echarts.init(document.getElementById('example'));
// 顯示標題,圖例和空的坐標軸
myChart.setOption({
title: {
text: '異步數據加載示例'
},
tooltip: {},
legend: {
data:['score','money']
},
xAxis: {
data: []
},
yAxis: {},
series: [{
name: 'score',
type: 'line',
data: []
},{
name: 'money',
type: 'bar',
data: []
}]
});
myChart.showLoading(); // 顯示加載動畫
// 從url請求數據,異步加載
$.get('/viewdata').done(function (data) {
myChart.hideLoading(); // 隱藏加載動畫
// 填入數據
myChart.setOption({
xAxis: {
data: data.name
},
series: [{
name: 'score', // 根據名字對應到相應的系列
data: data.score.map(parseFloat) // 轉化為數字(注意map)
},{
name: 'money',
data: data.money.map(parseFloat)
}]
});
});
// 第二個表格
exChart.setOption({
title: {
text: '異步數據加載示例'
},
tooltip: {},
legend: {
data:['score','money']
},
xAxis: {
data: []
},
yAxis: {},
series: [{
name: 'score',
type: 'line',
data: []
},{
name: 'money',
type: 'bar',
data: []
}]
});
exChart.showLoading(); // 顯示加載動畫
// 異步加載數據
$.get('/viewdata2').done(function (data) {
exChart.hideLoading(); // 隱藏加載動畫
// 填入數據
exChart.setOption({
xAxis: {
data: data.name
},
series: [{
name: 'score', // 根據名字對應到相應的系列
data: data.score.map(parseFloat) // 轉化為數字(注意map)
},{
name: 'money',
data: data.money.map(parseFloat)
}]
});
});
</script>
</body>
</html>
代碼有點多,選重點部分講。第一部分如下,主要是讀取了div容器,然后設置好不需要動態加載的圖表的橫縱坐標軸等信息。
<script type="text/javascript">
var myChart = echarts.init(document.getElementById('main'));
// 顯示標題,圖例和空的坐標軸
myChart.setOption({
title: {
text: '異步數據加載示例'
},
tooltip: {},
legend: {
data:['score','money']
},
xAxis: {
data: []
},
yAxis: {},
series: [{
name: 'score',
type: 'line',
data: []
},{
name: 'money',
type: 'bar',
data: []
}]
});
第二部分是重點,主要負責通過ajax技術動態的接收數據庫回傳的data,實時地加載到前端的echarts組件中。$.get('/viewdata').done(function (data))
表示從url請求數據,然后執行函數。其中$.get(url,data,success(response,status,xhr),dataType)
是簡寫的ajax函數,等價於如下代碼。
$.ajax({
url: url,
data: data,
success: success,
dataType: dataType
});
myChart.showLoading(); // 顯示加載動畫
// 從url請求數據,異步加載
$.get('/viewdata').done(function (data) {
myChart.hideLoading(); // 隱藏加載動畫
// 填入數據
myChart.setOption({
xAxis: {
data: data.name
},
series: [{
name: 'score', // 根據名字對應到相應的系列
data: data.score.map(parseFloat) // 轉化為數字(注意map)
},{
name: 'money',
data: data.money.map(parseFloat)
}]
});
});
因此以上內容也可以改寫如下:
function getData() {
$.ajax({
cache: false,
type: "GET",
url: "/viewdata", //把表單數據發送到/viewdata
data: {}, // 發送的數據,這里是前端接收數據,所以是空
dataType : "json", //返回數據形式為json
async: false,
error: function(request) {
alert("發送請求失敗!");
},
success: function(result) {
myChart.hideLoading(); // 隱藏加載動畫
// console.log(result);
myChart.setOption({
xAxis: {
data: result.name
},
series: [{
name: 'score', // 根據名字對應到相應的系列
data: result.score.map(parseFloat) // 轉化為數字(注意map)
},{
name: 'money',
data: result.money.map(parseFloat)
}]
});
}
});
};
//頁面加載完畢,發送ajax請求
$(document).ready(function () {
getData();
});
4. 前后端交互
講到這里,筆者覺得有必要記錄以下前后端交互的操作方法。Web應用基於ajax進行前后端數據交互,一般利用Get或者Post方式來實現。比較流行的做法是前端提交表單數據,后端處理完畢后返回Json數據到前端進行顯示。以下是前端發送數據到后端的流程代碼。
4.1 前端發送到后端
get請求:
前端:
<script src="{{url_for('static',filename='js/jquery.js')}}"></script>
<script>
var data={
'name':'kikay',
'age':18
}
$.ajax({
type:'GET',
url:'{{url_for("test_get")}}',
data:data,
dataType:'json',//希望服務器返回json格式的數據
success:function(data){
alert(JSON.stringify(data));
alert(data['test'])
}
});
</script>
后端:request.args.get
獲取get請求數據
@test.route('/test_get/',methods=['POST','GET'])
def test_get():
#獲取Get數據
name=request.args.get('name')
age=int(request.args.get('age'))
#返回
if name=='kikay' and age==18:
return jsonify({'result':'ok'})
else:
return jsonify({'result':'error'})
post請求:
前端:
<script src="{{url_for('static',filename='js/jquery.js')}}"></script>
<script>
var data={
'name':'kikay',
'age':18
}
$.ajax({
type:'POST',
url:'{{url_for("test_post")}}',
data:data,
dataType:'json',//希望服務器返回json格式的數據
success:function(data){
alert(JSON.stringify(data));
}
});
</script>
后端:request.form.get
獲取get請求數據
@test.route('/test_post/',methods=['POST','GET'])
def test_post():
#獲取POST數據
name=request.form.get('name')
age=int(request.form.get('age'))
#返回
if name=='kikay' and age==18:
return jsonify({'result':'ok'})
else:
return jsonify({'result':'error'})
4.2 后端發送給前端
后端發送數據給前端主要通過return回dic/json。
@app.route("/")
def index():
dic={}
dic['name']='xiaomin'
dic['num']=1
return render_template('index.html',data=dic)
前端通過接收數據只需要把ajax相關代碼中的data設置為空。
$.ajax({
type:'GET',
url:'{{url_for("test_get")}}',
data:{}, //不發送data
dataType:'json',//希望服務器返回json格式的數據
success:function(data){ //從這里接收到data
alert(JSON.stringify(data));
alert(data['test'])
}
5. CSS樣式設置
最后就是調整兩個div容器的位置和樣式,插入到html中。
三、結果
數據僅供顯示,無任何意義。運行成功后界面如下:
四、展望
-
文件分層:當項目較大的時候,可以把數據的查詢功能(sql)放在單獨的腳本里utils.py,在app.py里只負責調用接口和創建路由。同樣當js樣式較多時候,可以把ajax請求放在controller.js中中轉,在其他js文件里渲染靜態樣式。
- 還可以通過china.js/lefatlet.js等第三方庫添加地圖進行顯示。
- 前后端交互的原理相當重要,前后端數據的發送和接收方法值得注意。
- 后續還需部署服務,以實現在別的電腦上進行訪問。
- 今天做的只是一個簡單樣例,后續可以實現https://blog.csdn.net/hxxjxw/article/details/105336981中的疫情可視化系統完成完整系統的設計。