pyhaya’s diary

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

Djangoで家計簿のWebアプリケーションを作る 3 カテゴリー選択を可能にする

家計簿アプリの続きです。これからグラフを表示することを考えて、ユーザー入力欄に支出のカテゴリーを選択する欄を追加します。

開発環境

モデルにカテゴリーを追加する。

モデルを下のように書き換えます。

money/models.py

from django.db import models

# Create your models here.
class Money(models.Model):
    use_date = models.DateField('日付')
    detail = models.CharField(max_length=200)
    cost = models.IntegerField(default=0)
    category = models.CharField(max_length=10)    # <- 追加

    def __str__(self):
        return self.detail + ' ¥' + str(self.cost)

フォームを変更する

モデルに対応して、フォームを変更します。

money/forms.py

from django import forms

from .models import Money

class SpendingForm(forms.Form):
    choices = (
            ('食費', '食費'),
            ('学費', '学費'),
            ('交通費', '交通費'),
            ('趣味', '趣味'),
            )

    use_date = forms.DateTimeField(label='日付')
    cost = forms.IntegerField(label='金額')
    detail = forms.CharField(
            max_length=200,
            label='用途'
            )
    category = forms.ChoiceField(choices=choices, label='カテゴリー')

選択肢となるものをchoicesとして最初に定義しています。('食費', '食費')などと書いているのは、データベースに登録されるときの名前と選択肢に表示される名前をそれぞれ表しています。

そして、フィールドとしては、ChoiceFieldとして追加しています。

ビューを変更する

送信ボタンが押されたときに受け取る値にもカテゴリーを追加します。

money/views.py

from django.shortcuts import render, redirect
from django.utils import timezone
import pytz
import datetime

from .models import Money
from .forms import SpendingForm

# 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
            }

    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)   

ついでに、特定の月に使った全金額も計算してしまいました。

テンプレートを変更する

最後に、テンプレートを変更していきます。

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>

        <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>
    </body>

データベースを更新する

データベースのテーブル数を変えたので、データベースの更新を行わなくてはいけません。

python manage.py makemigrations

で更新しようとすると、次のような表示が出ます。

You are trying to add a non-nullable field 'category' to money without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option:

何を言っているかというと、テーブル数増えたけど、今まで登録したデータのこの欄どうするの?ということです。1番を選択してデフォルト値を適当に設定してしまいます。

1を入力してエンターするとPythonが起動します。ここでは'食費'をデフォルト値にします。食費を入力してエンターを押すと、デフォルト値のセットが完了します。

Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> '食費'

後は、データベースへ変更を反映します。

python manage.py migrate

これで完了です。