pyhaya’s diary

プログラミング、特にPythonについての記事を書きます。Djangoや機械学習などホットな話題をわかりやすく説明していきたいと思います。

Djangoで家計簿のWebアプリケーションを作る 4 日ごとの支出額を可視化する

Djangoで家計簿のアプリケーション作ってみた、という記事の4つ目です。今回は日ごとの支出をmatplotlibでグラフ化します。Webアプリでグラフを作る場合にはJavascriptに便利なツールがそろっているのですが、Javascriptは現在勉強中なので今回はmatplotlibでグラフを作ってみます。

過去記事は下のリンクをどうぞ

pyhaya.hatenablog.com

pyhaya.hatenablog.com

pyhaya.hatenablog.com

開発環境

構成

views.py中にグラフを書いて保存するコードを書きます。画像の保存形式は画質がいいのでSVGを使います。この関数をビューを表示するときに動くindexが呼ばれるたびに実行するようにします。

グラフを書く

money/views.py

import calendar
import datetime
from django.shortcuts import render, redirect
from django.utils import timezone
import matplotlib.pyplot as plt
import pytz

from .models import Money
from .forms import SpendingForm

plt.rcParams['font.family'] = 'IPAPGothic'    #日本語の文字化け防止

# Create your views here.
TODAY = str(timezone.now()).split('-')

def index(request, year=TODAY[0], month=TODAY[1]):
    money = Money.objects.filter(use_date__year=year,
            use_date__month=month).order_by('use_date')
    total = 0
    for m in money:
        date = str(m.use_date).split(' ')[0]
        m.use_date = '/'.join(date.split('-')[1:3])

        total += m.cost

    form = SpendingForm()
    context = {'year' : year,
            'month' : month,
            'money' : money,
            'total' : total,
            'form' : form 
    }

    draw_graph(year, month)    #追加

    if request.method == 'POST':
        data = request.POST
        use_date = data['use_date']
        cost = data['cost']
        detail = data['detail']
        category = data['category']

        use_date = timezone.datetime.strptime(use_date, "%Y/%m/%d")
        tokyo_timezone = pytz.timezone('Asia/Tokyo')
        use_date = tokyo_timezone.localize(use_date)
        use_date += datetime.timedelta(hours=9)

        Money.objects.create(
                use_date = use_date,
                detail = detail,
                cost = int(cost),
                category = category,
                )
        return redirect(to='/money/{}/{}'.format(year, month))

    return render(request, 'money/index.html', context)

def draw_graph(year, month):    #追加
    money = Money.objects.filter(use_date__year=year,
            use_date__month=month).order_by('use_date')

    last_day = calendar.monthrange(int(year), int(month))[1] + 1
    day = [i for i in range(1, last_day)]
    cost = [0 for i in range(len(day))]
    for m in money:
        cost[int(str(m.use_date).split('-')[2])-1] += int(m.cost)
    plt.figure()
    plt.bar(day, cost, color='#00bfff', edgecolor='#0000ff')
    plt.grid(True)
    plt.xlim([0, 31])
    plt.xlabel('日付', fontsize=16)
    plt.ylabel('支出額(円)', fontsize=16)
    #staticフォルダの中にimagesというフォルダを用意しておきその中に入るようにしておく
    plt.savefig('money/static/images/bar_{}_{}.svg'.format(year, month),
            transparent=True)

    return None

今回追加したのは2か所だけです。

テンプレートを更新する

ビューに合わせてテンプレートを書き直します。

money/templates/money/index.html

<!DOCTYPE html>    
{% load static %}
<html>
    <head>
        <meta charset="utf-8">
        <title>HousekeepingBook</title>
        <link rel="stylesheet" type="text/css"
        href="{% static 'money/style.css' %}">
    </head>
    <body>
        <h1>{{ year }}年{{ month }}月</h1>
        <form action="/money/" method="post">
            {% csrf_token %}
            {{ form.as_table }}
            <input type="submit" value="送信">
        </form>
        <div class="wapper">
            <div class="main">
                <table>
                    <tr>
                        <th>日付</th>
                        <th>用途</th>
                        <th>カテゴリー</th>
                        <th>金額</th>
                    </tr>
                    {% for m in money %}
                    <tr>
                        <td>{{ m.use_date }}</td>
                        <td>{{ m.detail }}</td>          
                        <td>{{ m.category }}</td>
                        <td>{{ m.cost }}円</td>
                    </tr>
                    {% endfor %}
                </table>
                <div class="tot">
                    合計:{{ total }}円
                </div>
            </div>
            <div class="main">
                <img src="/static/images/bar_{{ year }}_{{ month }}.svg"
                width=80%
            </div>
        </div>
    </body>
</html>

imgタグの画像ファイルへのパスは、{% static %}を使うのが良いのでしょうが、今回はファイル名に{{ year }}{{ month }}といった変数が入ってくるので、ふつうに書きました。

ここまでのページの感じ

f:id:pyhaya:20181110094204p:plain

次回記事
pyhaya.hatenablog.com