这一期继续编写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 Marshmallowdb = 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_blueprintapp.register_blueprint(main_blueprint)return app
这里还加入了数据库的配置信息,需要修改app.config:
class Config:# scrapy_demo 就是之前旅游爬虫教程中建的数据库,如果不清楚,可以去看这个教程# 视频:https://www.bilibili.com/video/BV1Vx4y147wQ# 博客:https://blog.csdn.net/roccreed?type=blogSQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:12345678@localhost/scrapy_demo?charset=utf8'SQLALCHEMY_TRACK_MODIFICATIONS = False
2 后端模型
通过模型,可以在Flask后端系统里以面向对象的形式来操作数据库里的数据。
首先,新建app/models.py:
from . import dbclass 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 Tourclass TourSchema(ma.SQLAlchemyAutoSchema):class Meta:model = Tourload_instance = Truetour_schema = TourSchema()
tours_schema = TourSchema(many=True)
修改routes.py 文件:
from flask import Blueprint, jsonifyfrom app.models import Tour
from app.schemas import tours_schemamain = 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 jsonifydef 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 方式),并且对返回也是对了封装的,总的来说成果斐然 👍🏻。
三个图都实现了前后端对接: