从0开始搭建vue + flask 旅游景点数据分析系统(七):可视化前后端对接实现

这一期继续编写flask后端,并且完成echarts折线图、柱状图和饼图的对接。

1 新增一些依赖

pip install Flask-SQLAlchemy Flask-Marshmallow pymysql

修改 init.py文件,下面给出完整代码:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow

db = SQLAlchemy()
ma = Marshmallow()

def create_app():
    app = Flask(__name__)
    app.config.from_object('app.config.Config')
    db.init_app(app)
    ma.init_app(app)
    
    from .routes import main as main_blueprint
    app.register_blueprint(main_blueprint)

    return app

这里还加入了数据库的配置信息,需要修改app.config:

class Config:
    # scrapy_demo 就是之前旅游爬虫教程中建的数据库,如果不清楚,可以去看这个教程
    # 视频:https://www.bilibili.com/video/BV1Vx4y147wQ
    # 博客:https://blog.csdn.net/roccreed?type=blog
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:12345678@localhost/scrapy_demo?charset=utf8'
    SQLALCHEMY_TRACK_MODIFICATIONS = False

2 后端模型

通过模型,可以在Flask后端系统里以面向对象的形式来操作数据库里的数据。

首先,新建app/models.py:

from . import db

class Tour(db.Model):
    __tablename__ = 'tb_tour'

    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(255), nullable=False)
    title_en = db.Column(db.String(255))
    img = db.Column(db.String(255))
    score = db.Column(db.Float)
    comments = db.Column(db.Integer)
    comment_url = db.Column(db.String(255))
    rank_title = db.Column(db.String(255))
    ranks = db.Column(db.Integer)
    select_user = db.Column(db.String(255))
    select_comment = db.Column(db.Text)
    nation = db.Column(db.String(255))
    city = db.Column(db.String(255))

创建schemas.py 文件,作用是数据在发送给前端的时候进行序列化,新建app/schemas.py:

from . import ma
from .models import Tour

class TourSchema(ma.SQLAlchemyAutoSchema):
    class Meta:
        model = Tour
        load_instance = True

tour_schema = TourSchema()
tours_schema = TourSchema(many=True)

修改routes.py 文件:

from flask import Blueprint, jsonify

from app.models import Tour
from app.schemas import tours_schema

main = Blueprint('main', __name__)

# 这个测试的后面就不需要了,可以删除
@main.route('/test', methods=['GET'])
def test():
    data = [{'id': 1, 'name': 'John'}, {'id': 2, 'name': 'Jane'}]
    return jsonify(data)

# 十大热门景点
@main.route('/commentsRank', methods=['GET'])
def getCommentsRank():
    top_tours = Tour.query.order_by(Tour.comments.desc()).limit(10).all()
    return tours_schema.jsonify(top_tours)

3 测试一下

在浏览器里输入 localhost:8080/commentsRank 访问,得到如下结果:

可以看到,访问后端是可以获取到数据的。
在这里插入图片描述

4 返回封装

在开始和前端对接前,先对后端的返回进行一定的优化

创建app/utils.py 文件

from flask import jsonify

def make_response(data=None, code=0, message='Success'):
    response = {
        'code': code,
        'message': message,
        'data': data if data is not None else []
    }
    return jsonify(response)

修改routes.py,之前测试的方法这边删除了:


# 十大热门景点(按照评论数排名)
@main.route('/top-tours', methods=['GET'])
def get_top_tours():
    try:
        top_tours = Tour.query.order_by(Tour.comments.desc()).limit(10).all()
        result = tours_schema.dump(top_tours)
        return make_response(data=result)
    except Exception as e:
        return make_response(code=1, message=str(e))

在测试一下,就可以发现结果封装在data里面了。

然后,下一步编写前端文件。

5 折线图的对接

上一小节已经写好了折线图的后端代码,现在开始写前端代码,修改LineChart.vue,这边我给出完整的源码了,另外两个图我只给出修改部分源码,因为原本的data部分不需要做任何修改:

