Django:グラフ描写ツール Echartsで要素数が変動するグラフを描く方法

Django:グラフ描写ツール Echartsで要素数が変動するグラフを描く方法

前回記事でも書きましたが、要素数が決まっていればそれほど手間ではありません。
でも、引きたいグラフラインの数=要素数が不確定な場合がどうしても出てきます。

例えば、1社で楽天市場に3店舗出してるとか。
モール数だとほぼ固定でも店舗数になると一気に不確定になってしまいました。
と言う事で、可変する要素数の時にJavaScript側に送る方法の実装例です。

基本的な段取り

大きな流れとしては下のように動きます。

  1. views.py内で企業別の店舗リストを取得する
  2. views.py内で店舗毎にグラフ化したい値(売上高とか)を用意する
  3. 店舗とグラフ化したい値を関連付けた状態でTemplate.htmlに送る
  4. Template内で少し成形しJavaScriptに送る
  5. JavaScriptでグラフにはめる形に調整して読込

views.py内で成形したデータの形

Template.htmlに送るデータはこんな形に作りました。

(前略) 
  shop_sales = []
   shop_sales.append({'name': "楽天_A店", 'data': [100,500,50,320,0,800,300,600]})
   shop_sales.append({'name': "楽天_B店", 'data': [4500,2000,430,700,500,320,3000,600]})
   shop_sales.append({'name': "Amazon", 'data': [5000,4000,7000,4500,8000,6000,3000,3200]})
   shop_sales.append({'name': "Yahoo_A店", 'data': [40,320,700,500,300,60,450,800]})
   shop_sales.append({'name': "auPay_A店", 'data': [60,70,50,45,80,30,32,40]})
  date_span = ['5月','6月','7月','8月','9月','10月','11月','12月']
    
  params = {
    'shop_sales':shop_sales,
    'date_span' :date_span
    }

  return render(request, 'show.html', params)

今回はグラフを書く為のテストデータなのでappendで配列追加してますが、本来はSQLこねて形成です。

変更するJavaScriptの中身

グラフに描写するための情報は先の記事にも書いた【static/build/js/custom.js】が担当しています。
まずは今回使うグラフ描写の【Bootstrapテンプレート『gentelella』】での該当箇所を抽出してみます。

    //echart Line

    if ($('#echart_line').length) {

      var echartLine = echarts.init(document.getElementById('hogehoge'), theme);
      echartLine.setOption({
          title: {text: '売上月次集計', subtext: 'Subtitle'},
          tooltip: {trigger: 'axis'},
          legend: {
              x: 220,
              y: 40,
              data: ['Intent', 'Pre-order', 'Pre-order2', 'Deal', 'Deal2']
          },
          toolbox: {
              show: true,
              feature: {
                  magicType: {
                      show: true,
                      title: {line: 'Line', bar: 'Bar', stack: 'Stack', tiled: 'Tiled'},
                      type: ['line', 'bar', 'stack', 'tiled']
                  },
                  restore: {
                      show: true,
                      title: "Restore"
                  },
                  saveAsImage: {
                      show: true,
                      title: "Save Image"
                  }
              }
          },
          calculable: true,
          xAxis: [{
              type: 'category',
              boundaryGap: false,
              data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 's2']
          }],
          yAxis: [{
              type: 'value'
          }],
          series: [
            {
                name: 'Deal',
                type: 'line',
                smooth: true,
                itemStyle: {
                    normal: {
                        areaStyle: {
                            type: 'default'
                        }
                    }
                },
                data: [10, 12, 21, 54, 260, 830, 710]
            }, {
                name: 'Pre-order',
                type: 'line',
                smooth: true,
                itemStyle: {
                    normal: {
                        areaStyle: {
                            type: 'default'
                        }
                    }
                },
                data: [30, 182, 434, 791, 390, 30, 10]
            }, {
                name: 'Intent',
                type: 'line',
                smooth: true,
                itemStyle: {
                    normal: {
                        areaStyle: {
                            type: 'default'
                        }
                    }
                },
                data: [110, 22, 301, 4, 100, 1000, 200]
            }, {
                name: 'Deal2',
                type: 'line',
                smooth: true,
                itemStyle: {
                    normal: {
                        areaStyle: {
                            type: 'default'
                        }
                    }
                },
                data: [320, 132, 201, 534, 420, 90, 20]
            }, {
                name: 'Pre-order2',
                type: 'line',
                smooth: true,
                itemStyle: {
                    normal: {
                        areaStyle: {
                            type: 'default'
                        }
                    }
                },
                data: [1320, 1132, 601, 234, 120, 90, 20]
            }]
        });
    }

この中で項目数の増減で可変するパーツは次の3か所(3パーツ)です。

  1. 12行目:横軸項目
  2. 36行目:プロットする値のラベル
  3. 41行目~102行目:グラフにプロットする値とスタイル

変更後のcustom.jp

    //echart Line

    if ($('#echart_line').length) {
      var echartLine = echarts.init(document.getElementById('hogehoge'), theme);
      echartLine.setOption({
          title: {text: '売上月次集計', subtext: 'Subtitle'},
          tooltip: {trigger: 'axis'},
          legend: {
              x: 220,
              y: 40,
              data: shops//※変更箇所
          },
          toolbox: {
              show: true,
              feature: {
                  magicType: {
                      show: true,
                      title: {line: 'Line', bar: 'Bar', stack: 'Stack', tiled: 'Tiled'},
                      type: ['line', 'bar', 'stack', 'tiled']
                  },
                  restore: {
                      show: true,
                      title: "Restore"
                  },
                  saveAsImage: {
                      show: true,
                      title: "Save Image"
                  }
              }
          },
          calculable: true,
          xAxis: [{
              type: 'category',
              boundaryGap: false,
              data: date_span//※変更箇所
          }],
          yAxis: [{
              type: 'value'
          }],
          series: JSON.parse(plot_data),//※変更箇所
        });
    }

スッキリしました。まぁ送った値を呼び出してるだけですからね。

注意点

JSON.parse()はJSONにエンコードするJavaScriptの命令です。
これは、後述するテンプレート内でのデータ操作が原因で必要になっています。

show.html(template.html)scriptタグ内に書き入れた事

  <script>
    let echart_data = [];
    let shops = [];

    {% for list in shop_sales %}
        //plotする情報を成形
        ec_line_base = {};
        ec_line_base['name'] = '{{list.name|safe}}';
        ec_line_base['type'] = 'line';
        ec_line_base['smooth'] = true;
        ec_line_base['itemStyle'] = {normal: {areaStyle: {type: 'default'}}};
        ec_line_base['data'] = {{list.data}};
        echart_data.push(ec_line_base);
        //店舗名リストを作成
        shops.push({{list.name}});
    {% endfor %}

    let plot_data = JSON.stringify(echart_data);
  </script>

色々な姿で実装してみたのですが、この形が一番理解しやすかったです。
でも、でっかい問題が...。

作成したリスト【echart_data】をそのままJavaScriptで読込むとエラーとなりグラフが描写されない。

理由は全く見えなかったのですが、JSONデコードして再エンコードすると何故かplotしてくれました。
それが前段の【JSON.parse(plot_data)】と今回の【plot_data = JSON.stringify(echart_data)】です。

やってる事はviews.pyでもできる事。ならばviewsで作業しよう

実際グラフを書いてくれるようにはなりましたが、如何せんモタツキを感じます。

「じゃぁviewsで書けばいんじゃね?」
Template内で書いた内容はここで書かないといけない内容ではないのでviews.pyに持っていくことにしました。
その内容の該当箇所がコレ。

(前略)
      echart_data = []
      shops = []
      for list in shop_sales:
        ec_line_base = {}
        ec_line_base['name'] = list['name']
        ec_line_base['type'] = 'line'
        ec_line_base['smooth'] = 'true'
        ec_line_base['itemStyle'] = {'normal': {'areaStyle': {'type': 'default'}}}
        ec_line_base['data'] = list['data']
        echart_data.append(ec_line_base)

        shops.append(list['name'])

      plot_data = json.dumps(echart_data)
(後略)

15行目はJSONエンコードです。

で、これを読み込むにはcustom.js側も一部変更します。

    //echart Line

    if ($('#echart_line').length) {
   (中略)
          yAxis: [{
              type: 'value'
          }],
          series: plot_data,//※変更箇所
        });
    }

JSON化した値を渡したので、再エンコードの必要なくそのまま読ますことができます。
これで動作の構成は次のように変化。

  1. views.py内で企業別の店舗リストを取得する
  2. views.py内で店舗毎にグラフ化したい値(売上高とか)を用意する
  3. views.py内でグラフ用のデータを成形、JSONでエンコード
  4. TemplateからJavaScriptに変数を送る
  5. JavaScriptで読込

シンプルになり速度も向上しました。

まとめ

色々やってみて謎が1つあります。
EchartsのplotデータがJSONを要求している事はよくわかりました。

TemplateでJSONに成型してJavaScriptに送ってもグラフを書いてくれなかったのは何故?

これだけがどうも納得できず『謎』のまま。
まぁviews.pyで書いちゃったからもういいんですけどね。

\ 最新情報をチェック /

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください