項目結構參考重慶2020年巴渝工匠杯市賽題目、2019年重慶市h3c大數據挑戰賽題目
項目代碼已托管到github,地址見:https://github.com/ybqdren/BygjRace-DataShowChart
一、起步-新建項目
配置環境
1.新建Python項目 命名為01_MyFlaskTest
2.在Teminal中安裝pipenv
pip install pipenv
3.創建當前項目空間的虛擬環境
pipenv install
4.激活虛擬環境
pipenv shell
在teminal中執行上述指令,會返現項目路徑前出現'(虛擬環境名稱)$'類似字段。
這表明Pipenv已經為我們的項目激活了虛擬環境的子shell,此時虛擬環境表示已經激活成功了
安裝三方包
1.安裝flask
pipenv install flask
2.安裝flask-sqlalchemy
pipenv install flask-sqlalchemy
3,安裝Jinja2
pipenv install jinja2
4.安裝flask-mysqldb(sqlalchemy連接mysql數據庫必須)
pipenv install mysqldb
5.安裝flask-migrate
pipenv install flask-migrate
6.安裝flask-script
pipenv install flask-script
7.安裝SQLAlchemy
pipenv install SQLAlchemy
按照目錄規范添加目錄
如圖:
二、開始編碼
Flask實例對象准備
1.定義flask app實例創建工廠函數
app/_init_.py
def create_app(app_name):
app = Flask(app_name)
return app
2.在工廠函數中裝配db
app/extensions.py
from flask_sqlalchemy import SQLAlchemy
from flask import Flask
#創建對象
db = SQLAlchemy()
#初始化
def config_extensions(app):
db.init_app(app)
app/config.py
import os
class Config():
SQLALCHEMY_DATABASE_URI = 'mysql://root:666666@127.0.0.1:3306/race_flask'
SQLALCHEMY_TRACK_MODIFICATIONS = True
SQLALCHEMY_ECHO = True
app/_init_.py
def create_app(app_name):
app = Flask(app_name)
app.config.from_object(Config)
config_extensions(app) #裝配SQLAlchemy對象 db
return app
藍圖准備
1.創建藍圖實例對象
app/views/main.py
from flask import Blueprint
# 創建藍圖
main_print = Blueprint('main_print',__name__)
2.提供藍本注冊函數
app/veiws/main/_init_.py
from .main import main_print
# 封裝
DEFAULT_BLUEPRINT = {
(main_print,'')
}
#藍圖注冊
def config_blueprint(app):
for blueprint,prefix in DEFAULT_BLUEPRINT:
app.register_blueprint(blueprint,prefix=prefix)
前端頁面准備
1.echarts文件構建下載
https://echarts.apache.org/zh/builder.html
2.前端顯示頁面准備
app/templates/main/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>數據可視化展示頁面</title>
<script src="/static/js/echarts.min.js"></script>
</head>
<body>
</body>
</html>
3.藍圖中構建視圖函數
app.views.main.py
from flask import render_template
@main_print.route('/index')
def index():
return render_template('/main/index.html')
三、數據與模型類
模型類
app/modles/tbl_video_game_sales.py
from app.extensions import db
class Tbl_Video_Game_Sales(db.Model):
__tablename__ = 't_video_game_sales'
Rank = db.Column(db.String(255),primary_key=True) #銷售排名
Name = db.Column(db.String(255)) #游戲名稱
Platform = db.Column(db.String(255)) #該游戲發布平台
Year = db.Column(db.Integer) #發布年份(1980-2016)
Genre = db.Column(db.String(255)) #游戲類型
Publisher = db.Column(db.String(255)) #出版公司
NA_Sales = db.Column(db.DECIMAL(255,2)) #北美區域銷量(以百萬為單位計數)
EU_Sales = db.Column(db.DECIMAL(255,2)) #歐美區域銷量(以百位為單位計數)
JP_Sales = db.Column(db.DECIMAL(255,2)) #日本區域銷量(以百位為單位計數)
Other_Sales = db.Column(db.DECIMAL(255,2)) #其他國家銷量(以百位為單位計數)
Global_Sales = db.Column(db.DECIMAL(255,2)) #全球銷量(以百位為單位計數)
app/models/_init_.py
from .tbl_video_game_sales import Tbl_Video_Game_Sales
數據遷移
1.創建遷移倉庫
創建數據遷移對象
app/extensions.py
from flask_sqlalchemy import SQLAlchemy
from flask import Flask
from flask_migrate import Migrate,MigrateCommand
#創建對象
db = SQLAlchemy()
migrate = Migrate()
#初始化
def config_extensions(app):
db.init_app(app)
migrate.init_app(app,db)
manager.py
from app import create_app
from app.views import config_blueprint
from flask_script import Manager
from flask_migrate import MigrateCommand
app = create_app('app')
config_blueprint(app)
manager = Manager(app)
manager.add_command('db',MigrateCommand)#使用 MigrateCommand類使用db命令附加
if __name__ == '__main__':
manager.run()
Terminal上輸入
python manage.py db init
flask-migrate會為我們生成一個遷移文件夾
2.創建遷移腳本
python manage.py db migrate
使用migrate子命令可以自動生成遷移腳本
ORM模型映射
此處參考:https://flask-sqlalchemy.palletsprojects.com/en/2.x/contexts/
當前步驟在Python Console中完成
1.導入工廠函數創建Flask實例對象
from app import create_app
app = create_app('app')
2.設置應用上下文(重要!)
app.app_context().push()
如果此處不推送上下文,會報錯:No application found. Either work inside a view function or push an application context.
3.導入實例化的SQLAlchemy對象db,並綁定app
from app.extensions import db
db.init_app(app)
此時查看db對象,就會發現已經成功綁定了flask實例對象,並成功讀取到配置信息
4.導入orm模型類
from app.models import Tbl_Video_Game_Sales
5.執行db.create_all()指令
db.create_all()
db.session.commit()
6.測試orm模型映射是否成功
Tbl_Video_Game_Sales.query.all()[:2]
查詢結果
四、數據可視化
建立頁面模板 'base.html'
app/templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block head_title%}{% endblock head_title%}</title>
<script src="/static/js/echarts.min.js"></script>
<style>
#main{
width: 1000px;
height: 500px;
margin-left: auto;
margin-right: auto;
margin-top:20px;
}
#content-id{
text-align: center;
font-size: 30px;
padding-top: 20px;
}
</style>
</head>
{% block content%}
{% endblock content%}
</html>
導入模型類
from app.models import Tbl_Video_Game_Sales
開啟調試模式
from flask_script import Server
manager.add_command("runserver",Server(use_debugger=True))
1.柱狀圖
柱形圖 -> 同一對象不對維度份額比較
視圖函數
@main_print.route('/charts/bar')
def charts_bar():
game_sale = Tbl_Video_Game_Sales.query.first()
return render_template('/main/bar-chart.html',game_sale = game_sale)
頁面
{% extends 'base.html'%}
{% block head_title %}
數據可視化展示-柱狀圖
{% endblock head_title%}
{% block content %}
<body>
<div id="content-id">柱狀圖-bar</div>
<div id="main"></div>
<script type="text/javascript">
var barChart = echarts.init(document.getElementById('main'));
var option = {
title: {
text: '{{game_sale.Name}}各區域銷量',
subtext: '單位:百萬'
},
tooltip:{},
xAxis: {
data: ["北美","歐美","日本","其他國家"]
},
yAxis: {
type:'value'
},
series: [{
name: '銷量',
type: 'bar',
data: [{{game_sale.NA_Sales}}, {{game_sale.EU_Sales}}, {{game_sale.jp_Sales}}, {{game_sale.Other_Sales}}]
}]
};
barChart.setOption(option);
</script>
</body>
{% endblock content %}
2.折線圖
數據i變化趨勢
視圖函數
@main_print.route('/charts/line')
def charts_line():
game = dict()
for y in range(1999,2011,1):
count = Tbl_Video_Game_Sales.query.filter(Tbl_Video_Game_Sales.Year == y).count()
game[y] = count
return render_template('/main/line-chart.html',game = game)
頁面
{% extends 'base.html'%}
{% block head_title%}
數據可視化展示-折線圖
{% endblock %}
{% block content %}
<body>
<div id="content-id">折線圖-line</div>
<div id="main"></div>
</body>
<script>
var line_chart = echarts.init(document.getElementById('main'));
var option = {
title:{
text:'2000年~2010年發行游戲數量增長趨勢',
subtext:'單位:個'
},
tooltip:{},
xAxis:{
type:'category',
data:[{%for g in game%}'{{ g }}',{% endfor %}]
},
yAxis:{},
series:[{
type:'line',
data:[{% for g in game%}{{game[g]}},{% endfor %}]
}]
};
line_chart.setOption(option);
</script>
{% endblock %}
3.雙圖表切換(折線/柱)
視圖函數
@main_print.route('/charts/changeView')
def change_View():
game_sale = Tbl_Video_Game_Sales.query.order_by(Tbl_Video_Game_Sales.Global_Sales.desc()).first()
return render_template('/main/changeView-chart.html',game_sale = game_sale)
頁面
{% extends 'base.html' %}
{% block head_title%}
圖像切換-折線圖/柱狀圖
{% endblock head_title%}
{% block content %}
<body>
<div id="content-id">折線圖/柱形圖-line/bar</div>
<div id="main"></div>
</body>
<script>
my_chart = echarts.init(document.getElementById('main'));
var option = {
title:{
text:'一個簡單的折/柱切換圖',
},
tooltip:{
trigger:'item'
},
toolbox:{
feature:{
magicType:{
type:['line','bar']
}
}
},
xAxis:{
type:'category',
data:["北美","歐美","日本","其他國家"]
},
yAxis: {},
series:[{
type:'line',
data: [{{game_sale.NA_Sales}}, {{game_sale.EU_Sales}}, {{game_sale.jp_Sales|default('3.5')}}, {{game_sale.Other_Sales}}]
}]
};
my_chart.setOption(option);
</script>
{% endblock content %}
4.雷達圖
多維度數據展示
視圖函數
@main_print.route('/charts/radar')
def charts_radar():
game = Tbl_Video_Game_Sales.query.filter(Tbl_Video_Game_Sales.Platform == 'Wii').all()[:3]
return render_template('/main/radar-chart.html',game = game)
頁面
{% extends 'base.html'%}
{% block head_title %}
數據可視化展示-雷達圖
{% endblock head_title %}
{% block content %}
<body>
<div id="content-id">雷達圖-radar</div>
<div id="main"></div>
<script>
var na_sales = [{% for g in game%}{{ g.NA_Sales }},{% endfor %}]; //北美地區銷量
var eu_sales = [{% for g in game %}{{ g.EU_Sales }},{% endfor %}]; //歐美地區銷量
var jp_sales = [{% for g in game %}{{ g.JP_Sales }},{% endfor %}]; //日本地區銷量
var other_sales = [{% for g in game%}{{ g.Other_Sales }},{% endfor %}]; //其他地區銷量
var radar_chart = echarts.init(document.getElementById('main'));
var option = {
title:{
text:'各區域銷量'
},
tooltip:{},
legend:{
data:[{% for g in game %}'{{ g.Name }}',{% endfor %}],
},
radar:{
indicator:[
{name:'北美',max:50},
{name:'歐洲',max:50},
{name:'日本',max:50},
{name:'其他',max:50}
]
},
series: [{
type:'radar',
data:[
{% for g in game%}
{
value:[na_sales[{{ loop.index }}-1],eu_sales[{{ loop.index }}-1],jp_sales[{{ loop.index }}-1],other_sales[{{ loop.index }}-1]],
name:'{{ g.Name }}'
},
{% endfor %}
]
}]
};
radar_chart.setOption(option);
</script>
</body>
{% endblock content %}
5.餅圖
同一對象組成展示
視圖函數
@main_print.route('/charts/pie')
def charts_pie():
game = Tbl_Video_Game_Sales.query.filter(Tbl_Video_Game_Sales.Platform == 'PS3').order_by(Tbl_Video_Game_Sales.Global_Sales.desc()).all()[:10]
return render_template('/main/pie-chart.html',game = game)
頁面
{% extends 'base.html' %}
{% block head_title%}
數據可視化展示-餅圖
{% endblock head_title%}
{% block content %}
<body>
<div id="content-id">餅圖-pie</div>
<div id="main"></div>
<script>
var na_sales = [{% for g in game %}{{ g.NA_Sales}},{% endfor %}];
var eu_sales = [{% for g in game %}{{g.EU_Sales}},{% endfor %}];
var jp_sales = [{% for g in game %}{{g.JP_Sales}},{% endfor %}];
var other_sales = [{% for g in game %}{{g.Other_Sales}},{% endfor %}];
var avg_na =eval( na_sales.join("+"));
var avg_eu = eval(eu_sales.join("+"));
var avg_jp = eval(jp_sales.join("+"));
var avg_other = eval(other_sales.join("+"));
var pie_chart = echarts.init(document.getElementById('main'));
var option = {
title:{
text:'PS3平台游戲銷量區域份額',
subtext:'摘取銷量前十游戲數據'
},
tooltip:{
formatter:'【{b}區域銷量】 <br/> {c}百萬份({d}%)'
},
legend:{
data:['北歐','歐洲','日本','其他']
},
series:[
{
type:'pie',
radius:['25%','50%'],
data:[
{value:avg_na,name:'北歐'},
{value:avg_eu,name:'歐洲'},
{value:avg_jp,name:'日本'},
{value:avg_other,name:'其他'}
]
}
]
};
pie_chart.setOption(option);
</script>
</body>
{% endblock content %}
6.南丁格爾圖(玫瑰圖)
比餅圖更直觀
視圖函數
@main_print.route('/charts/rosePie')
def charts_rosePie():
game = Tbl_Video_Game_Sales.query.filter(Tbl_Video_Game_Sales.Platform == 'PS3').order_by(Tbl_Video_Game_Sales.Global_Sales.desc()).all()[:10]
return render_template('/main/rosePie-chart.html',game = game)
頁面
{% extends 'base.html' %}
{% block head_title%}
數據可視化展示-南丁格爾圖
{% endblock head_title%}
{% block content %}
<body>
<div id="content-id">南丁格爾圖-rosePie</div>
<div id="main"></div>
<script>
var na_sales = [{% for g in game %}{{ g.NA_Sales}},{% endfor %}];
var eu_sales = [{% for g in game %}{{g.EU_Sales}},{% endfor %}];
var jp_sales = [{% for g in game %}{{g.JP_Sales}},{% endfor %}];
var other_sales = [{% for g in game %}{{g.Other_Sales}},{% endfor %}];
var avg_na =eval( na_sales.join("+"));
var avg_eu = eval(eu_sales.join("+"));
var avg_jp = eval(jp_sales.join("+"));
var avg_other = eval(other_sales.join("+"));
var pie_chart = echarts.init(document.getElementById('main'));
var option = {
title:{
text:'PS3平台游戲銷量區域份額',
subtext:'摘取銷量前十游戲數據'
},
tooltip:{
formatter:'【{b}區域銷量】 <br/> {c}百萬份({d}%)'
},
legend:{
data:['北歐','歐洲','日本','其他']
},
series:[
{
type:'pie',
radius:['25%','50%'],
data:[
{value:avg_na,name:'北歐'},
{value:avg_eu,name:'歐洲'},
{value:avg_jp,name:'日本'},
{value:avg_other,name:'其他'}
],
roseType:'radius' //area
}
]
};
pie_chart.setOption(option);
</script>
</body>
{% endblock content %}
項目啟動
python manage.py runserver