<template>
  <div>
    <v-chart :option="chartOptions" style="width: 100%; height: 300px;"></v-chart>
  </div>
</template>

<script>
import {getCommentsRank} from "@/api/tour"

export default {
  name: 'TouristSpotRanking',
  data() {
    return {
      chartOptions: {
        title: {
          text: '旅游景点评论排名',
        },
        tooltip: {
          trigger: 'axis',
        },
        legend: {
          data: ['评论数'],
        },
        xAxis: {
          type: 'category',
          data: ['景点A', '景点B', '景点C', '景点D', '景点E'],
        },
        yAxis: {
          type: 'value',
        },
        series: [
          {
            name: '评论数',
            type: 'line',
            data: [820, 932, 901, 934, 1290],
          },
        ],
      },
    };
  },
  mounted() {
    getCommentsRank().then(res => {
      console.log(res.data.data);
      this.chartOptions.xAxis.data = res.data.data.map(item => item.title);
      this.chartOptions.series[0].data = res.data.data.map(item => item.comments);
    })
  }
};
</script>

<style scoped>
/* 添加一些样式使图表看起来更好 */
</style>

在tour.js中添加方法:

// 排名前十的景点
export function getCommentsRank() {
    return request({
        url:  '/commentsRank',
        method: 'get'
    })
}

实现效果如下,可以看到景点按照评论数的折线图:
在这里插入图片描述

6 柱状图的对接

tour.js中添加方法:

// 按照城市排名
export function getCityRank() {
    return request({
        url:  '/cityRank',
        method: 'get'
    })
}

修改routes.py:

# 景点按照评分排名
@main.route('/scoreRank', methods=['GET'])
def getScoreRank():
    try:
        top_tours = Tour.query.filter(Tour.comments>1000).order_by(Tour.score.desc()).limit(5).all()
        result = tours_schema.dump(top_tours)
        return make_response(data=result)
    except Exception as e:
        return make_response(code=1, message=str(e))

修改BarChart.vue:

import {getScoreRank} from "@/api/tour";

  mounted() {
    getScoreRank().then(res => {
      // console.log(res.data.data);
      this.chartOptions.xAxis.data = res.data.data.map(item => item.title);
      this.chartOptions.series[0].data = res.data.data.map(item => item.score);
    })
  }

效果:
在这里插入图片描述

7 饼图的对接

tour.js中添加方法:

// 按照评分排名 
export function getScoreRank() {
    return request({
        url:  '/scoreRank',
        method: 'get'
    })
}

修改routes.py:

# 由于评分都很近,因此这边限制了评论数超过1000的景点再按照评分来排名
# 这样区分度大,不至于前端的柱状图都是10分
# 景点按照城市统计
@main.route('/cityRank', methods=['GET'])
def getCityRank():
    try:
        ret = db.session.query(Tour.city.label('name'),
                               db.func.count(Tour.id).label('value')).group_by(Tour.city).order_by(db.desc('value')).all()
        result = chart_schema.dump(ret)
        return make_response(data=result)
    except Exception as e:
        return make_response(code=1, message=str(e))

修改schemas.py,用来封装饼图数据的,添加:

class ChartData(ma.Schema):
    class Meta:
        fields = ('name', 'value')

chart_schema = ChartData(many=True)

修改PieChart.vue:

import {getCityRank} from "@/api/tour";

mounted() {
    getCityRank().then(res => {
      // console.log(res.data);
      this.chartOptions.series[0].data = res.data.data
    })
  }

效果:

在这里插入图片描述

8 小结

这期完成了对三个echarts图形的前后端对接,后端实现了从数据库获取数据(利用sqlalchemy + pymsyql 方式),并且对返回也是对了封装的,总的来说成果斐然 👍🏻。
三个图都实现了前后端对接:
在这里插入图片描述

  • 23
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

麦麦大数据

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值