Djangoで個人開発している資産シュミレーションアプリで、毎年の配当金額をグラフで表示
したかったのでChart.jsで実装しました。
実装にあたり、Djangoのデータ送信に苦戦して試行錯誤していたのでデータ送信の方法など
をまとめました。
Chart.jsとは何か
Javascriptのライブラリの一つで、棒グラフや折れ線グラフなど様々な種類のグラフを
手軽に作成できる。(全然手軽じゃなかったけど・・・)
Chart.jsの導入方法
Chart.jsを導入する手順は以下です。
- <head>タグでChart.jsを読み込む
- グラフを表示したい場所に<canvas>タグを記述する
- <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プロパティへの受け渡しがうまくいきませんでしたが、プロパティのlabelsとdataの
値を文字列としてダブルクオーテーションで囲っていたのが原因でした。
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>
最終的に完成したグラフは以下です。フォームに入力した値によって
グラフ表示する年数や縦軸の配当金額が変わるようになっています。