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

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

1 新增一些依赖

python 复制代码
pip install Flask-SQLAlchemy Flask-Marshmallow pymysql

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

python 复制代码
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:

python 复制代码
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:

python 复制代码
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:

python 复制代码
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 文件:

python 复制代码
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 文件

python 复制代码
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,之前测试的方法这边删除了:

python 复制代码
# 十大热门景点(按照评论数排名)
@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部分不需要做任何修改:

jsx 复制代码
<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中添加方法:

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

实现效果如下,可以看到景点按照评论数的折线图:

6 柱状图的对接

tour.js中添加方法:

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

修改routes.py

python 复制代码
# 景点按照评分排名
@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:

jsx 复制代码
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中添加方法:

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

修改routes.py

python 复制代码
# 由于评分都很近,因此这边限制了评论数超过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,用来封装饼图数据的,添加:

python 复制代码
class ChartData(ma.Schema):
    class Meta:
        fields = ('name', 'value')

chart_schema = ChartData(many=True)

修改PieChart.vue:

jsx 复制代码
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 方式),并且对返回也是对了封装的,总的来说成果斐然 👍🏻。

三个图都实现了前后端对接:

相关推荐
Au_ust3 分钟前
css:感觉稍微高级一点的布局
前端·css
下页、再停留3 分钟前
【前端】CSS修改div滚动条样式
前端·css
一晌贪欢i3 分钟前
CSS中calc语法不生效
前端·css
前端白袍9 分钟前
Vue:后端返回二进制文件,前端如何实现浏览器自动下载?
前端·javascript·vue.js
问道飞鱼9 分钟前
【前端知识】简单讲讲什么是微前端
前端·微前端·qiankun·single-spa
大得36912 分钟前
css水平居中+垂直居中
vue.js·react
Jing_jing_X23 分钟前
心情追忆-首页“毒“鸡汤AI自动化
java·前端·后端·ai·产品经理·流量运营
Dklau-c23 分钟前
Linux下,修改环境变量的几种方法
linux·前端·chrome
Jet_closer_burning1 小时前
Vue.js 自定义指令:从零开始创建自己的指令
前端·javascript·vue.js
优雅永不过时·2 小时前
three.js实现地球 外部扫描的着色器
前端·javascript·webgl·three.js·着色器