【Django】Chart.jsを使ってDjangoにグラフ描画する方法

IT、仕事

Djangoで個人開発している資産シュミレーションアプリで、毎年の配当金額をグラフで表示
したかったのでChart.jsで実装しました。

実装にあたり、Djangoのデータ送信に苦戦して試行錯誤していたのでデータ送信の方法など
をまとめました。

Chart.jsとは何か

Javascriptのライブラリの一つで、棒グラフや折れ線グラフなど様々な種類のグラフを
手軽に作成できる。(全然手軽じゃなかったけど・・・)

Chart.js
Simple yet flexible JavaScript charting library for the modern web

Chart.jsの導入方法

Chart.jsを導入する手順は以下です。

  1. <head>タグでChart.jsを読み込む
  2. グラフを表示したい場所に<canvas>タグを記述する
  3. <script>タグの中に必要な情報を定義

<head>タグの中でChart.jsを読み込む

Chart.jsを導入するには、ローカルからダウンロードする方法と
CDNでネットワークから持ってくる二つの方法があります。

CDN・・・コンテンツ・デリバリー・ネットワークの略
      デジタルコンテンツをインターネットから大量配布するためのネットワーク

今回はCDNを採用しました。
以下のコードをグラフを描画したいHTMLファイルに追加するだけでChart.jsの機能が使えます。

 <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>

グラフを表示したい場所に<canvas>タグを記述する

bodyタグの中でグラフを表示したい場所に<canvas>タグを追加します。
タグを追加した場所にグラフが表示されます。

タグにはidを必ず追加しておきましょう。タグの例は以下です。

<canvas id="bar" height="70px"></canvas>

<script>タグの中に必要な情報を定義

グラフを表示する場所を決めたら、<script>タグの中にグラフ描画に必要な情報を定義します。
最初はDjangoで使うということを考えず、シンプルに書いてどのように表示されるか確認します。

<script type="text/javascript">
            var ctx = document.getElementById("bar");
            var myRadarChart = new Chart(ctx, {
                type: 'bar',
                data: {
                    labels: [2023,2024,2025,2026,2027]
                    datasets: [
                        {   label: "配当金",
                            data: [130000,160000,190000,210000,240000],
                            backgroundColor: 'rgba(255, 99, 132, 0.6)',
                            borderColor: 'rgba(255, 99, 132, 0.9)',
                            pointBackgroundColor: 'rgba(255, 99, 132, 0.9)',
                            pointBorderColor: 'rgba(255, 99, 132, 1)',
                            borderWidth: 3,
                            pointRadius: 3,
                        }]}
            });
            </script>

type:’bar’とすることで今回は棒グラフを指定しています。
そして、dataプロパティの中で、各種設定をしていきます。

labelsの個数は自由に変更できますが、dataはそれに対応した個数を指定する必要があります。

後半部分ではグラフの色などを指定しています。
グラフの種類によっては設定できる内容が多少変わります。このサイトを参考にしてください。

上記スクリプトの設定を行うと、このような棒グラフを表示できます。
labelsがX軸、dataがY軸となっています。

このスクリプトだとデータを<script>タグに書き込まないとグラフ表示されないので、
Djangoを使ってフォームから入力した値と内部で計算したデータをHTMLに送信できるようにします。

Djangoでの使い方

ここからはDjangoでの使い方を説明します。Djangoでも基本的な部分は変わりませんが、
Viewで取った値をdataプロパティに渡す必要があります。

処理の流れは、「フォームに値入力→Viewで計算→dataプロパティに値出力」
という感じです。

dataプロパティへの受け渡しがうまくいきませんでしたが、プロパティのlabelsdata
値を文字列としてダブルクオーテーションで囲っていたのが原因でした。

from django import forms

