2006-08-30 01:11:48

Djangoで家計簿を作ろう(2)

テーマ:Python
続いてViewやらTemplateやら弄っているうちに、全体のモデルがはっきりしてきたので、最初から作り直した。

モデルはこんな感じになった。

# -*- coding: utf-8 -*-
from django.db import models

class Kouza(models.Model):
name = models.CharField('名称',maxlength=200)
def __str__(self):
return self.name
class Meta:
verbose_name = '口座'
verbose_name_plural = verbose_name
class Admin:
pass

class Kaesuate(models.Model):
name = models.CharField('名称',maxlength=200)
def __str__(self):
return self.name
class Meta:
verbose_name = '返すあて'
verbose_name_plural = verbose_name
class Admin:
pass

class Koumoku(models.Model):
from datetime import datetime
kaesuate = models.ForeignKey(Kaesuate, verbose_name='返すあて')
kouza = models.ForeignKey(Kouza, verbose_name='口座')
siyoubi = models.DateTimeField('使用日', default=datetime.today())
meimoku = models.CharField('名目',maxlength=200)
kingaku = models.IntegerField('金額')
bikou = models.CharField('備考',maxlength=500,blank=True)
yotei = models.BooleanField('予定')

def __str__(self):
return "%s %s %d %s %s" % (self.siyoubi.strftime('%m/%d'),
self.kaesuate,
self.kingaku, self.meimoku,
self.bikou)

class Meta:
get_latest_by = 'siyoubi'
ordering = ['siyoubi']
verbose_name = '項目'
verbose_name_plural = verbose_name
class Admin:
pass

class CardKind(models.Model):
name = models.CharField('名称',maxlength=200)
hikiotosi = models.IntegerField('基本引落し日',blank=True)
kouza = models.ForeignKey(Kouza, verbose_name='口座')
def __str__(self):
return self.name
class Meta:
verbose_name = 'カードの種類'
verbose_name_plural = verbose_name
class Admin:
pass

class Card(models.Model):
koumoku = models.ForeignKey(Koumoku, verbose_name='項目')
kind = models.ForeignKey(CardKind, verbose_name='種類')
tuki = models.IntegerField('支払い月',blank=True)
sumi = models.BooleanField('支払い済み')

def __str__(self):
if self.sumi:
sumi='済'
else:
sumi='未'
return "%s %s %s" % (sumi,self.kind,self.koumoku)
class Meta:
verbose_name = 'カード払い'
verbose_name_plural = verbose_name
class Admin:
pass

class SyunyuKind(models.Model):
name = models.CharField('名称',maxlength=200)
def __str__(self):
return self.name
class Meta:
verbose_name = '収入の種類'
verbose_name_plural = verbose_name
class Admin:
pass

class Syunyu(models.Model):
koumoku = models.ForeignKey(Koumoku, verbose_name='項目')
kind = models.ForeignKey(SyunyuKind, verbose_name='種類')
def __str__(self):
return "%s %s" % (self.kind,self.koumoku)
class Meta:
verbose_name = '収入'
verbose_name_plural = verbose_name
class Admin:
pass

前借先(Kaesuate)と、銀行の口座(Kouza)を明確に分け、カード払いを示せるように、カード種別(CardKind)とカード払い(Card)のモデルを追加した。また、収入は、金額の±では無く、カード払いと同じように、収入種別(SyunyuKind)と収入(Syunyu)のモデルを追加した。
カード払いも収入も、項目へのリレーションを持つが、逆方向へのリレーションは持たない。
DjangoがDBのJOINを隠蔽してうまいことやってくれることを信じているが、やってくれるのだろうか?

ちなみに、あっちこっちローマ字表記なのは、英語名を考えるのに手が止まるのを嫌ってのことである。
変数名称とかに悩むと、すっごい時間がかかり、モチベーションが下がりまくるので、いんちきローマ字で表すことにした。
後でリファクタリングすりゃいいやとか考えていたが、データベースにデータを入れたまま、モデルを安全に更新する方法が分からない。

基本的に前回まで入れていたテストデータはまっさらに消えた。
AD
いいね!した人  |  コメント(0)  |  リブログ(0)
2006-08-27 22:59:31

Djangoで家計簿を作ろう

テーマ:Python
家計簿っていうか、将来の収支を期待して先に使ってしまう「前借」という経済的綱渡りを管理したいので、貸借表に近いのだが、今までは表計算ソフトを使っていた。
これが、自分で作っているのに何故か使いづらい。カードの支払いが絡むと良く分からなくなる。自分で作っておいてユーザフレンドリーでない。
で、せっかくなのでDjangoを使ったWebアプリを作ろうって感じで。

まあ、Webアプリにしたからといって使いやすくなるわけじゃないが、Webにしとけば嫁も気軽に参照できるし。
実際には、Webアプリ系家計簿サービスを探したら、ちょうどいいのがなかったので。
ちなみにざっとぐぐると、Django使って2日で家計簿作ったとか、簡単に見つかるが、そんなに早くは出来なさそうです。

Djangoはチュートリアルを参照しながら、管理画面作成まで出来たところ。
作ったモデルは現段階で、こんな感じ。
models.py

# -*- coding: utf-8 -*-
from django.db import models

class Kouza(models.Model):
name = models.CharField(maxlength=200)

def __str__(self):
return self.name

class Admin:
pass

class Koumoku(models.Model):
kouza = models.ForeignKey(Kouza)
siyoubi = models.DateField('使用日',blank=True)
meimoku = models.CharField('名目',maxlength=200)
kingaku = models.IntegerField('金額')
bikou = models.CharField('備考',maxlength=500,blank=True)
kakutei = models.BooleanField('確定')

def __str__(self):
return "%s %s %d - %s | %s" % (self.yotei.strftime('%m/%d'), self.meimoku, self.kingaku, self.bikou, self.kouza)

class Admin:
pass

チュートリアルに従ってシェルで遊んでみたら日本語をMySQLが受け付けなくて困った。
多分文字コードの問題だと思うから、とりあえず気にしないことにした。

Kouzaは口座のことで、前借したものをどの収入で返せるかという分類。たとえばボーナスという口座では、年に2回収入があり、
ボーナスの予定額からあといくら使えるかというのを示す。
Koumokuは項目で、個々の前借を示す。確定フィールドを付ける事で、まだ使用していないが使用する予定の物も示せるようにした。
収入の方をどうするか悩み中。
AD
いいね!した人  |  コメント(0)  |  リブログ(0)
2006-08-25 22:49:36

ライフゲーム(多次元拡張)

テーマ:Python
前回のライフゲームは2次元限定だが、ライフゲームのロジック自体は1次元や、3次元以上に簡単に拡張できる。
面倒なので表示系の処理は考えないとすると、前回のプログラムから書き換える関数は、positionsのみで良い。
これは、任意の点の周辺(それ自身も含む)の点の集合を返す関数であるから、与えられた点の次元に応じて返す集合を変えればよい。

#1次元ライフゲームバージョン
def positions(point):
for dx in (-1,0,1):
yield (point[0]+dx,)

もちろん、誕生/死滅の閾値が2次元のままだと動かない。
これを1次元、2次元、3次元の場合と作っていけばいいわけだが、まあ一々書けないので再帰的に処理すると次のようになる。

def positions(point,pos=None,axis=0):
"""指定された任意の次元の点から、その周辺(指定された点を含む)を
示す点のタプルを生成するジェネレータ

1次元の点からその周辺領域を生成
>>> list( positions((1,)))
[(0,), (1,), (2,)]

2次元の点からその周辺領域を生成
>>> list( positions((1,1))) #doctest: +NORMALIZE_WHITESPACE
[(0, 0), (0, 1), (0, 2),
(1, 0), (1, 1), (1, 2),
(2, 0), (2, 1), (2, 2)]

3次元の点からその周辺領域を生成
>>> list( positions((1,1,1))) #doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
[(0, 0, 0), (0, 0, 1), (0, 0, 2),
(0, 1, 0), ...,
(1, 1, 1),
..., (2, 1, 2),
(2, 2, 0), (2, 2, 1), (2, 2, 2)]
"""
if not pos:
pos = list(point)
if axis == len(pos):
yield tuple(pos)
else:
for delta in (-1,0,+1):
pos[axis] = point[axis] + delta
for p in positions(point, pos, axis+1):
yield p

