djangoのチュートリアルをさくっとやってみた(フロントサイト構築)

前回の記事でdjangoチュートリアルの2までの内容(管理サイトの構築)まで行ったので、チュートリアルの3以降の内容(フロントサイトの構築)を行います。

3.2 フロントサイトの作成
投票一覧(index)・詳細(detail)・投票(vote)・投票結果(result)のページを作成します。

ルーティングの追加
各ページへのurlの設定を行います。

$ vi urls.py
  6 urlpatterns = patterns('',
  7     url(r'^polls/$', 'polls.views.index'),
  8     url(r'^polls/(?P<poll_id>\d+)/$', 'polls.views.detail'),
  9     url(r'^polls/(?P<poll_id>\d+)/results/$', 'polls.views.results'),
 10     url(r'^polls/(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
 11     url(r'^admin/', include(admin.site.urls)),
 12 )

ビューの追加
一旦、各ページのダミーのビューを追加します。
これで「/polls」にアクセスすると表示はされるようになります。

vi polls/views.py
  1 from django.http import HttpResponse
  2
  3 def index(request):
  4     return HttpResponse("Hello, world. You're at the poll index.")
  5
  6 def detail(request, poll_id):
  7     return HttpResponse("You're looking at poll %s." % poll_id)
  8
  9 def results(request, poll_id):
 10     return HttpResponse("You're looking at the results of poll %s." % poll_id)
 11
 12 def vote(request, poll_id):
 13     return HttpResponse("You're voting on poll %s." % poll_id)

投票一覧画面(index)の作成1
テンプレートを利用しない一覧の実装例

$ vi polls/views.py
  1 from polls.models import Poll
  2 from django.http import HttpResponse
  3
  4 def index(request):
  5     latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
  6     output = ', '.join([p.question for p in latest_poll_list])
  7     return HttpResponse(output)

投票一覧画面(index)の作成2
テンプレートを利用した一覧の実装例

$ vi polls/views.py
  1 from django.template import Context, loader
  2 from polls.models import Poll
  3 from django.http import HttpResponse
  4
  5 def index(request):
  6     latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
  7     t = loader.get_template('polls/index.html')
  8     c = Context({
  9         'latest_poll_list': latest_poll_list,
 10     })
 11     return HttpResponse(t.render(c))

$ mkdir templates/polls
$ vi templates/polls/index.html
  1 {% if latest_poll_list %}
  2     <ul>
  3     {% for poll in latest_poll_list %}
  4         <li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li>
  5     {% endfor %}
  6     </ul>
  7 {% else %}
  8     <p>No polls are available.</p>
  9 {% endif %}

投票一覧画面(index)の作成3
ショートカット(render_to_response)を利用した実装例

$ vi polls/views.py
  1 from django.shortcuts import render_to_response
  2 from polls.models import Poll
  3 from django.http import HttpResponse
  4
  5 def index(request):
  6     latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
  7     return render_to_response('polls/index.html',
  8                               {'latest_poll_list': latest_poll_list})

投票詳細画面(detail)の作成1
リクエストパラメータのpoll_idが存在しない番号の場合、HTTP STATUS 404を応答するように実装

$ vi polls/views.py
  4 from django.http import Http404
~(略)~
 10 def detail(request, poll_id):
 11     try:
 12         p = Poll.objects.get(pk=poll_id)
 13     except Poll.DoesNotExist:
 14         raise Http404
 15     return render_to_response('polls/detail.html', {'poll': p})

$ vi templates/polls/detail.html
  1 <h1>{{ poll.question }}</h1>
  2 <ul>
  3 {% for choice in poll.choice_set.all %}
  4     <li>{{ choice.choice }}</li>
  5 {% endfor %}
  6 </ul>

投票詳細画面(detail)の作成2
HTTP STATUS 404の応答をショートカット(get_object_or_404)で実装

$ vi polls/views.py
  1 from django.shortcuts import render_to_response, get_object_or_404
~(略)~
 10 def detail(request, poll_id):
 11     p = get_object_or_404(Poll, pk=poll_id)
 12     return render_to_response('polls/detail.html', {'poll': p})

URLconfsの記述の簡略化と別ファイル化
urls.pyの記述をpolls/urls.pyに分離して記述も簡略化
(patternsの第一引数にパッケージ名を指定し簡略化)

// 「/polls」のルーティングは「polls/urls.py」に移動
$ vi urls.py
  1 from django.conf.urls.defaults import patterns, include, url
  2
  3 from django.contrib import admin
  4 admin.autodiscover()
  5
  6 urlpatterns = patterns('',
  7     url(r'^polls/', include('polls.urls')),
  8     url(r'^admin/', include(admin.site.urls)),
  9 )

$ vi polls/urls.py 
  1 from django.conf.urls.defaults import patterns, include, url
  2
  3 urlpatterns = patterns('polls.views',
  4     url(r'^$', 'index'),
  5     url(r'^(?P<poll_id>\d+)/$', 'detail'),
  6     url(r'^(?P<poll_id>\d+)/results/$', 'results'),
  7     url(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
  8 )

投票フォーム(vote)と結果(result)の作成
一覧→投票→投票結果表示までの一連の流れを実装します。

// 投票詳細画面(detail)にフォームを組込
$ vi templates/polls/detail.html
  1 <h1>{{ poll.question }}</h1>
  2
  3 {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
  4
  5 <form action="/polls/{{ poll.id }}/vote/" method="post">
  6 {% csrf_token %}
  7 {% for choice in poll.choice_set.all %}
  8     <input type="radio" name="choice" id="choice{{ forloop.counter }}"
  9      value="{{ choice.id }}" />
 10     <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
 11 {% endfor %}
 12 <input type="submit" value="投票する" />
 13 </form>

// 結果画面(result)のテンプレートを作成
  1 <h1>{{ poll.question }}</h1>
  2
  3 <ul>
  4 {% for choice in poll.choice_set.all %}
  5     <li>{{ choice.choice }} -- {{ choice.votes }} 票</li>
  6 {% endfor %}
  7 </ul>
  8
  9 <a href="/polls/{{ poll.id }}/">Vote again?</a>

// 各ページのビューの実装
// detailのcontext_instanceはテンプレートの「csrf_token」のために必要
$ vi polls/views.py
  1 from django.shortcuts import render_to_response, get_object_or_404
  2 from django.template import RequestContext
  3 from polls.models import Choice, Poll
  4 from django.http import HttpResponseRedirect, HttpResponse
  5 from django.core.urlresolvers import reverse
  6
  7 def index(request):
  8     latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
  9     return render_to_response('polls/index.html',
 10                               {'latest_poll_list': latest_poll_list})
 11 def detail(request, poll_id):
 12     p = get_object_or_404(Poll, pk=poll_id)
 13     return render_to_response('polls/detail.html', {'poll': p},
 14                                context_instance=RequestContext(request))
 15
 16 def results(request, poll_id):
 17     p = get_object_or_404(Poll, pk=poll_id)
 18     return render_to_response('polls/results.html', {'poll': p})
 19
 20 def vote(request, poll_id):
 21     p = get_object_or_404(Poll, pk=poll_id)
 22     try:
 23         selected_choice = p.choice_set.get(pk=request.POST['choice'])
 24     except (KeyError, Choice.DoesNotExist):
 25         return render_to_response('polls/detail.html', {
 26             'poll': p,
 27             'error_message': "You didn't select a choice.",
 28         }, context_instance=RequestContext(request))
 29     else:
 30         selected_choice.votes += 1
 31         selected_choice.save()
 32         return HttpResponseRedirect(reverse('polls.views.results', args=(p.id,)))

汎用ビューの利用

$ vi polls/urls.py
  1 from django.conf.urls.defaults import *
  2 from django.views.generic import DetailView, ListView
  3 from polls.models import Poll
  4
  5 urlpatterns = patterns('',
  6     url(r'^$',
  7         ListView.as_view(
  8             queryset=Poll.objects.order_by('-pub_date')[:5],
  9             context_object_name='latest_poll_list',
 10             template_name='polls/index.html')),
 11     url(r'^(?P<pk>\d+)/$',
 12         DetailView.as_view(
 13             model=Poll,
 14             template_name='polls/detail.html')),
 15     url(r'^(?P<pk>\d+)/results/$',
 16         DetailView.as_view(
 17             model=Poll,
 18             template_name='polls/results.html'),
 19         name='poll_results'),
 20     url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
 21 )

// 各ビュー(index,detail,result)の削除
$ vi polls/views.py
  1 from django.shortcuts import get_object_or_404, render_to_response
  2 from django.http import HttpResponseRedirect
  3 from django.core.urlresolvers import reverse
  4 from django.template import RequestContext
  5 from polls.models import Choice, Poll
  6
  7 def vote(request, poll_id):
  8     p = get_object_or_404(Poll, pk=poll_id)
  9     try:
 10         selected_choice = p.choice_set.get(pk=request.POST['choice'])
 11     except (KeyError, Choice.DoesNotExist):
 12         return render_to_response('polls/detail.html', {
 13             'poll': p,
 14             'error_message': "You didn't select a choice.",
 15         }, context_instance=RequestContext(request))
 16     else:
 17         selected_choice.votes += 1
 18         selected_choice.save()
 19         return HttpResponseRedirect(reverse('poll_results', args=(p.id,)))

これでチュートリアルは一通り完了です。少しdjangoを解った気分にはなれました。