DjangoでTODOリストを作ろう(その4)
TODOリスト の4回目。
今回は駆け足で登録・更新・削除を作っていく。 コレが出来れば一通り必要な機能は全部実装。
ではでは早速。まずは urls.py を編集してリクエストからのパスを通してあげる。以下、 todo/urls.py
from django.conf.urls.defaults import *
# create, update, deleteを追加。
urlpatterns = patterns('',
(r'^create/$', 'hige.todo.views.create'),
(r'^update/(?P<object_id>\d+)/$', 'hige.todo.views.update'),
(r'^delete/(?P<object_id>\d+)/$', 'hige.todo.views.delete'),
(r'^list/$', 'hige.todo.views.list'),
(r'^$', 'hige.todo.views.list'),
)
ココで出て来た (?P<object_id>\d+) って部分、 ココではURLから値を抜き取って、 object_id な名前をつけて view の引数に渡すよ。って指示をしてる。なので、 views.py の update や delete の引数には request 以外にも object_id な引数が飛び込んでくる。ココでは \d+ を指定してるので、数字しか一致しない。なので、引数に入ってくるのは必ず数字になる。(型が数字かどうかは別)
ちょっと眺めてみると hige.todo.views な文字が何回も書かれてる事に気づく。少しココで整頓しておく。 patterns 関数の第1引数はキックする view のプリフィックスを指定しておける便利機能付き。今回は hige.todo.views までが同じなので、それをプリフィックスとして指定する。
from django.conf.urls.defaults import *
# create, update, deleteを追加。
urlpatterns = patterns('hige.todo.views',
(r'^create/$', 'create'),
(r'^update/(?P<object_id>\d+)/$', 'update'),
(r'^delete/(?P<object_id>\d+)/$', 'delete'),
(r'^list/$', 'list'),
(r'^$', 'list'),
)
コレでそれぞれリクエストから view までの紐付けが出来たので、 views.py に実装して行く。とりあえずは create から作って新規登録を出来るようにする訳ですが、殆ど変わらないので update も一緒に作ってしまう。以下、 todo/views.py
def create(request):
p = {}
# モデルに紐づくフォームのクラス!の作成
f = forms.form_for_model(Task)
# ココでフォームをインスタンス化
form = f(request.POST.copy() or None)
# データのバリデート処理
if form.is_valid():
# エラーが無ければセーブしてリダイレクト
form.save()
return HttpResponseRedirect('/todo/')
# GETで来た時やエラーがあった場合はフォーム画面へ
p['title'] = 'add new task.'
p['form'] = form
return render_to_response('todo/task_form.html', p)
def update(request, object_id):
p = {}
# object_idで該当するデータをもってくる。
object = get_object_or_404(Task, pk=object_id, is_active=True)
# インスタンスに紐づくフォームクラスの作成。
f = forms.form_for_instance(object)
form = f(request.POST.copy() or None)
if form.is_valid():
form.save()
return HttpResponseRedirect('/todo/')
p['title'] = 'update task #%s' % object.id
p['form'] = form
return render_to_response('todo/task_form.html', p)
ここでちょっと説明とか。バリデートに関しては、デフォルトでモデルを作成したときの制約をほぼそのままフォームに適応してくれる。ので、特に変更が無ければ何も書かなくてもちゃんとデータの妥当性を検証してくれる。 oldforms の頃からそうだったけど、 newforms になってさらにコード量が減った。かなり素敵。
で、ロジックで create と update で違う所と言えば、既存のデータを取ってくるのと、それに合わせて newforms のメソッドが変わるぐらい。それ以外はほぼ一緒。テンプレートに落とすタイトルが違うぐらい。
次に todo/task_form.html を作る。 newforms には簡単にフォームを作る便利機能があるのですが、個人的にあんまり好きじゃないので、今回はひとつずつ書いていく。覚えといて損はないしね。
{% extends "base.html" %}
{% block title %}{{ title }}{% endblock %}
{% block contents %}
<h1>{{ title }}</h1>
<form action="." method="post" accept-charset="utf-8">
<ul>
<li>
<label for="{{ form.priority.auto_id }}">
{{ form.priority.label }}
</label>
{{ form.priority }}
{% if form.priority.errors %}
<label for="{{ form.priority.auto_id }}">
{{ form.priority.errors|join:"," }}
</label>
{% endif %}
</li>
<li>
<label for="{{ form.description.auto_id }}">
{{ form.description.label }}
</label>
{{ form.description }}
{% if form.description.errors %}
<label for="{{ form.description.auto_id }}">
{{ form.description.errors|join:"," }}
</label>
{% endif %}
</li>
<li>
<label> </label>
<input type="submit" value="add task." />
</li>
</ul>
</form>
{% endblock %}
コレでひとまず完了。ただ、このままだとURL直叩きじゃないと来れないので、一覧画面にテキトーにリンクを貼る。今回はあんまり深く考えずに create へのリンクは一番下あたりに、 update へのリンクは task 部分に貼ってみた。ついでに delete へのリンクもこしらえておく。以下、 task_list.html の抜粋。
{% block contents %}
<h1>Todo List</h1>
{% if object_list %}
<ul>
{% for object in object_list %}
<li>
{{ object.priority }}
<a href="/todo/update/{{ object.id }}/">{{ object }}</a>
<a href="/todo/delete/{{ object.id }}/">delete</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>no task.</p>
{% endif %}
<a href="/todo/create/">add new task.</a>
{% endblock %}
ココでひとまず遊んでみる。エラーな画面が出る場合はデバッグを楽しむ。一通り遊んでみたら最後に残った削除画面の作成にはいる。
削除機能に関しては、リンクから GET で飛んでだけでデータが削除されるってのは個人的にものすごく気持ち悪いので、確認画面を間に挟んでそこから POST させて削除する形で作る。以下、 views.py の抜粋。
def delete(request, object_id):
p = {}
object = get_object_or_404(Task, pk=object_id, is_active=True)
# POSTなリクエストが来たら削除。
if request.method == 'POST':
object.delete()
return HttpResponseRedirect('/todo/')
p['object'] = object
return render_to_response('todo/task_confirm_delete.html', p)
確認画面の todo/task_confirm_delete.html はこんな感じ。キャンセルボタンはちょっと手抜きしました。
{% extends "base.html" %}
{% block title %}delete confirm{% endblock %}
{% block contents %}
<p>delete task #{{ object.id }}?</p>
<dl>
<dt>Priority:</dt>
<dd>{{ object.priority }}</dd>
<dt>Description:</dt>
<dd>{{ object }}</dd>
</dl>
<form action="." method="post" accept-charset="utf-8">
<input type="hidden" name="object_id" value="{{ object.id }}" />
<input type="submit" value="delete this task." />
<input type="button" value="cancel" onClick="javascript:history.back();" />
</form>
{% endblock %}
フォームから object_id な値を投げてるのは特に意味はありません。ただ、何も投げないのが気持ち悪かったのと、どっかで使えるかな?って思ってただけ。 POST で来た時にURL中の object_id と比較して、一致したら削除するってのも良いかも。(あんまり意味ないか?)
以下、今回のまとめ?
- urls.py の patterns 関数の第1引数を上手く使おう。
- URLからは正規表現でパラメータを引っこ抜く。
- newforms はそのうち名前が変わるのでリネームは絶対。
これで一通りの動作を全部実装!かなり過不足が多い気がするけど。あと、デザインを全く弄ってないので見てくれはかなりダサイ。そこらへんは好みとかあると思うのであえて触れない方向で。
最後に Django の開発環境でのCSSなどの静的ファイルの提供方法を。プロジェクト直下の urls.py に以下を追加する。
from django.conf import settings
if settings.DEBUG:
urlpatterns += patterns('django.views.static',
(r'^site_media/(?P<path>.*)$', 'serve', {'document_root':settings.MEDIA_ROOT,}),
)
HTMLにはこんな感じで書く。
<link rel="stylesheet" type="text/css" href="/site_media/path/to/css" />
コレでCSSとかJavaScriptとかも読み込めるはず。でも、コレはあくまでも開発環境用なので本番では使わないでね。とのこと。 コチラ に 本家サイト の情報が和訳されております。
結局 jQuery 使わなかった。すいません。でも、また時間見つけてなにか書きたいと思う。何より自分の勉強になりました。乱雑な内容ではありますが、これから Django を触ってみようと思ってる方(某 endless 氏とか)の手助けになればと思います。ってならないかぁ。
- Posted at:
- 2007/06/24 23:58:42
- 3 Comments
- 0 TrackBacks
- Trackback:
- http://humming.via-kitchen.com/2007/06/24/todo-list-by-using-django-part4/trackback/
TrackBacks
まだ登録されていません。
Comments
yabe
一通り実行できました。
参考になりました。
ありがとうございます。
kishi-r
やっとこさ出来やしたw
ただ慣れないせいで、
スペルミスなどが多いのでデバックに
時間が掛かってしまったよぉ。。。
とりあえず全部完了っす!
nobu
>yabeさん
>kishi-rさん
お疲れさまですー。
内容が薄くてすいませんです。
徐々にではあるものの、まわりにPythonistaが増えているのが嬉しいですなw