全部演示均基於Django2.0javascript
閱讀此篇文章你能夠:html
一張會議記錄表,裏邊有一個字段存放會議舉行的地點,例如北京、上海、洛陽等等,須要取舉行會議最多的前20個地點繪製成柱狀圖展現,項目爲先後端分離的架構前端
看了需求主要有三個關鍵點:java
1.先後端分離:前端只負責頁面渲染,後端提供API負責數據輸出python
2.須要繪製成柱狀圖:繪製圖表的第三方插件有不少,咱們這裏就選擇百度開源的echarts,簡單好用且功能強大jquery
3.取舉行會議最多的前20個地點:瞭解一點SQL知識的話就知道須要先要對地點字段進行group by,而後order by desc倒序,最後limit取前20git
那麼在Django中應該如何group by,並在group by以後order by排序,最後limit呢?這裏咱們介紹django的兩個函數aggregate
和annotate
ajax
aggregate聚合函數,用於對QuerySet整個對象結果的彙總,例如獲取員工總數(COUNT),平均(AVG)年齡,最大(MAX)年齡,最小(MIN)年齡,銷售總額(SUM)等,輸出的結果是一個字典django
咱們有一個model以下:json
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函數區別於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:
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示例介紹完成。
若是你以爲文章對你有幫助,請轉發分享給更多的人。若是你以爲讀的不盡興,推薦閱讀如下文章: