
photo credit: lepiaf.geo (back mid February)
Djangoをもう少し理解しようと、Djangoドキュメントサイトのチュートリアルをやっていた。
チュートリアル4の前半までは、あまり悩むことも無く、うまくいっていたのだが、最後の汎用ビューで詰まった。
というか、汎用ビューのチュートリアルはちょっとはしょりすぎで良く分からない。
urls.py を書き換えるところまでは問題ない。
で、その後テンプレートも書き換えないといけないんだが、それがかなりはしょられているので分かりにくい。
分かったことをまとめておく。
urls.py は以下の通り
from django.conf.urls.defaults import *
from mysite.polls.models import Poll
info_dict = {
'queryset': Poll.objects.all()
}
urlpatterns = patterns('',
(r'^$', 'django.views.generic.list_detail.object_list', info_dict),
(r'^(?P<object_id>\d+)/$', 'django.views.generic.list_detail.object_detail', info_dict),
url(r'^(?P<object_id>\d+)/results/$',
'django.views.generic.list_detail.object_detail',
dict(info_dict, template_name='polls/results.html'), 'poll_results'),
(r'^(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
)
ここで、info_dict で設定された queryset の結果はテンプレートではobject_list として参照できる。
これをチュートリアル中に書いておいて欲しかった。
これが分からないためかなり悩んだんです。
なので、チュートリアルで作っていたテンプレート /mysite/polls/index.html は以下のように書き換えなければいけない。
{% if object_list %}
<ul>
{% for poll in object_list %}
<li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
ついでだから、detailへのリンクを追加しておいた。
こんなもっといい書き方があるかもしれないが、今のところはこの形で。
さらに、汎用ビューのdjango.views.generic.list_detail.object_list は モデル名/モデル名_list.html というテンプレートを呼び出すので、/polls/index.html のファイル名を /polls/poll_list.html に変更しなければならない。
同様に、django.views.generic.list_detail.object_detail は モデル名/モデル名_detail.html というテンプレートを呼び出すので、/polls/detail.html は/polls/poll_list.html にファイル名を変更しておく。
次に、detail のテンプレートは以下のように変更が必要。
<h1>{{ object.question }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="/polls/{{ object.id }}/vote/" method="post">
{% for choice in object.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}"
value="{{ choice.id }}" />
<label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
{% endfor %}
<input type="submit" value="投票する" />
</form>
poll で渡されていたオブジェクトが object という汎用名で渡されているので、poll を objectに変更した。
結果を表示するテンプレート /polls/results.html は以下のようになる。
<h1>{{ object.question }}</h1>
<ul>
{% for choice in object.choice_set.all %}
<li>{{ choice.choice }} -- {{ choice.votes }} 票</li>
{% endfor %}
</ul>
<p><a href="/polls/">投票一覧に戻る</a></p>
これは、urls.py 内でパターンをurl()で設定していて、テンプレートを明示的に設定しているので、テンプレートのファイル名は元のままでOK。
vote部分は汎用ビューを使わないので、views.py 中のvote() が使われる。
vote(request, poll_id)関数内を、汎用ビューにあわせて以下のように変更する必要がある。
def vote(request, poll_id):
p = get_object_or_404(Poll, pk=poll_id)
try:
selected_choice = p.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
# Poll 投票フォームを再表示します。
return render_to_response('polls/poll_detail.html', {
'object': p,
'error_message': "選択肢を選んでいません。",
})
else:
selected_choice.votes += 1
selected_choice.save()
# ユーザーがBackボタンを押して同じフォームを提出するのを防ぐため
# POST データを処理できた場合には必ず
# HttpResponseRedirect を返すようにします。
return HttpResponseRedirect(reverse('poll_results', args=(p.id,)))
変更点は選択肢が選ばれていないときの render_to_response 部分
return render_to_response('polls/poll_detail.html', {
'poll': p,
'error_message': "選択肢を選んでいません。",
})
のpoll を onject に変更。(汎用ビュー用にテンプレートをobject で参照するように変更したため)
さらに、reverseするための名前をulrs.py のurl()関数で名前をつけた poll_results に変更している。
以上。
汎用ビューを使おうとしてかなり悩んだ部分をカンタンにまとめておいた。