Django1.5 tutorial Part4 個人的メモ
簡単なフォームを書く
polls/detail.html
にフォームを以下のように追加する.
<h1>{{ poll.question }}</h1> {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %} <form action="{% url 'polls:vote' poll.id %}" method="post"> {% csrf_token %} {% for choice in poll.choice_set.all %} <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" /> <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br /> {% endfor %} <input type="submit" value="Vote" /> </form>
コードの説明:
- 各投票(Poll)の選択肢(choice)のラジオボタンを表示
- フォームのアクションは
{% url 'polls:vote' poll.id %}
で指定 forloop.counter
はforタグがそのループを何回回ったかの回数- POSTの場合,CSRF(Cross Site RequestForgeries)に注意する必要があるので,
{% csrf_token %}
を使って対応する必要がある.
次にpolls/views.py
中のvote()
関数を実装する.
from django.shortcuts import get_object_or_404, render from django.http import HttpResponseRedirect, HttpResponse from django.core.urlresolvers import reverse from polls.models import Choice, Poll # ... 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): # Redisplay the poll voting form. return render(request, 'polls/detail.html', { 'poll': p, 'error_message': "You didn't select a choice.", }) else: selected_choice.votes += 1 selected_choice.save() # Always return an HttpResponseRedirect after successfully dealing # with POST data. This prevents data from being posted twice if a # user hits the Back button. return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))
ここでやっていることの説明.
request.POST
は辞書的なオブジェクトで,view側ではキーワード指定でsubmitされたデータを参照出来る.この例ではrequest.POST['choice']
でchoiceのvalue,すなわちchoiceのidを文字列で受け取っている.request.POST['choice']
はKeyErrorを発生させるかもしれないのでその例外処理を記述している- choiceのカウントをインクリメントした後はHttpResponseの代わりにHttpResponse.HttpResonseRedirectを返すようにしている.これはPOSTした後には結果ページにリダイレクトするべきというWeb開発のプラクティスに基づいている.
- reverse()関数を使っているのはview.pyでリダイレクト先のURLをハードコードするのを避けるためにである.上記例の
reverse('polls:results', args=(p.id,))
は以下の結果文字列を返す.
'/polls/3/results/' # この'3'はp.idの値である.
この辺りの詳しい説明はここを参照すること.
次にやることはresults()の修正
def results(request, poll_id): poll = get_object_or_404(Poll, pk=poll_id) return render(request, 'polls/results.html', {'poll': poll})
子の実装はdetail()とほとんど同じ.この冗長性を取り除く方法は後述.
そうしたら,polls/results.html
テンプレートを作成する.
<h1>{{ poll.question }}</h1> <ul> {% for choice in poll.choice_set.all %} <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li> {% endfor %} </ul> <a href="{% url 'polls:detail' poll.id %}">Vote again?</a>
総称viewを使う:少ないコードは良いことだ.
detail(), results(), そしてindex()はそれぞれ非常に似ている.これらは以下のような共通処理を行っている.
- URLを通じて得たパラメータに応じたデータをデータベースから取得
- テンプレートをロードしてレンダリングしたテンプレートを返す
これは非常に一般的なのでDjangoは"generic views"というショートカットを提供している.
Amend URLconf
polls/urls.py
を以下のように修正する.
from django.conf.urls import patterns, url from polls import views urlpatterns = patterns('', url(r'^$', views.IndexView.as_view(), name='index'), url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'), url(r'^(?P<pk>\d+)/results/$', views.ResultsView.as_view(), name='results'), url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'), )
Amend views
古いindex, detail, results関数を削除して総称viewを使う以下のコードに変更する.
from django.shortcuts import get_object_or_404, render from django.http import HttpResponseRedirect from django.core.urlresolvers import reverse from django.views import generic from polls.models import Choice, Poll class IndexView(generic.ListView): template_name = 'polls/index.html' context_object_name = 'latest_poll_list' def get_queryset(self): """Return the last five published polls.""" return Poll.objects.order_by('-pub_date')[:5] class DetailView(generic.DetailView): model = Poll template_name = 'polls/detail.html' class ResultsView(generic.DetailView): model = Poll template_name = 'polls/results.html' def vote(request, poll_id): ....
この例ではListView
とDetailView
という二つの総称viewを使っている.これらは
- 各総称viewは何のモデルに基づいているかを知る必要がある.これは
model
属性で指定する - DetailView総称viewはURLから"pk"という名前で主キーが捕捉されてくることを期待しているので,上記例では
poll_id
をpk
に変更している.
デフォルトで,DetailViewはテンプレートを”template_name
属性にその名前を設定してやればよい.
その他の総称viewの詳細はここを参照すること.