class HelloForm(forms.Form):
    age = forms.IntegerField(label='年齢(歳)', \
        widget=forms.NumberInput(attrs={'class':'form-control'}))
    assets = forms.IntegerField(label='総資産(円)', \
        widget=forms.NumberInput(attrs={'class':'form-control'}))
    yields = forms.FloatField(label='利回り(年間)', \
        widget=forms.NumberInput(attrs={'class':'form-control'}))
    live = forms.IntegerField(label='生活費(年間)', \
        widget=forms.NumberInput(attrs={'class':'form-control'}))
    amount = forms.IntegerField(label='年間投資額(円)', \
        widget=forms.NumberInput(attrs={'class':'form-control'}))
    zohai = forms.FloatField(label='増配率(%)', \
        widget=forms.NumberInput(attrs={'class':'form-control'}))
    kabuka = forms.FloatField(label='株価成長率(%)', \
        widget=forms.NumberInput(attrs={'class':'form-control'}))
    def result(self,request):
        params = {
            'title':'結果',
            'message':'こんなもんよ',
            'graphx':'x',
            'graphy':'y',
            'g_datax':'x',
            'g_datay':'y',
            'sisan':'s',
            'rimawari':'r',
            'monthly':'m'
        }
        
        if (request.method == 'POST'):
            rimawari = float(request.POST['yields']) / 100
            haitou = int(request.POST['assets']) * rimawari
            haitou_n = int(haitou)
            fire_m = int(request.POST['live']) - haitou
            live = int(request.POST['live'])
            hai = int(request.POST['amount']) * rimawari
            zohai = float(request.POST['zohai']) / 100
            seityo = float(request.POST['kabuka']) / 100
            asset = int(request.POST['assets'])
            amount = int(request.POST['amount'])

            #配当金が生活費よりも多くなったらループ終わり
            y = []
            while(live > haitou):
                    haitou += hai+int(haitou*zohai)
                    y.append(round(haitou))
                    
            
            #年数を取得
            fire_y = len(y)
            n_date = datetime.datetime.now()
            n_year = n_date.year
            x = [n_year + n for n in range(int(fire_y))]
            
            params['graphx'] = x
            params['graphy'] = y

            #資産推移を取得
            s = []
            y_asset = asset + int(asset*seityo) + amount
            s.append(y_asset) 
            for i in range(int(fire_y)-1):
                 y_asset = y_asset + int(y_asset*seityo) + amount
                 s.append(round(y_asset))
            
            params['sisan'] = s

            #利回りを取得
            r = []
            for j in range(int(fire_y)):
                 r.append(round(y[j] / s[j] * 100,2))

            params['rimawari'] = r

            #月次配当金計算
            m = []
            for z in range(int(fire_y)):
                 m.append(round(y[z] / 12))
            
            params['monthly'] = m

            #グラフ情報
            params['g_datax'] = json.dumps(x)
            params['g_datay'] = json.dumps(y)


            #メッセージ表示
            params['message'] = '配当金(年):' + str(haitou_n) + '円'\
                '<br>年間生活費:' + str(live) + '円'\
                '<br>Fire達成まで:' + str(fire_m) + "円" + \
                '<br>Fire達成まで:' + str(fire_y)+ "年"
            
        
        return render(request,'simulation/kekka.html',params)
            
<body class="container">
    <h1 class="display-4 text-primary">{{title}}</h1>
    <p class="h5 mt-4">{{message|safe}}</p>
    <div class="row">
        <div class="col">
           <h3 class="display-4 text-primary">グラフ</h3>
           <canvas id="bar" height="70px"></canvas>
           {% block content %}
           <script type="text/javascript">
            var ctx = document.getElementById("bar");
            var myRadarChart = new Chart(ctx, {
                type: 'bar',
                data: {
                    labels: {{g_datax|safe}},
                    datasets: [
                        {   label: "配当金",
                            data: {{g_datay|safe}},
                            backgroundColor: 'rgba(255, 99, 132, 0.6)',
                            borderColor: 'rgba(255, 99, 132, 0.9)',
                            pointBackgroundColor: 'rgba(255, 99, 132, 0.9)',
                            pointBorderColor: 'rgba(255, 99, 132, 1)',
                            borderWidth: 3,
                            pointRadius: 3,
                        }]}
            });
            </script>
            {% endblock %}
        </div>

最終的に完成したグラフは以下です。フォームに入力した値によって
グラフ表示する年数や縦軸の配当金額が変わるようになっています。

タイトルとURLをコピーしました