从零开始的Django框架入门到实战教程(内含实战实例) - 11 利用echart和Ajax请求完成数据可视化(学习笔记)

目录

  • 0. 既有工作
  • 1. 结果展示
  • 2. 页面设计
  • 3. 柱状图
  • 4. 饼图
  • 5. 折线图

  Django是目前比较火爆的框架,之前有在知乎刷到,很多毕业生进入大厂实习后因为不会git和Django框架3天就被踢掉了,因为他们很难把自己的工作融入到整个组的工作中。因此,我尝试自学Django并整理出如下笔记。
  主要内容包括echart插件最基本的使用(套模板),Ajax传输请求和数据,ORM和pandas数据统计与分析。
  作为一个管理系统,数据统计功能是必不可少的,系统中数据庞杂,若无一个较好的可视化界面,个人认为这个管理系统还存在很大的提升空间。
  数据可视化离不开图表,方便起见,我将使用插件完成简单的图标制作。介绍两个比较大的图表插件:

  • highchart(Highcharts):国外的,不过也很好用。现在也出了中文文档

  • echarts(ECharts):国内的,也很不错,百度开发的。我们用echarts完成接下来的演示。

0. 既有工作

  我们在此之前已经完成了较多工作,详情关注博主的Django专栏。我们接下来的工作也是在原来的页面的基础上进行的。

  如上图所示,我们已经完成了管理员账户、部门管理、用户管理、靓号管理、任务管理、订单管理的界面,我们直接基于这几个界面完成接下来的数据统计工作。

  先从简单的图表入手,我们计划完成柱状图、折现图、饼图的绘制。

1. 结果展示

  先浅浅展示下结果

2. 页面设计

  我们在完成简单的几种图之前,首先应该在原有的系统中设计新的界面用于数据统计。

  这部分的思路还是传统的Django设计流程:首先修改模板html(我的是叫 layout.html 在其中添加一个能到达数据统计界面的按钮),然后在Django的urls.py文件中添加新的路径(Django会通过这个路径管理网址以及每个页面对应的后端函数),接下来完成views.py中对应的函数(函数有返回值,这里的返回值就是html文件),根据上一步的返回值完成对应的html文件(前端设计)。

  修改模板htmllayout.html ),修改后我们的界面顶部导航栏多了一个数据统计链接,点击后可以访问数据统计页面(

  • 数据统计
  • )。

    {% load static %}
    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Olsentitle>
        <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
        <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap-datetimepicker.min.css' %}">
        <style>
            .navbar{
                border-radius: 0;
            }
        style>
    head>
    <body>
    
    <nav class="navbar navbar-default">
        
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                        data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">Toggle navigationspan>
                    <span class="icon-bar">span>
                    <span class="icon-bar">span>
                    <span class="icon-bar">span>
                button>
                <a class="navbar-brand" href="#">Olsen用户管理系统a>
            div>
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav">
                    <li><a href="/admin/list">管理员账户a>li>
                    <li><a href="/depart/list">部门管理a>li>
                    <li><a href="/user/list">用户管理a>li>
                    <li><a href="/num/list">靓号管理a>li>
                    <li><a href="/task/list">任务管理a>li>
                    <li><a href="/order/list">订单管理a>li>
                    <li><a href="/chart/list">数据统计a>li>
                ul>
                <ul class="nav navbar-nav navbar-right">
    
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                           aria-expanded="false">{{ request.session.info.username }} <span class="caret">span>a>
                        <ul class="dropdown-menu">
                            <li><a href="#">个人资料a>li>
                            <li><a href="#">我的信息a>li>
                            <li role="separator" class="divider">li>
                            <li><a href="/logout">注销a>li>
                        ul>
                    li>
                ul>
            div>
        div>
    nav>
    
    <div>
        {% block content %}
        {% endblock %}
    div>
    
    
    <script src="{% static 'js/jquery.js' %}">script>
    <script src="{% static 'plugins/bootstrap-3.4.1-dist/js/bootstrap.min.js' %}">script>
    {% block js %}
    {% endblock %}
    
    body>
    html>
    

      添加路径urls.py)。可以看到我们已经完成了很多的工作,只需要在路径列表中再加一条path('chart/list', chart.chart_list)

    from django.urls import path
    from application01.views import depart, num, user, admin, account, task, order, chart
    
    urlpatterns = [
        # path('admin/', admin.site.urls),
        path('depart/list', depart.depart_list),
        path('depart/add', depart.depart_add),
        path('depart/del', depart.depart_del),
        path('depart//edit', depart.depart_edit),
        path('user/list', user.user_list),
        path('user/add', user.user_add),
        path('user/madd', user.user_madd),
        path('user//edit', user.user_edit),
        path('user//del', user.user_del),
        path('num/list', num.num_list),
        path('num/add', num.num_add),
        path('num/a', num.num_a),
        path('num//edit', num.num_edit),
        path('num//del', num.num_del),
        path('admin/list', admin.admin_list),
        path('admin/add', admin.admin_add),
        path('admin//edit', admin.admin_edit),
        path('admin//del', admin.admin_del),
        path('admin//reset', admin.admin_reset),
        path('login', account.login),
        path('logout', account.logout),
        path('image/code', account.image_code),
        path('task/list', task.task_list),
        path('task/add', task.task_add),
        path('order/list', order.order_list),
        path('order/add', order.order_add),
        path('order/delete', order.order_delete),
        path('order/detail', order.order_detail),
        path('order/edit/', order.order_edit),
        path('chart/list', chart.chart_list)
    ]
    

      完成views.py中函数。因为界面比较多,我把原来的views.py拆成好几个文件,分别对应几个数据表,相关目录如下:

    从零开始的Django框架入门到实战教程(内含实战实例) - 11 利用echart和Ajax请求完成数据可视化(学习笔记)_第1张图片

      我们这里只是规划一个界面,先不整那些花里胡哨的东西,简简单单返回一个页面就好了。

    # -*- coding:utf-8 -*-
    from django.shortcuts import render
    
    def chart_list(request):
        return render(request, 'chart_list.html')
    

      前端设计。继承在第一步写好的模板,再加上一点bootstrap官网里的模板代码。

    {% extends 'layout.html' %}
    
    {% block content %}
    <div class="container">
        <div class="panel panel-default">
            <div class="panel-heading">折线图div>
            <div class="panel-body">
                ————————————---------
            div>
        div>
        <div class="row">
            <div class="col-sm-8">
                <div class="panel panel-default">
                    <div class="panel-heading">柱状图div>
                    <div class="panel-body">
                        1 1 11 <br>
                        1 1 11 <br>
                        1 1111 <br>
                        1111111<br>
                    div>
                div>
            div>
            <div class="col-sm-4">
                <div class="panel panel-default">
                    <div class="panel-heading">饼图div>
                    <div class="panel-body">
                        O
                    div>
                div>
            div>
        div>
    div>
    
    {% endblock %}
    

      最后,我们完成了页面的规划,最顶上方折线图,饼图和柱状图在下面共用一行。

    从零开始的Django框架入门到实战教程(内含实战实例) - 11 利用echart和Ajax请求完成数据可视化(学习笔记)_第2张图片

    3. 柱状图

      注意,首先你必须要下载Echarts的js文件(Echarts),然后在echart官网中直接挑一款比较喜欢的图自己修改下配置即可。

      我们意在统计不同部门的员工的平均年龄和部门员工数量。

    {% extends 'layout.html' %}
    {% load static %}
    {% block content %}
    <div class="container">
        <div class="panel panel-default">
            <div class="panel-heading">折线图div>
            <div class="panel-body">
                <div id="l2" style="width: 100%;height:400px;">div>
            div>
        div>
        <div class="row">
            <div class="col-sm-7">
                <div class="panel panel-default">
                    <div class="panel-heading">柱状图div>
                    <div class="panel-body">
                        <div id="m2" style="width: 100%;height:400px;">div>
                    div>
                div>
            div>
            <div class="col-sm-5">
                <div class="panel panel-default">
                    <div class="panel-heading">饼图div>
                    <div class="panel-body">
                        <div id="p1" style="width: 100%;height:400px;">div>
                    div>
                div>
            div>
        div>
    
    div>
    
    {% endblock %}
    
    {% block js %}
    <script src="{% static 'js/echarts.js' %}">script>
    <script type="text/javascript">
        $(function () {
            initBar();
        })
    
        function initBar() {
            var myChart = echarts.init(document.getElementById('m2'));
            option_bar = {
                title: {
                    text: '员工员工基本信息概览',
                    textAlign: 'Auto',
                    left: 'center',
                },
                legend: {
                    data:[],
                    bottom: 0,
                },
                tooltip: {},
                xAxis: [{
                    type: 'category',
                    data: [],
                }],
                yAxis: [
                    {
                        type: 'value',
                        name:'平均年龄',
                    },
                    {
                        type: 'value',
                        name:'人数',
                    }
                ],
                series: [
                    {
                        data: [],
                        type: 'bar'
                    }
                ]
            }
            $.ajax({
                url: "/chart/bar",
                type: "get",
                dataType: "JSON",
                success: function (res_bar) {
                    if(res_bar.status){
                        option_bar.legend.data = res_bar.data.legend;
                        option_bar.series = res_bar.data.series_list;
                        option_bar.xAxis[0].data = res_bar.data.xAxis;
                        myChart.setOption(option_bar);
                    }
                }
            })
        };
    
    script>
    {% endblock %}
    

      后端数据处理代码如下,这边采取pandas完成输出的统计。

    def chart_bar(request):
        row_obj = pd.DataFrame(models.User.objects.all().values("age", "Department__title"))
        data = row_obj.groupby("Department__title")['age'].describe()
        legend = ['平均年龄', '部门人数']
        series_list = [
            {
                'name': '平均年龄',
                'type': 'bar',
                'data': data['mean'].tolist(),
            },
            {
                'name': '部门人数',
                'type': 'bar',
                'yAxisIndex': 1,
                'data': data['count'].tolist(),
            },
        ]
        xAxis = data.index.tolist()
        result = {
            "status": True,
            "data": {
                "legend": legend,
                "xAxis": xAxis,
                "series_list": series_list,
            }
        }
        return JsonResponse(result)
    

      效果如下

    4. 饼图

      利用饼图展示订单状态。

      部分html代码如下,为模板语言中js部分的代码

    {% block js %}
    <script src="{% static 'js/echarts.js' %}">script>
    <script type="text/javascript">
        $(function () {
            initPie();
        })
        
        function initPie() {
            var myChart = echarts.init(document.getElementById('p1'));
            option = {
                tooltip: {
                    trigger: 'item'
                },
                title: {
                    text: '订单支付状态',
                    textAlign: 'Auto',
                    left: 'center',
                },
                legend: {
                    top: '90%',
                    left: 'center'
                },
                series: [
                    {
                        name: 'Access From',
                        type: 'pie',
                        radius: ['40%', '70%'],
                        avoidLabelOverlap: false,
                        itemStyle: {
                            borderRadius: 10,
                            borderColor: '#fff',
                            borderWidth: 2
                        },
                        label: {
                            show: false,
                            position: 'center'
                        },
                        emphasis: {
                            label: {
                                show: true,
                                fontSize: '40',
                                fontWeight: 'bold'
                            }
                        },
                        labelLine: {
                            show: false
                        },
                        data: [
                            // { value: 1048, name: 'Search Engine' },
                        ]
                    }
                ]
            };
            $.ajax({
                url: "/chart/pie",
                type: "get",
                dataType: "JSON",
                success: function (res) {
                    if(res.status){
                        option.series[0].data = res.data.series_list;
                        myChart.setOption(option);
                    }
                }
            })
        };
    script>
    {% endblock %}
    

      数据处理用比较简单的ORM语言完成。

    def chart_pie(request):
        choice = dict(models.Order.status_choices)
        series_list = models.Order.objects.values('status').annotate(count=Count('status'))
        series_list = [{'name': choice[i["status"]], 'value':i['count']} for i in series_list]
        result = {
            "status": True,
            "data": {
                "series_list": series_list,
            }
        }
        return JsonResponse(result)
    

    5. 折线图

      展示下结果

      折线图展示的是不同尾号的手机号码在不同VIP等级下的平均价格。

      部分html代码如下,为模板语言中js部分的代码

    {% block js %}
    <script src="{% static 'js/echarts.js' %}">script>
    <script type="text/javascript">
        $(function () {
            initLine();
        })
    
        function initLine() {
            var myChart = echarts.init(document.getElementById('l2'));
            option_line = {
                title: {
                    text: '号码价格信息概览',
                    textAlign: 'Auto',
                    left: 'center',
                },
                tooltip: {
                    trigger: 'axis'
                },
                legend: {
                    data: [],
                    orient: 'vertical',
                    x:'right',      //可设定图例在左、右、居中
                    y:'center',     //可设定图例在上、下、居中
                    padding:[0,20,0,0],
                    // data: ['Email', 'Union Ads', 'Video Ads', 'Direct', 'Search Engine']
                },
                grid: {
                    top: '10%',
                    left: '3%',
                    right: '10%',
                    bottom: '1%',
                    containLabel: true
                },
                toolbox: {
                    feature: {
                        saveAsImage: {}
                    }
                },
                xAxis: [{
                    type: 'category',
                    boundaryGap: false,
                    // data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
                }],
                yAxis: {
                    type: 'value'
                },
                series: [
                ]
            };
            $.ajax({
                url: "/chart/line",
                type: "get",
                dataType: "JSON",
                success: function (res_line) {
                    if(res_line.status){
                        option_line.legend.data = res_line.data.legend;
                        option_line.series = res_line.data.series_list;
                        option_line.xAxis[0].data = res_line.data.xAxis;
                        console.log(res_line);
                        myChart.setOption(option_line);
                    }
                }
            })
        };
    script>
    {% endblock %}
    

      特意统计了个比较复杂的东西,还是用ORM语言完成。

    def chart_line(request):
        series_list = [models.YourNum.objects.filter(mobile__endswith=i) for i in range(10)]
        series_list = [end_num.values('level').annotate(avg=Avg('price')).order_by("level") for end_num in series_list]
        series_list = [{'name': "尾号"+str(index), 'type': 'line', 'data': [level_sorted['avg'] for level_sorted in end_with]} for index, end_with in enumerate(series_list)]
        result = {
            "status": True,
            "data": {
                "xAxis": [str(i+1)+"级" for i in range(5)],
                "legend": [i['name'] for i in series_list],
                "series_list": series_list,
            }
        }
        # print(result)
        return JsonResponse(result)
    

    你可能感兴趣的:(#,Django,前端,django,ajax,学习)