全部演示均基于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示例介绍完成。
若是你以为文章对你有帮助,请转发分享给更多的人。若是你以为读的不尽兴,推荐阅读如下文章: