Django+Echarts畫圖實例詳解

這篇文章主要介紹了Django+Echarts畫圖實例詳解,可以瞭解Django中aggregate和annotate函數的使用方法及其Django+Echarts繪製柱狀圖的完整示例,感興趣的小夥伴們可以參考一下

所有演示均基於Django2.0

閱讀此篇文章你可以:

  • 瞭解Django中aggregate和annotate函數的使用方法
  • 獲取一個Django+Echarts繪製柱狀圖的完整示例

需求說明

一張會議記錄表,裏邊有一個字段存放會議舉行的地點,例如北京、上海、洛陽等等,需要取舉行會議最多的前20個地點繪製成柱狀圖展示,項目爲前後端分離的架構

需求分析

看了需求主要有三個關鍵點:

1.前後端分離:前端只負責頁面渲染,後端提供API負責數據輸出

2.需要繪製成柱狀圖:繪製圖表的第三方插件有很多,我們這裏就選擇百度開源的echarts,簡單好用且功能強大

3.取舉行會議最多的前20個地點:瞭解一點SQL知識的話就知道需要先要對地點字段進行group by,然後order by desc倒序,最後limit取前20

那麼在Django中應該如何group by,並在group by之後order by排序,最後limit呢?這裏我們介紹django的兩個函數 aggregate 和 annotate

aggregate

aggregate聚合函數,用於對QuerySet整個對象結果的彙總,例如獲取員工總數(COUNT),平均(AVG)年齡,最大(MAX)年齡,最小(MIN)年齡,銷售總額(SUM)等,輸出的結果是一個字典

我們有一個model如下:

class Employee(models.Model):
  name = models.CharField(max_length=32, verbose_name='姓名')
  age = models.IntegerField(verbose_name='年齡')
  salary = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='薪資')

想要獲取員工的工資總額,我們可以這樣寫

>>> from django.db.models import Sum

>>> Employee.objects.aggregate(Sum('salary'))
{'salary__sum': Decimal('5000.00')}

想要同時獲取員工的平均年齡、最大年齡和最小年齡,我們可以這樣寫

>>> from django.db.models import Avg, Max, Min

>>> Employee.objects.aggregate(Avg('age'), Max('age'), Min('age'))
{'age__avg': 23.333333333333332, 'age__max': 30, 'age__min': 18}

annotate

annotate函數區別於aggregate函數的一個最重要的地方是annotate函數 輸出的結果是一個QuerySet對象 ,這個非常重要,aggregate函數最後輸出的結果是個字典,也就不能再在字典的基礎上進行QuerySet操作了,而annotate函數執行完成後輸出QuerySet對象可以繼續調用Django內置的filter、order_by等函數來完成更加複雜的查詢計算操作

用到annotate函數的邏輯往往比較複雜,Django非常人性化的提供了query方法,方便查看annotate生成的SQL語句幫助我們確定執行過程

以上邊的實際需求爲例,model如下:

class EventInfo(models.Model):
  event_location = models.CharField(max_length=30)

  class Meta:
    db_table = "app_event_info"

我們需要先對地點event_location進行group by:

>>> _t = EventInfo.objects.values_list('event_location').annotate(Count('id'))

# values_list可以獲取evnet_location的元組列表。
# values_list方法加個參數flat=True可以獲取event_location的值列表。

group by之後我們就需要order by排序了,如果我們不知道order by的字段,我們可以通過query先查看group by生成的SQL語句

>>> print(_t.query)
SELECT "app_event_info"."event_location", COUNT("app_event_info"."id") AS "id__count" FROM "app_event_info" GROUP BY "app_event_info"."event_location"

這個時候可以看到實際上輸出的結果有一個叫 id__count 的字段表示地點的總數,那麼我們就可以接着對地點總數進行排序了,因爲是要倒敘,需要在字段名 id__count 前邊加上 - 號來表示倒序

>>> _x = _t.order_by('-id__count')
>>>
>>> print(_x.query)
SELECT "app_event_info"."event_location", COUNT("app_event_info"."id") AS "id__count" FROM "app_event_info" GROUP BY "app_event_info"."event_location" ORDER BY "id__count" DESC

最後limit取前二十,Django中limit可以直接通過QuerySet結果後加python的數組切片語法來實現,就像[0:20](其中0可以省略)相當於limit 20一樣,[10:20]意思爲取第10到第20條數據

>>> _y = _x[:20]
>>>
>>> print(_y.query)
SELECT "app_event_info"."event_location", COUNT("app_event_info"."id") AS "id__count" FROM "app_event_info" GROUP BY "app_event_info"."event_location" ORDER BY "id__count" DESC LIMIT 20

上邊的每一步我們都通過query打印了SQL,確定是我們想要的結果了。需求分析清楚,所有的關鍵點我們也都知道怎麼處理了,那麼接下來實現就水到渠成了。

實現代碼

URL如下:

from django.urls import path
from django.views.generic.base import TemplateView

from .views import echarts_data
urlpatterns = [
  path('echarts/', TemplateView.as_view(template_name='echarts.html'), name='echarts-url'),

  path('api/echarts/', echarts_data, name='api-echarts')
]

因爲是前後端分離的,所以我這裏用了兩個url echarts api/echarts

echarts 爲前臺訪問地址,對應下邊的html代碼,通過ajax方式調用後端接口,所以這裏直接用了TemplateView,不需要再寫額外的view代碼

api/echarts 爲後端API的地址,對應下邊的view代碼,爲前臺提供數據接口

前端HTML:

<!DOCTYPE html>
<html>
<head>
 <meta charset="utf-8">
 <title>ops-coffee</title>
 <!-- 引入 echarts.js -->
 <script src="/static/js/jquery.min.js"></script>
 <script src="/static/js/echarts/echarts.common.min.js"></script>
</head>
<body>
 <!-- 爲ECharts準備一個具備大小(寬高)的Dom -->
 <div id="main" style="height:400px;"></div>
 <script type="text/javascript">
  // 基於準備好的dom,初始化echarts實例
  var myChart = echarts.init(document.getElementById('main'));


  $.ajax({
   type: "get",
   url: "/api/echarts",
   dataType: "json",
   success: function (data) {

    // 指定圖表的配置項和數據
    var option = {
     title: {
      left: 'center',
      text: 'ops-coffee 運維咖啡吧'
     },
     tooltip: {},
     xAxis: {
       data: data.key
     },
     yAxis: {},
     series: [{
      name: '數量',
      type: 'bar',
      data: data.value
     }]
    };

    // 使用剛指定的配置項和數據顯示圖表。
    myChart.setOption(option);
   },
   error: function () {
    alert('Error: ajax 請求出錯!')
   }
  });
 </script>
</body>
</html>

實例比較簡單,抄的echarts官方示例,這裏會看到echarts渲染圖形實際上只需要X軸和Y軸兩個數據變量,且都爲list列表類型

後端VIEW:

from django.http import JsonResponse
from django.db.models import Count

from .models import EventInfo

def echarts_data(request):
  _x = EventInfo.objects.values_list('event_location').annotate(Count('id')).order_by('-id__count')[:20]

  jsondata = {
    "key": [i[0] for i in _x],
    "value": [i[1] for i in _x]
  }

  return JsonResponse(jsondata)

最核心的那行group by + order by + limit的ORM拼接,我們上邊已經詳細的介紹過了,那麼這裏只需要在輸出的結果中單獨的把城市跟數量轉成兩個列表對應echarts裏邊需要的X軸Y軸數據就可以了

最後訪問url: https://ops-coffee.cn/echarts 可以看到我們想要的結果

整個Demo示例介紹完成。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持神馬文庫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章