您好,登錄后才能下訂單哦!
本篇文章為大家展示了怎么在Django中利用Highcharts 實(shí)現(xiàn)數(shù)據(jù)可視化,內(nèi)容簡明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。
開發(fā)環(huán)境
使用 Python 2.7.14,django 1.11.8,highcharts 4.0.1
直接命令行輸入以下語句,即可安裝django 1.11.8
。
pip install django==1.11.8
至于Highcharts
,可以去官網(wǎng)下載。我用的是之前前輩給的模板,js不是太懂,所以基本沒改,只是為了方便進(jìn)行拓展,對(duì)功能模塊進(jìn)行了注釋。
開發(fā)需求
手頭已有爬取的winstore不同app,不同榜單,不同地區(qū)的多天rank數(shù)據(jù)。這些rank數(shù)據(jù)存放在MySQL服務(wù)器中,庫名為winstore,表名為winstore_rank。
現(xiàn)在需要將這些rank數(shù)據(jù)用折線圖的方式展示出來。同時(shí)在網(wǎng)頁上需要可以根據(jù)選擇的日期,地區(qū),榜單來動(dòng)態(tài)產(chǎn)生折線圖。
問題解析
根據(jù)開發(fā)需求,可以將這次任務(wù)分為三個(gè)部分。
前端頁面
a. ajax動(dòng)態(tài)獲取地區(qū)列表、榜單列表,生成對(duì)應(yīng)的下拉列表,必要時(shí)需將傳統(tǒng)下拉列表轉(zhuǎn)換成多選下拉列表
b. 根據(jù)搜索結(jié)果,將符合條件的app的rank添加到折線圖中
服務(wù)器端
a. 接受前端的請(qǐng)求,與數(shù)據(jù)庫通信,返回所請(qǐng)求數(shù)據(jù)
MySQL數(shù)據(jù)庫
a. 根據(jù)服務(wù)器端傳輸?shù)膕ql語句進(jìn)行對(duì)應(yīng)的查詢
根據(jù)上述的分析,前端肯定是js + jQuery + Echarts + jquery.multiselect
了,服務(wù)器端采用Django,數(shù)據(jù)庫方面Django有對(duì)應(yīng)的驅(qū)動(dòng)模塊,不用管。
1. 前端頁面
新建一個(gè)文件rank.html
,內(nèi)容如下:
<head> {% load static %} <script type="text/javascript" src="{% static 'js/jquery.min.js' %}"></script> <script type="text/javascript" src="{% static 'js/highcharts.js' %}"></script> <script type="text/javascript" src="{% static 'js/jquery-ui.min.js' %}"></script> <script type="text/javascript" src="{% static 'js/exporting.js' %}"></script> <script type="text/javascript" src="{% static 'js/jquery.multiselect.min.js' %}"></script> <link rel="stylesheet" href="{% static 'css/jquery-ui.min.css' %}" rel="external nofollow" > <link rel="stylesheet" href="{% static 'css/jquery.multiselect.css' %}" rel="external nofollow" > <link rel="stylesheet" href="{% static 'css/screen1.css' %}" rel="external nofollow" > <style type="text/css"> #set-content ul li #chart { width: 60px; font-size: 12px; height: 22px; } </style> <script type="text/javascript"> // 設(shè)定開始日期和結(jié)束日期,默認(rèn)為最近10天 $(function() { $("#beginDate").datepicker({dateFormat: "yy-mm-dd"}); $("#endDate").datepicker({dateFormat: "yy-mm-dd"}); var dateNow = new Date(); var str_dateNow = dateNow.getFullYear() + "-" + (dateNow.getMonth() + 1) + "-" + dateNow.getDate(); var dateBegin = new Date(dateNow - 10 * 1000 * 3600 * 24); var str_dateBegin = dateBegin.getFullYear() + "-" + (dateBegin.getMonth() + 1) + "-" + dateBegin.getDate(); $("#beginDate").datepicker("setDate", str_dateBegin); $("#endDate").datepicker("setDate", str_dateNow); }); // 動(dòng)態(tài)獲取數(shù)據(jù)庫中region數(shù)據(jù),填充入下拉列表 $(function() { $.get("/getWinstoreRegions", {"limit": "0"}, function(regionsDict) { for (var id in regionsDict) { regionOption = "<option value='" + id + "'>" + regionsDict[id] + "</option>"; $("#region").append(regionOption); } }, "json" ) }); // 動(dòng)態(tài)獲取數(shù)據(jù)庫中chart數(shù)據(jù),填充入下拉列表 $(function() { $.get("/getWinstoreCharts", {"limit": "0"}, function(chartsDict) { for (var id in chartsDict) { chartOption = "<option value='" + id + "'>" + chartsDict[id] + "</option>"; $("#chart").append(chartOption); } }, "json" ) }); // 動(dòng)態(tài)獲取數(shù)據(jù)庫中category數(shù)據(jù),填充入下拉列表 $(function() { $.get("/getWinstoreCategories", {"limit": "0"}, function(categoriesDict) { for (var id in categoriesDict) { categoryOption = "<option value='" + id + "'>" + categoriesDict[id] + "</option>"; $("#category").append(categoryOption); } }, "json" ) }); // 動(dòng)態(tài)獲取數(shù)據(jù)庫中app名字,填充入下拉列表 $(function() { $.get( "/getWinstoreApps", {"limit":"0",}, function(dataDict) { // 循環(huán)添加下拉列表的option for (var id in dataDict) { appOption = "<option value='" + id + "'>" + dataDict[id] + "</option>"; $("#appName").append(appOption); } // 初始化多選 $("#appName").multiselect({header: false,}); // 選中所有下拉列表項(xiàng) $("#appName").multiselect("checkAll"); // 動(dòng)態(tài)設(shè)置多選框的寬度 var ulList = $(".ui-multiselect-checkboxes")[0]; // 必須先單擊多選下拉列表,然后才可以獲取對(duì)應(yīng)元素的寬度值 $(".ui-multiselect")[0].click(); var maxWidth = 0; for (var i = 0; i < ulList.childElementCount; i++) { var currentInputWidth = $(ulList.childNodes[i]).find("input")[0].offsetWidth; var currentSpanWidth = $(ulList.childNodes[i]).find("span")[0].offsetWidth; var currentWidth = currentSpanWidth + currentInputWidth * 3; if (currentWidth > maxWidth) { maxWidth = currentWidth; } } // 設(shè)置對(duì)應(yīng)標(biāo)簽的寬度 $($(".ui-multiselect")[0]).width(maxWidth); $($(".ui-multiselect-menu")[0]).width(maxWidth + 6); // 二次單擊 $(".ui-multiselect")[0].click(); }, "json"); }); // 綁定query按鈕的單擊操作 $(function() { $("#query").click(function() { var region = $("#region").val(); var beginDate = $("#beginDate").val(); var endDate = $("#endDate").val(); var chart = $("#chart").val(); var appNames = $("#appName").val(); var category = $("#category").val(); // 將appNames連接成字符串 queryReport(region, beginDate, endDate, chart, category, appNames.join("@")); }); }) var lineChart; // 獲取繪圖數(shù)據(jù) function queryReport(region, beginDate, endDate, chart, category, appNames) { // 清空原有繪圖數(shù)據(jù) $("#container")[0].innerHTML = ""; // 初始化折線圖參數(shù) var lineChart = new Highcharts.Chart({ chart: { renderTo: 'container', type: 'line' }, title: { text: 'Daily Ranking', style: {fontFamily: 'Helvetica', fontWeight: '200'} }, subtitle: { text: 'By Product', style: {fontFamily: 'Helvetica', fontWeight: '200'} }, xAxis: [{ // master axis type: 'datetime', gridLineWidth:1, gridLineDashStyle: 'longdash', tickInterval: 24 * 3600 * 1000, }, { // slave axis type: 'datetime', linkedTo: 0, opposite: true, tickInterval: 24 * 3600 * 1000, labels: { formatter: function () {return Highcharts.dateFormat('%a', this.value);} } }], tooltip: { headerFormat: '<span>{point.key}</span><br/>', pointFormat: '<span >\u25AC</span> {series.name}: <b>{point.y}</b><br/>', }, yAxis: [{ // Primary yAxis min:1, reversed: true, labels: { format: 'No. {value}', style: { color: '#4572A7' } }, title: { text: 'Ranking', style: { color: '#4572A7' } } }, { // Secondary yAxis min:1, reversed: true, title: { text: 'Ranking', style: { color: '#4572A7' } }, labels: { format: 'No. {value}', style: { color: '#4572A7' } }, opposite: true, }], plotOptions: { column: { dataLabels: { enabled: true }, enableMouseTracking: true }, line: { dataLabels: { enabled: true }, enableMouseTracking: true } }, series: [], }); // 構(gòu)造url參數(shù) parameters = {'region': region, 'beginDate': beginDate, 'endDate': endDate, 'chart': chart, 'category': category, 'appNames': appNames }; // 請(qǐng)求繪圖數(shù)據(jù) $.get("/getWinstoreRank", parameters, function(rankDict) { var ranksOfApp = new Array(); for (var app in rankDict) { lineChart.addSeries({ name: app, data: rankDict[app] }); } }, "json" ); } </script> </head> <body> <div id="set-content"> <ul> <li> <label for="region">Country/Region: </label> <select id="region"></select> </li> <li> <label for="beginDate">Begin Date: </label> <input type="text" id="beginDate"> </li> <li> <label for="endDate">End Date: </label> <input type="text" id="endDate"> </li> <li> <label for="chart">Chart: </label> <select id="chart"></select> </li> <li> <label for="category">Category: </label> <select id="category"></select> </li> <li> <label for="appName">App:</label> <select id="appName" multiple="multiple" size="4"></select> </li> <li> <button id='query'>Query</button> </li> </ul> </div> <div id="container"></div> </body>
這里稍微解釋下,在實(shí)際使用中,使用highcharts生成折線圖,根據(jù)不同的數(shù)據(jù),只需要修改series參數(shù)即可。而series參數(shù)是個(gè)啥,可以在上面的HTML代碼中搜索series即可。稍微觀察下,就明白了。
至于你想換個(gè)大餅圖,柱狀圖,可以 點(diǎn)擊這里 找現(xiàn)成的例子,稍作修改就可以使用了。當(dāng)然也許你有更多個(gè)性化的需求,那可以 點(diǎn)擊這里 找到對(duì)應(yīng)的配置項(xiàng)進(jìn)行修改。
2. 服務(wù)器端
1、首先命令行進(jìn)入到你想放置項(xiàng)目代碼的地方
django_admin startproject winstore
2、進(jìn)入剛剛新建的項(xiàng)目文件夾
cd winstore
3、創(chuàng)建新的應(yīng)用rank
。這里的應(yīng)用可以理解成具有獨(dú)立功能的一組網(wǎng)頁的結(jié)合,當(dāng)然在本篇博客里,只有一個(gè)網(wǎng)頁了。
python manage.py startapp rank
4、在rank文件夾中新建文件夾templates
和static
,將剛剛新建的rank.html
放入templates
文件夾,同時(shí)將引用的js庫文件放入static
文件夾下,注意文件夾層級(jí)。
5.、打開winstore文件夾下的settings.py
,在INSTALL_APPS
下添加rank
,添加之后如下:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rank' # 添加的部分 ]
將DATABASES
修改成你自己的MySQL數(shù)據(jù)庫的控制信息。下面是我的數(shù)據(jù)庫設(shè)置:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'winstore', # 數(shù)據(jù)庫名 'HOST': '127.0.0.1', # IP 'PORT': '3306', # 端口號(hào) 'USER': 'root', # 用戶名 'PASSWORD': '111111', # 密碼 } }
6、編輯rank
文件夾下的views.py
文件,在rank.html中加入必要的網(wǎng)頁動(dòng)態(tài)功能的實(shí)現(xiàn)。由于app的排名數(shù)據(jù)是根據(jù)其所處的榜單chart
和應(yīng)用類別category
,以及不同的地區(qū)region
來確定的,所以這里里的功能實(shí)現(xiàn)就需要包括5個(gè)部分。分別對(duì)appName
、chart
、category
、region
實(shí)現(xiàn)從數(shù)據(jù)庫動(dòng)態(tài)獲取其取值集合以及獲取排名數(shù)據(jù)。對(duì)應(yīng)的實(shí)現(xiàn)分別如下:
appName
def getWinstoreApps(request): """ 根據(jù)接收到的GET請(qǐng)求返回app的取值集合 """ # 構(gòu)造SQL語句 sql = 'SELECT DISTINCT appName FROM winstore_rank' # 默認(rèn)appNames的key和value相同 appNames = {} try: result = getDataFromSQL(sql) result = [r[0] for r in result] for key in result: appNames[key] = key except Exception as e: print('getWinstoreApps ERROR: ' + str(e)) appNames['QQ'] = 'QQ' return JsonResponse(appNames)
chart
def getWinstoreCharts(request): """ 根據(jù)接收到的GET請(qǐng)求返回chart的取值集合 """ # 構(gòu)造SQL語句 sql = 'SELECT DISTINCT chart FROM winstore_rank' # 默認(rèn)charts的key和value相同 charts = {} try: result = getDataFromSQL(sql) result = [r[0] for r in result] for key in result: charts[key] = key except Exception as e: print('getWinstoreCharts ERROR: ' + str(e)) charts['Free'] = 'Free' return JsonResponse(charts)
category
def getWinstoreCategories(request): """ 根據(jù)接收到的GET請(qǐng)求返回category的取值集合 """ # 構(gòu)造SQL語句 sql = 'SELECT DISTINCT category FROM winstore_rank' # 默認(rèn)categories的key和value相同 categories = {} try: result = getDataFromSQL(sql) result = [r[0] for r in result] for key in result: categories[key] = key except Exception as e: print('getWinstoreCategories ERROR: ' + str(e)) categories['Education'] = 'Education' return JsonResponse(categories)
region
def getWinstoreRegions(request): """ 根據(jù)接收到的GET請(qǐng)求返回region的取值集合 """ # 構(gòu)造SQL語句 sql = 'SELECT DISTINCT region FROM winstore_rank' # 默認(rèn)regions的key和value相同 regions = {} try: result = getDataFromSQL(sql) result = [r[0] for r in result] for key in result: regions[key] = key except Exception as e: print('getWinstoreRegions ERROR: ' + str(e)) regions['EN-US'] = 'EN-US' return JsonResponse(regions)
獲取排名數(shù)據(jù)
def getWinstoreRank(request): """ 根據(jù)接收到的GET請(qǐng)求返回對(duì)應(yīng)app的排名數(shù)據(jù) """ # 從GET請(qǐng)求中獲取參數(shù) region = request.GET.get("region", "EN-US") chart = request.GET.get("chart", "Free") category = request.GET.get("category", "Education") beginDate = request.GET.get("beginDate", "2018-01-22") endDate = request.GET.get("endDate", "2018-02-02") appNames = request.GET.get("appNames", "QQ").split("@") # 構(gòu)造SQL語句 sqlTemp = 'SELECT the_date, rank FROM winstore_rank WHERE ' \ 'region="%s" AND chart="%s" AND category="%s" AND ' \ 'the_date BETWEEN "%s" AND "%s" AND ' \ 'appName=' % (region, chart, category, beginDate, endDate) # 以每個(gè)appName作為key,對(duì)應(yīng)的排名數(shù)據(jù)列表作為value appRank = {} for appName in appNames: sql = sqlTemp + '"' + appName + '"' try: result = getDataFromSQL(sql) # 根據(jù)數(shù)據(jù)庫返回的結(jié)果將缺少rank數(shù)據(jù)的日期補(bǔ)0 result = addZeroToRank(beginDate, endDate, result) appRank[appName] = result except Exception as e: print('getWinstoreRank ERROR: ' + str(e)) return JsonResponse(appRank) def addZeroToRank(beginDate, endDate, result): """ 以beginDate和endDate為日期的起始,將result中缺少的日期補(bǔ)全,同時(shí)設(shè)定排名為0 Param: beginDate: 開始日期字符串,“2018-01-23” endDate: 結(jié)束日期字符串, “2018-02-02” result: 形如[(date, 23L), (date, 12L), [date, 3L]......] Return: 按照日期順序排列的排名數(shù)據(jù),缺省排名為0 """ # 將日期字符串轉(zhuǎn)變?yōu)閐ate類型數(shù)據(jù),方便日期加減 y, m, d = [int(i) for i in beginDate.split("-")] begin = datetime.date(y, m, d) y, m, d = [int(i) for i in endDate.split("-")] end = datetime.date(y, m, d) current = begin # 獲取result中的日期,方便進(jìn)行判斷 resultTemp = [r[0] for r in result] while current <= end: if not (current in resultTemp): result.append((current, 0)) current += datetime.timedelta(days=1) result.sort(key=lambda x: x[0]) return [int(r[1]) for r in result]
這里主要就是構(gòu)造SQL語句,然后訪問數(shù)據(jù)庫獲取對(duì)應(yīng)的數(shù)據(jù)集合。其中getDataFromSQL()
是對(duì)訪問MySQL數(shù)據(jù)庫的簡單封裝,具體代碼如下:
def getDataFromSQL(sql): """ 根據(jù)sql語句獲取數(shù)據(jù)庫的返回?cái)?shù)據(jù) """ cursor = connection.cursor() cursor.execute(sql) return list(cursor.fetchall())
一些涉及到的引用可以參考文末給出的項(xiàng)目代碼。
最終綁定一下首頁
def index(request): """ 綁定網(wǎng)站首頁 """ return render(request, 'rank.html')
3. MySQL數(shù)據(jù)庫
實(shí)際應(yīng)用時(shí),相關(guān)的rank數(shù)據(jù)是通過爬蟲獲取的。在這里,就直接填充一些隨機(jī)的rank數(shù)據(jù)進(jìn)去了,不影響最終的結(jié)果。
最終成果展示
首頁
rank數(shù)據(jù)展示
可以看到雖然前端頁面很簡陋,但是功能是實(shí)現(xiàn)了。不過有個(gè)問題 就是重新點(diǎn)擊query按鈕后,highcharts提供的右側(cè)頁面中間下載圖片的那個(gè)三道杠會(huì)出現(xiàn)并排的兩個(gè)。
上述內(nèi)容就是怎么在Django中利用Highcharts 實(shí)現(xiàn)數(shù)據(jù)可視化,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。