Djangoで家計簿のWebアプリケーションを作ってみる
Djangoで家計簿アプリを作ります。完成イメージは、次のような感じです。
- 月ごとの支出の様子がテーブルで見られる
- 日付と金額、用途を入力してデータを登録できる
- 日ごとの支出の推移をグラフで可視化
Money Fowardでいいじゃんという突込みが聞こえてきそうですが、無視します。今回は上の2つをやってみたいと思います。
Djangoにあまり慣れていない方は、過去にチュートリアルっぽいものも書いているのでぜひ読んでみてください(アーカイブで2018年10月のところあたりにいくつかあります)。
準備作業
プロジェクトを作る
まずはお決まりのコマンドです。プロジェクトの名前はあまり深く考えずに家計簿を意味する「housekeeping book」にしました。
mkdir housekeeping_book cd housekeeping_book django-admin startproject config . #おおもとのフォルダと、プロジェクトのsettingとかが入ってるフォルダの名前を同じにしたくなかった python manage.py startapp money
初期設定
config/settings.py
#... INSTALLED_APPS = [ 'money.apps.MoneyConfig', # <-これを追加(多分33行目あたり) 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] #... LANGUAGE_CODE = 'ja' #多分107行目あたりにある TIME_ZONE = 'Asia/Tokyo' #...
アプリの中身を作る
データベース(モデル)を作る
データベースにどんな情報が必要か考える。ひとまずは次のものが必要だろう。
- 出費があった日付
- 支出額
- 使い道
あとでこれはほぼ確実に増えるけど、まずはこれで作ってみる。
money/models.py
from django.db import models # Create your models here. class Money(models.Model): use_date = models.DateTimeField('日付') detail = models.CharField(max_length=200) cost = models.IntegerField(default=0) def __str__(self): return self.detail + ' ¥' + str(self.cost)
管理者画面でMoneyを見たときには使い道と費用が見えるようにしておいた(def __str__(self)
)。
ではマイグレーション。
python manage.py makemigrations python manage.py migrate
URLの設定
moneyアプリケーション用のURLの設定をする。これもお決まりのコード。
money/urls.py
from django.urls import path from . import views app_name = 'money' urlpatterns = [ path('', views.index, name='index'), #views.indexはまだ作ってないからあとで作る ]
config/urls.py
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('money/', include('money.urls')), ]
ページを作る(ただの動作確認)
とりあえず簡単なページを作ってここまで問題ないか確認しておく。
money/views.py
from django.shortcuts import render, redirect from django.utils import timezone def index(request): today = str(timezone.now()).split('-') context = { 'year' : today[0], 'month' : today[1], } return render(request, 'money/index.html', context)
timezone.now()
で得られる日付データは無駄にデータが多いので、str
に変換してからのsplit
で年と月だけ取り出せるようにしておいた。
templatesフォルダーを作って、
money/templates/money/index.html
<!DOCTYPE html> <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> </body> </html>
これでpython manage.py runserver
で
http://127.0.0.1:8000/money
にアクセスするとページの上部に2018年11月(執筆段階で)とでかでかと表示される。
月の支出を全部表示させる
ここから機能を実装していく。まずは、データベースに登録されているデータを全部表示させる。
money/views.py
from django.shortcuts import render, redirect from django.utils import timezone from .models import Money def index(request): today = str(timezone.now()).split('-') money = Money.objects.all() for m in money: date = str(m.use_date).split(' ')[0] m.use_date = '/'.join(date.split('-')[1:3]) context = {'year' : today[0], 'month' : today[1], 'money' : money, } 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> <table> <tr> <th>日付</th> <th>用途</th> <th>金額</th> </tr> {% for m in money %} <tr> <td>{{ m.use_date }}</td> <td>{{ m.detail }}</td> <td>{{ m.cost }}円</td> </tr> {% endfor %} </body> </html>
表示させてみたらあまりにしょぼかったのでちょっとCSS使いました。
money/static/money/style.css
table{ border-collapse:collapse; margin:0 0; } th{ color:#005ab3; } td{ border-bottom:1px dashed #999; } th,tr:last-child td{ border-bottom:2px solid #005ab3; } td,th{ padding:10px;
データベースにはまだ何も登録してないので何も表示されません。手っ取り早く表示を確認したい人はpython manage.py createsuperuser
で管理者アカウント作ってconfig/admin.pyにモデルを登録して、管理者サイトからモデルをいくつか登録してみるといいと思います。
支出を登録する
次はフォームを作ります。必要な入力欄は3つ、日付と金額、そして用途です。
money/forms.py
from django import forms from .models import Money class SpendingForm(forms.Form): use_date = forms.DateTimeField(label='日付') cost = forms.IntegerField(label='金額') detail = forms.CharField( max_length=200, label='用途' )
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. def index(request): today = str(timezone.now()).split('-') money = Money.objects.all() for m in money: date = str(m.use_date).split(' ')[0] m.use_date = '/'.join(date.split('-')[1:3]) form = SpendingForm() #フォームを読み込む context = {'year' : today[0], 'month' : today[1], 'money' : money, 'form' : form } if request.method == 'POST': # フォームでデータが送られてきたら data = request.POST use_date = data['use_date'] cost = data['cost'] detail = data['detail'] 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) #時間を9時間遅らせる Money.objects.create( # データベースにデータを入れる use_date = use_date, detail = detail, cost = int(cost), ) return redirect(to='/money/') #再び/money/を読み込む return render(request, 'money/index.html', context)
タイムゾーンを'Asia/Tokyo'に設定してもなぜか世界標準時になってしまうのでdatetime.timedelta
で9時間遅らせています。ここら辺に詳しい人がいたらなぜこのようになるのか教えてくださるとうれしいです。
ここでの変更をHTMLにも反映させます。
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> </tr> {% for m in money %} <tr> <td>{{ m.use_date }}</td> <td>{{ m.detail }}</td> <td>{{ m.cost }}円</td> </tr> {% endfor %} </table> </body> </html>
ここまでの成果
ここまでのコードを走らせると次のようになります。
形はできましたが、日付順に並べたり、11月だけのものを表示させたりという機能を実装する必要もあります。それは次回行いたいと思います。