• 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的两个函数aggregateannotate

    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')
    ]
    

    因为是前后端分离的,所以我这里用了两个urlechartsapi/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查看,运维咖啡吧专注于原创精品内容分享,感谢您的支持

    扫码关注公众号查看更多实用文章

  • 相关阅读:
    editable : false与 readonly 的区别
    Ubuntu环境下使用Jupyter Notebook查找桌面.csv文档的方法
    实验01——java模拟银行ATM系统
    Wannafly挑战赛26 御坂网络
    Wannafly挑战赛29 御坂美琴(递归,模拟)
    牛客练习赛31 龙魂合一萨坎与晶石
    牛客练习赛31
    Codeforces Round #520 (Div. 2)A. A Prank
    (花里胡哨)New Game!(牛客国庆集训派对Day1)
    Educational Codeforces Round 54 (Rated for Div. 2)A. Minimizing the String(签到题)
  • 原文地址:https://www.cnblogs.com/37Y37/p/10018397.html
Copyright © 2020-2023  润新知