來源:https://www.cnblogs.com/37Y37/p/10018397.html
所有演示均基於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') ]
因為是前后端分離的,所以我這里用了兩個urlecharts
和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:
文章未完,全部內容請關注公眾號【運維咖啡吧】或個人網站https://ops-coffee.cn查看,運維咖啡吧專注於原創精品內容分享,感謝您的支持