從0開始搭建Flask-Echarts數據可視化項目



項目結構參考重慶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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM