Commits

Ian Lewis committed 2e8fcc9

資料をRSTに

  • Participants
  • Parent commits 4df3ddb
  • Branches google_login

Comments (0)

Files changed (1)

File django-hack-a-thon-get-handson.txt

+==============================================================
+"Starting app-engine-patch" at 4/25 Django 勉強会 GAE hands on
+==============================================================
 
-"Starting app-engine-patch" at 4/25 Django 勉強会 GAE hands on
-
-1. app-engine-patch を入手
+app-engine-patch を入手
+-----------------------
 
 何はともあれ入手しましょう。
 http://code.google.com/p/app-engine-patch/
 から app-engine-patch-sample-1.0.zip をダウンロードします。
 
-解凍した app-engine-patch-sample ディレクトリをコピーして使いましょう。
+解凍した app-engine-patch-sample ディレクトリをコピーして使いましょう。::
 
-$ cp -r app-engine-patch-sample handson
+    $ cp -r app-engine-patch-sample handson
 
+アプリを動かす
+--------------
 
-2. このままで色々動かす事ができます。興味があったら試してみましょう。
+このままで色々動かす事ができます。興味があったら試してみましょう。::
 
-$ cd handson
-$ python manage.py runserver
+    $ cd handson
+    $ python manage.py runserver
 
+設定ファイルを少し直しましょう。
+---------------------------------------------
 
-3. 設定ファイルを少し直しましょう。
 * app.yaml の application を自分の app slot 名に変更します。
-また、下記のように admin 用の handler も足しておきましょう。
-------------------------------------------------------------------------------
-handlers:
-- url: /media
-  static_dir: _generated_media
-- url: /admin/.*
-  script: common/appenginepatch/main.py
-  login: admin
-- url: /.*
-  script: common/appenginepatch/main.py
-------------------------------------------------------------------------------
+  また、下記のように admin 用の handler も足しておきましょう。::
+
+    handlers:
+    - url: /media
+      static_dir: _generated_media
+    - url: /admin/.*
+      script: common/appenginepatch/main.py
+      login: admin
+    - url: /.*
+      script: common/appenginepatch/main.py
 
 * このハンズオンでは Google Account 認証を使うように変更します。
   settings.py の編集をしましょう。
-  まずは SECRET_KEY に秘密の文字列を入れます。
-  次に middleware のところで下記のモジュールをコメントアウトして
+* まずは SECRET_KEY に秘密の文字列を入れます。
+* 次に middleware のところで下記のモジュールをコメントアウトして
   'django.contrib.auth.middleware.AuthenticationMiddleware'
   下記のモジュールを有効にします。
-  'ragendja.auth.middleware.GoogleAuthenticationMiddleware'
-  最後に認証に使用するモジュールを指定します。
-  (コメントアウトされているのをアンコメントするだけです)
-  AUTH_USER_MODULE = 'ragendja.auth.google_models'
-  AUTH_ADMIN_MODULE = 'ragendja.auth.google_admin'
+*  'ragendja.auth.middleware.GoogleAuthenticationMiddleware'
+   最後に認証に使用するモジュールを指定します。
+   (コメントアウトされているのをアンコメントするだけです)
+   AUTH_USER_MODULE = 'ragendja.auth.google_models'
+   AUTH_ADMIN_MODULE = 'ragendja.auth.google_admin'
 
+さらに不必要なアプリケーションを無効にします。
 
-4. さらに不必要なアプリケーションを無効にします。
 * settings.py の INSTALLED_APPS にて myapp, registration, mediautils を
   外します。
-* それらのディレクトリは消してしましましょう
-$ rm -rf myapp registration
+* それらのディレクトリは消してしましましょう::
+    
+    $ rm -rf myapp registration
 
-* mac で X-MAC-JAPANESE 関係の Exception が出る場合
-settings.py の冒頭で下記のようにします
-------------------------------------------------------------------------------
-def register_x_max_ja():
-    "workaround of 'Caught an exception while rendering: unknown encoding: X-MAC-JAPANESE'"
-    import codecs
-    from encodings.utf_8 import getregentry
-    codecs.register(lambda name: getregentry() if name == 'x-mac-japanese' else None)
+mac で X-MAC-JAPANESE 関係の Exception が出る場合
+-------------------------------------------------
 
-register_x_max_ja()
-------------------------------------------------------------------------------
+settings.py の冒頭で下記のようにします::
 
+    def register_x_max_ja():
+        "workaround of 'Caught an exception while rendering: unknown encoding: X-MAC-JAPANESE'"
+        import codecs
+        from encodings.utf_8 import getregentry
+        codecs.register(lambda name: getregentry() if name == 'x-mac-japanese' else None)
 
-5. poll アプリケーションを作成していきましょう。
+    register_x_max_ja()
 
-* manage.py を使って poll アプリケーションを作成します。
-$ python manage.py startapp polls
+poll アプリケーションを作成していきましょう。
+--------------------------------------------
 
-* urls.py を下記のように直します。
-------------------------------------------------------------------------------
- -*- coding: utf-8 -*-
-from django.conf.urls.defaults import *
-from ragendja.urlsauto import urlpatterns
-from ragendja.auth.urls import urlpatterns as auth_patterns
-from django.contrib import admin
+* manage.py を使って poll アプリケーションを作成します。::
 
-admin.autodiscover()
+    $ python manage.py startapp polls
 
-handler500 = 'ragendja.views.server_error'
+* urls.py を下記のように直します。::
 
-urlpatterns = auth_patterns + patterns('',
-    ('^admin/(.*)', admin.site.root),
-    (r'^polls/', include('polls.urls')),
-) + urlpatterns
-------------------------------------------------------------------------------
+     -*- coding: utf-8 -*-
+    from django.conf.urls.defaults import *
+    from ragendja.urlsauto import urlpatterns
+    from ragendja.auth.urls import urlpatterns as auth_patterns
+    from django.contrib import admin
 
-* polls/urls.py を作成します。
-------------------------------------------------------------------------------
-# -*- coding: utf-8 -*-
-from django.conf.urls.defaults import *
+    admin.autodiscover()
 
-urlpatterns = patterns(
-  'polls.views',
-  url(r'^$', 'top', name='polls_top'),
-)
-------------------------------------------------------------------------------
+    handler500 = 'ragendja.views.server_error'
 
-* polls/views.py にメソッドを書きます。
-------------------------------------------------------------------------------
-# -*- coding: utf-8 -*-
-from django.http import HttpResponse, HttpResponseRedirect
-from django.utils.translation import ugettext as _
-from ragendja.template import render_to_response
+    urlpatterns = auth_patterns + patterns('',
+        ('^admin/(.*)', admin.site.root),
+        (r'^polls/', include('polls.urls')),
+    ) + urlpatterns
 
-def top(request):
-  return HttpResponse("Hello app-engine-patch!")
-------------------------------------------------------------------------------
+* polls/urls.py を作成します。::
+
+    # -*- coding: utf-8 -*-
+    from django.conf.urls.defaults import *
+
+    urlpatterns = patterns(
+      'polls.views',
+      url(r'^$', 'top', name='polls_top'),
+    )
+
+* polls/views.py にメソッドを書きます。::
+
+    # -*- coding: utf-8 -*-
+    from django.http import HttpResponse, HttpResponseRedirect
+    from django.utils.translation import ugettext as _
+    from ragendja.template import render_to_response
+
+    def top(request):
+      return HttpResponse("Hello app-engine-patch!")
 
 この状態で runserver すれば http://localhost:8080/polls/ で Hello メッ
 セージが見られます。
 
 
-6. Model を書きましょう。
-* polls/models.py を下記のようにします。
-------------------------------------------------------------------------------
-# -*- coding: utf-8 -*-
-from django.utils.translation import ugettext_lazy as _
-from google.appengine.ext import db
-import datetime
+Model を書きましょう。
+------------------------
 
-class Poll(db.Model):
-    question = db.StringProperty(verbose_name='question', required=True)
-    pub_date = db.DateTimeProperty(verbose_name='date published',
-                                   required=True)
-    def __unicode__(self):
-        return self.question
+* polls/models.py を下記のようにします。::
 
-    def was_published_today(self):
-        return self.pub_date.date() == datetime.date.today()
+    # -*- coding: utf-8 -*-
+    from django.utils.translation import ugettext_lazy as _
+    from google.appengine.ext import db
+    import datetime
 
-class Choice(db.Model):
-    poll = db.ReferenceProperty(Poll)
-    choice = db.StringProperty(verbose_name='choice', required=True)
-    votes = db.IntegerProperty(verbose_name='votes', required=True,
-                               default=0)
-    def __unicode__(self):
-        return self.choice
-------------------------------------------------------------------------------
+    class Poll(db.Model):
+        question = db.StringProperty(verbose_name='question', required=True)
+        pub_date = db.DateTimeProperty(verbose_name='date published',
+                                       required=True)
+        def __unicode__(self):
+            return self.question
 
-* これで manage.py shell できます
-$ python manage.py shell
-In [1]: from polls.models import Poll, Choice
-In [2]: Poll.all().get()
-(None)
-In [4]: import datetime
-In [5]: p = Poll(question="What's up?", pub_date=datetime.datetime.now())
-In [6]: p.put()
-Out[6]: datastore_types.Key.from_path('polls_poll', 2L, _app=u'tm-test')
-In [9]: p.key().id()
-Out[9]: 2L
-In [12]: p.question
-Out[12]: "What's up?"
-In [13]: p.pub_date 
-Out[13]: datetime.datetime(2009, 4, 22, 3, 8, 41, 420652)
-In [14]: p.pub_date = datetime.datetime(2007, 4, 1, 0, 0)
-In [15]: p.put()
-Out[15]: datastore_types.Key.from_path('polls_poll', 2L, _app=u'tm-test')
-In [17]: Poll.all().fetch(10)
-Out[17]: [Poll(**{'question': u"What's up?", 'pub_date': \
-	 datetime.datetime(2007, 4, 1, 0, 0)})]
+        def was_published_today(self):
+            return self.pub_date.date() == datetime.date.today()
 
+    class Choice(db.Model):
+        poll = db.ReferenceProperty(Poll)
+        choice = db.StringProperty(verbose_name='choice', required=True)
+        votes = db.IntegerProperty(verbose_name='votes', required=True,
+                                   default=0)
+        def __unicode__(self):
+            return self.choice
 
-7. Admin サイトを使います
-* polls/admin.py を下記のように作成します。
-------------------------------------------------------------------------------
-from polls.models import Poll
-from django.contrib import admin
+* これで manage.py shell できます::
 
-admin.site.register(Poll)
-------------------------------------------------------------------------------
+    $ python manage.py shell
+    In [1]: from polls.models import Poll, Choice
+    In [2]: Poll.all().get()
+    (None)
+    In [4]: import datetime
+    In [5]: p = Poll(question="What's up?", pub_date=datetime.datetime.now())
+    In [6]: p.put()
+    Out[6]: datastore_types.Key.from_path('polls_poll', 2L, _app=u'tm-test')
+    In [9]: p.key().id()
+    Out[9]: 2L
+    In [12]: p.question
+    Out[12]: "What's up?"
+    In [13]: p.pub_date 
+    Out[13]: datetime.datetime(2009, 4, 22, 3, 8, 41, 420652)
+    In [14]: p.pub_date = datetime.datetime(2007, 4, 1, 0, 0)
+    In [15]: p.put()
+    Out[15]: datastore_types.Key.from_path('polls_poll', 2L, _app=u'tm-test')
+    In [17]: Poll.all().fetch(10)
+    Out[17]: [Poll(**{'question': u"What's up?", 'pub_date': \
+         datetime.datetime(2007, 4, 1, 0, 0)})]
+
+Admin サイトを使います
+----------------------
+
+* polls/admin.py を下記のように作成します。::
+
+    from polls.models import Poll
+    from django.contrib import admin
+
+    admin.site.register(Poll)
+
 * settings.py の INSTALLED_APPS に 'polls' を追加しましょう。
 
 これで http://localhost:8000/admin にアクセスすれば Django の Admin が
 使えます!
 
 * ちょっと変更してみましょう。admin.site.register(Poll) の行を下記で置
-  き換えます。
-------------------------------------------------------------------------------
-class PollAdmin(admin.ModelAdmin):
-    fields = ['pub_date', 'question']
+  き換えます。::
 
-admin.site.register(Poll, PollAdmin)
-------------------------------------------------------------------------------
+    class PollAdmin(admin.ModelAdmin):
+        fields = ['pub_date', 'question']
+
+    admin.site.register(Poll, PollAdmin)
 
 どうなりましたか?
 
-* 今度は下記のように置き換えてみましょう。
-------------------------------------------------------------------------------
-class PollAdmin(admin.ModelAdmin):
-    fieldsets = [
-        (None,               {'fields': ['question']}),
-        ('Date information', {'fields': ['pub_date']}),
-    ]
+* 今度は下記のように置き換えてみましょう。::
 
-admin.site.register(Poll, PollAdmin)
-------------------------------------------------------------------------------
+    class PollAdmin(admin.ModelAdmin):
+        fieldsets = [
+            (None,               {'fields': ['question']}),
+            ('Date information', {'fields': ['pub_date']}),
+        ]
 
-* もう一つのパターンです
-------------------------------------------------------------------------------
-class PollAdmin(admin.ModelAdmin):
-    fieldsets = [
-        (None,               {'fields': ['question']}),
-        ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
-    ]
-------------------------------------------------------------------------------
+    admin.site.register(Poll, PollAdmin)
 
-* Choice を登録しましょう。polls/admin.py を下記のようにします。
-------------------------------------------------------------------------------
-from polls.models import Poll, Choice
-from django.contrib import admin
+* もう一つのパターンです::
 
-class ChoiceInline(admin.StackedInline):
-    model = Choice
-    extra = 3
+    class PollAdmin(admin.ModelAdmin):
+        fieldsets = [
+            (None,               {'fields': ['question']}),
+            ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
+        ]
 
-class PollAdmin(admin.ModelAdmin):
-    fieldsets = [
-        (None,               {'fields': ['question']}),
-        ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
-    ]
-    inlines = [ChoiceInline]
+* Choice を登録しましょう。polls/admin.py を下記のようにします。::
 
-admin.site.register(Poll, PollAdmin)
-admin.site.register(Choice)
-------------------------------------------------------------------------------
+    from polls.models import Poll, Choice
+    from django.contrib import admin
+
+    class ChoiceInline(admin.StackedInline):
+        model = Choice
+        extra = 3
+
+    class PollAdmin(admin.ModelAdmin):
+        fieldsets = [
+            (None,               {'fields': ['question']}),
+            ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
+        ]
+        inlines = [ChoiceInline]
+
+    admin.site.register(Poll, PollAdmin)
+    admin.site.register(Choice)
 
 これで Choice の編集画面では Poll を選べるように、また Poll にも
 Choice を追加できるようになりました。
 
-* StackedInline の代わりに TabularInline も使えます。
-------------------------------------------------------------------------------
-class ChoiceInline(admin.TabularInline):
-------------------------------------------------------------------------------
+* StackedInline の代わりに TabularInline も使えます。::
+
+    class ChoiceInline(admin.TabularInline):
 
 * Poll の一覧で表示する項目を変更してみましょう。まずは PollAdmin を編
-  集します。
-------------------------------------------------------------------------------
-class PollAdmin(admin.ModelAdmin):
-    # ...
-    list_display = ('question', 'pub_date', 'was_published_today')
-------------------------------------------------------------------------------
+  集します。::
 
-* polls/models.py の was_published_today に description を付けます。
-------------------------------------------------------------------------------
-class Poll(db.Model):
-    # ...
-    def was_published_today(self):
-        return self.pub_date.date() == datetime.date.today()
-    was_published_today.short_description = 'Published today?'
-------------------------------------------------------------------------------
+    class PollAdmin(admin.ModelAdmin):
+        # ...
+        list_display = ('question', 'pub_date', 'was_published_today')
 
-* PollAdmin に機能を足しましょう。
-------------------------------------------------------------------------------
-class PollAdmin(admin.ModelAdmin):
-    # ...
-    list_filter = ['pub_date']
-    search_fields = ['question']
-    date_hierarchy = 'pub_date'
-------------------------------------------------------------------------------
+* polls/models.py の was_published_today に description を付けます。::
+
+    class Poll(db.Model):
+        # ...
+        def was_published_today(self):
+            return self.pub_date.date() == datetime.date.today()
+        was_published_today.short_description = 'Published today?'
+
+* PollAdmin に機能を足しましょう。::
+
+    class PollAdmin(admin.ModelAdmin):
+        # ...
+        list_filter = ['pub_date']
+        search_fields = ['question']
+        date_hierarchy = 'pub_date'
+
 残念ながら、これらの機能は今は動かないようです。
 
 
-8. urls.py と view を書きましょう
+urls.py と view を書きましょう
+------------------------------
 
-* polls/urls.py
-------------------------------------------------------------------------------
-# -*- coding: utf-8 -*-
-from django.conf.urls.defaults import *
+* polls/urls.py::
 
-urlpatterns = patterns(
-  'polls.views',
-  url(r'^$', 'index', name='polls_index'),
-  url(r'^(?P<poll_id>\d+)/$', 'detail', name='polls_detail'),
-  url(r'^(?P<poll_id>\d+)/results/$', 'results', name='polls_results'),
-  url(r'^(?P<poll_id>\d+)/vote/$', 'vote', name='polls_vote'),
-)
-------------------------------------------------------------------------------
+    # -*- coding: utf-8 -*-
+    from django.conf.urls.defaults import *
 
-* index を作ります。polls/views.py 下記のように追加します。
-------------------------------------------------------------------------------
-from polls.models import Poll
+    urlpatterns = patterns(
+      'polls.views',
+      url(r'^$', 'index', name='polls_index'),
+      url(r'^(?P<poll_id>\d+)/$', 'detail', name='polls_detail'),
+      url(r'^(?P<poll_id>\d+)/results/$', 'results', name='polls_results'),
+      url(r'^(?P<poll_id>\d+)/vote/$', 'vote', name='polls_vote'),
+    )
 
-def index(request):
-    latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
-    output = ', '.join([p.question for p in latest_poll_list])
-    return HttpResponse(output)
+* index を作ります。polls/views.py 下記のように追加します。::
 
-def detail(request, poll_id):
-    pass
+    from polls.models import Poll
 
-def vote(request, poll_id):
-    pass
+    def index(request):
+        latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
+        output = ', '.join([p.question for p in latest_poll_list])
+        return HttpResponse(output)
 
-def results(request, poll_id):
-    pass
-------------------------------------------------------------------------------
+    def detail(request, poll_id):
+        pass
 
-* テンプレートを使うように変更します。
-------------------------------------------------------------------------------
-...
-from django.template import Context, loader
+    def vote(request, poll_id):
+        pass
 
-def index(request):
-    latest_poll_list = Poll.all().order('-pub_date').fetch(5)
-    t = loader.get_template('polls/index.html')
-    c = Context({
-        'latest_poll_list': latest_poll_list,
-    })
-    return HttpResponse(t.render(c))
-------------------------------------------------------------------------------
+    def results(request, poll_id):
+        pass
+
+* テンプレートを使うように変更します。::
+
+    ...
+    from django.template import Context, loader
+
+    def index(request):
+        latest_poll_list = Poll.all().order('-pub_date').fetch(5)
+        t = loader.get_template('polls/index.html')
+        c = Context({
+            'latest_poll_list': latest_poll_list,
+        })
+        return HttpResponse(t.render(c))
 
 ここでアクセスすると TemplateDoesNotExist が発生しますね。
 
-* テンプレートを書きましょう。polls/templates/index.html を書きます。
-------------------------------------------------------------------------------
-{% if latest_poll_list %}
-    <ul>
-    {% for poll in latest_poll_list %}
-        <li><a href="{% url polls_detail poll_id=poll.key.id %}">
-	    {{ poll.question }}
-	    </a>
-	</li>
-    {% endfor %}
-    </ul>
-{% else %}
-    <p>No polls are available.</p>
-{% endif %}
-------------------------------------------------------------------------------
+* テンプレートを書きましょう。polls/templates/index.html を書きます。::
+
+    {% if latest_poll_list %}
+        <ul>
+        {% for poll in latest_poll_list %}
+            <li><a href="{% url polls_detail poll_id=poll.key.id %}">
+            {{ poll.question }}
+            </a>
+        </li>
+        {% endfor %}
+        </ul>
+    {% else %}
+        <p>No polls are available.</p>
+    {% endif %}
 
 * ショートカットを使います。polls/views.py で下記のようにします。
   ragendja のショートカットもありますが、ここでは django のものを使いま
-  す。
-------------------------------------------------------------------------------
-from django.shortcuts import render_to_response
-# ...
-def index(request):
-    latest_poll_list = Poll.all().order('-pub_date').fetch(5)
-    return render_to_response('polls/index.html',
-                              {'latest_poll_list': latest_poll_list})
-------------------------------------------------------------------------------
+  す。::
 
-* detail view を書きましょう。polls/views.py
-------------------------------------------------------------------------------
-from ragendja.dbutils import get_object_or_404
-...
-def detail(request, poll_id):
-    p = get_object_or_404(Poll, id=long(poll_id))
-    return render_to_response('polls/detail.html', {'poll': p})
-------------------------------------------------------------------------------
+    from django.shortcuts import render_to_response
+    # ...
+    def index(request):
+        latest_poll_list = Poll.all().order('-pub_date').fetch(5)
+        return render_to_response('polls/index.html',
+                                  {'latest_poll_list': latest_poll_list})
 
-* polls/templates/detail.html
-------------------------------------------------------------------------------
-<h1>{{ poll.question }}</h1>
-<ul>
-{% for choice in poll.choice_set %}
-    <li>{{ choice.choice }}</li>
-{% endfor %}
-</ul>
-------------------------------------------------------------------------------
+* detail view を書きましょう。polls/views.py::
 
+    from ragendja.dbutils import get_object_or_404
+    ...
+    def detail(request, poll_id):
+        p = get_object_or_404(Poll, id=long(poll_id))
+        return render_to_response('polls/detail.html', {'poll': p})
 
-9. フォームを使用します。
+* polls/templates/detail.html::
 
-* polls/templates/detail.html を変更します。
-------------------------------------------------------------------------------
-<h1>{{ poll.question }}</h1>
+    <h1>{{ poll.question }}</h1>
+    <ul>
+    {% for choice in poll.choice_set %}
+        <li>{{ choice.choice }}</li>
+    {% endfor %}
+    </ul>
 
-{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
+フォームを使用します。
+--------------------------
 
-<form action="{% url polls_vote poll_id=poll.key.id %}" method="post">
-{% for choice in poll.choice_set %}
-    <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.key.id }}" />
-    <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
-{% endfor %}
-<input type="submit" value="Vote" />
-</form>
-------------------------------------------------------------------------------
+* polls/templates/detail.html を変更します。::
 
-* polls/views.py で vote を受け付けるように、また結果表示もしましょう。
-------------------------------------------------------------------------------
-from polls.models import Poll, Choice
-# ...
-from django.core.urlresolvers import reverse
-from google.appengine.ext import db
-# ...
+    <h1>{{ poll.question }}</h1>
 
-def vote(request, poll_id):
-    p = get_object_or_404(Poll, id=long(poll_id))
-    try:
-        selected_choice = p.choice_set.filter(
-            '__key__ =',
-            db.Key.from_path('polls_choice',
-                             long(request.POST['choice']))).get()
-        if selected_choice is None:
-          raise Exception('Specified choice not found.')
-    except Exception, e:
-        import logging
-        logging.info(e)
-        return render_to_response('polls/detail.html', {
-            'poll': p,
-            'error_message': "You didn't select a choice.",
-        })
-    def countup_vote(key):
-      c = Choice.get(key)
-      c.votes += 1
-      c.put()
-    db.run_in_transaction(countup_vote, selected_choice.key())
-    return HttpResponseRedirect(reverse('polls.views.results',
-                                        args=(p.key().id(),)))
+    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
 
-def results(request, poll_id):
-    p = get_object_or_404(Poll, id=long(poll_id))
-    return render_to_response('polls/results.html', {'poll': p})
-------------------------------------------------------------------------------
+    <form action="{% url polls_vote poll_id=poll.key.id %}" method="post">
+    {% for choice in poll.choice_set %}
+        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.key.id }}" />
+        <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
+    {% endfor %}
+    <input type="submit" value="Vote" />
+    </form>
 
-* polls/templates/results.html
-------------------------------------------------------------------------------
-<h1>{{ poll.question }}</h1>
+* polls/views.py で vote を受け付けるように、また結果表示もしましょう。::
 
-<ul>
-{% for choice in poll.choice_set %}
-    <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
-{% endfor %}
-</ul>
-------------------------------------------------------------------------------
+    from polls.models import Poll, Choice
+    # ...
+    from django.core.urlresolvers import reverse
+    from google.appengine.ext import db
+    # ...
 
-******************************************************************************
+    def vote(request, poll_id):
+        p = get_object_or_404(Poll, id=long(poll_id))
+        try:
+            selected_choice = p.choice_set.filter(
+                '__key__ =',
+                db.Key.from_path('polls_choice',
+                                 long(request.POST['choice']))).get()
+            if selected_choice is None:
+              raise Exception('Specified choice not found.')
+        except Exception, e:
+            import logging
+            logging.info(e)
+            return render_to_response('polls/detail.html', {
+                'poll': p,
+                'error_message': "You didn't select a choice.",
+            })
+        def countup_vote(key):
+          c = Choice.get(key)
+          c.votes += 1
+          c.put()
+        db.run_in_transaction(countup_vote, selected_choice.key())
+        return HttpResponseRedirect(reverse('polls.views.results',
+                                            args=(p.key().id(),)))
+
+    def results(request, poll_id):
+        p = get_object_or_404(Poll, id=long(poll_id))
+        return render_to_response('polls/results.html', {'poll': p})
+
+* polls/templates/results.html::
+
+    <h1>{{ poll.question }}</h1>
+
+    <ul>
+    {% for choice in poll.choice_set %}
+        <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
+    {% endfor %}
+    </ul>
+
 Polls アプリケーションが一応完成です。これで今日のハンズオンは終わりで
 す。もし時間があったらさらに機能を足してみましょう。
-******************************************************************************