関数の次の行から始まっているのはPythonで特徴的なテスト記法であるdoctestです。
最初に受け取った点の座標をリストに変換後、各項に+1,0,-1の演算を施して周辺集合を返す。ジェネレータにしてあります。
ちなみに、3次元までしか確認していないし、2次元以外ではライフゲームの動作も確認していませんが。
AD
いいね!した人  |  コメント(0)  |  リブログ(0)
2006-08-24 00:06:53

ライフゲーム

テーマ:Python
時々ふとライフゲームを作りたくなる。
Cでも書いた。Javaでも書いた。ならばPythonでも書かねばならぬだろう。


def born_cell(point,cells):
for pos in positions(point):
if pos not in cells:
cells[pos] = {'own':0,'side':0}
if pos == point:
cells[pos]['own'] += 1
else:
cells[pos]['side'] += 1

def positions(point):
for dx in (-1,0,1):
for dy in (-1,0,1):
yield (point[0]+dx, point[1]+dy)

def next(cells):
new_cells={}
for pos,cell in cells.iteritems():
if (cell['own'] > 0 and cell['side'] == 2) or cell['side'] == 3:
born_cell(pos,new_cells)
return new_cells

def lifegame(cells):
yield cells
while 1:
cells = next(cells)
yield cells

def create(init_str):
new_cells={}
x,y = 0,0
for line in init_str.splitlines():
for pos in line:
if pos=='o':born_cell((x,y),new_cells)
x += 1
y += 1
x = 0
return new_cells

def view(cells, lt=(0,0),rb=(9,9)):
"cellsを2次元文字列で表示する"
for y in xrange(lt[1], rb[1]+1):
for x in xrange(lt[0], rb[0]+1):
cell = cells.get((x,y), {'own':0})
if cell['own'] == 1:
print 'o',
else:
print '.',
print

実際にライフゲームの処理を担うのは最初の4つの関数 born_cell, positions, nextそれにlifegameである。
createは文字列形式の初期配置からcellのデータ構造を作成する関数。viewはその逆をして表示する関数である。

以下のように使う。

>>> cells=create("""\
.o.
..o
ooo""")
>>> for cell in lifegame(cells):
view(cell)
print
. o . . . . . . . .
. . o . . . . . . .
o o o . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .

. . . . . . . . . .
o . o . . . . . . .
. o o . . . . . . .
. o . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .

表示はアレだがグライダーが飛んでいく・・・。
ちなみに、セルのデータ構造は配列ではなく、辞書になっているので表示されていなくても無限にグライダーは飛んでいってる。
表示の簡易化のために初期配置を(0,0)にした(0,0)-(9,9)を表示しているだけなので、そこんところを何とかすればメモリの許す限り広大なライフゲームが出来るはず。
いいね!した人  |  コメント(0)  |  リブログ(0)
2006-08-20 22:38:43

いきなりタイトルを変えるってどうなんだろう?

テーマ:雑記
新しいブログを開設するのも面倒なので、冬眠状態のここを別のテーマで復活させたわけだが、読者もいるわけじゃないしいいかね。
リファクタリングってことで。

いいね!した人  |  コメント(0)  |  リブログ(0)

AD

ブログをはじめる

たくさんの芸能人・有名人が
書いているAmebaブログを
無料で簡単にはじめることができます。

公式トップブロガーへ応募

多くの方にご紹介したいブログを
執筆する方を「公式トップブロガー」
として認定しております。

芸能人・有名人ブログを開設

Amebaブログでは、芸能人・有名人ブログを
ご希望される著名人の方/事務所様を
随時募集しております。