「はじめての Django アプリ作成」チュートリアルをやったメモ その3
はじめての Django アプリ作成、その 3 — Django v1.0 documentation http://djangoproject.jp/doc/ja/1.0/intro/tutorial03.html
前回の続きでチュートリアルを追ってみた個人的メモです。
環境: MacOS X 10.6.4 Python 2.6.5 Django 1.2.3
ビューの設計
Poll アプリケーションのビュー
- アーカイブ:最新の調査項目
- 詳細:調査項目と投票フォーム。開票結果は表示しない
- 開票結果:調査項目に対する結果
- 投稿:選択肢に対する投票を受け付ける
ビューは Python の関数として表現される。
URL スキームの設計
ビューを書く上での最初のステップは URL 構造の設計。 →URLconf と呼ばれる Python モジュールを作成
Django で作られたページをリクエストすると...
-
* settings.py の **ROOT_URLCONF** を探す
- コールバック関数には HttpRequest オブジェクトを第一引数として渡す
- 正規表現内でキャプチャした値をキーワード引数として渡す
- オプションの辞書オブジェクトが指定されていれば、その内容も追加のキーワード引数として渡す
例:ROOT_URLCONF = 'mysite.urls' * urls.py をロード * **urlpatterns** という変数を探す
(正規表現, Python のコールバック関数, [, オプションの辞書オブジェクト])
ex. (r'^admin/', include(admin.site.urls)), * 先頭のタプルから順にリクエストされた URL と正規表現がマッチするまでテストしていく * マッチしたら指定されているコールバック関数を呼び出す
patterns
参照:URL ディスパッチャ — Django v1.0 documentation http://djangoproject.jp/doc/ja/1.0/topics/http/urls.html
実際に urls.py を書いてみる
urlpatterns = patterns('',
(r'^polls/$', 'mysite.polls.views.index'),
(r'^polls/(?P<poll_id>\d+)/$', 'mysite.polls.views.detail'),
(r'^polls/(?P<poll_id>\d+)/results/$', 'mysite.polls.views.results'),
(r'^polls/(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
(r'^admin/', include(admin.site.urls)),
)
/polls/23 をリクエストすると...
detail(request=<HttpRequest object>, poll_id='23')
/polls/23/vote をリクエストすると...
vote(request=<HttpRequest object>, poll_id='23')
... と、関数が呼び出される。
poll_id='23' -> (?P<poll_id>\d+) ?P<poll_id> でマッチしたパターンを識別する名前をつけている \d+ は数字にマッチする正規表現
正規表現で実現できる限りの URL を表現できる。 こんなものだって (r'^polls/latest.php$', 'mysite.polls.views.index')
これらの正規表現では GET/POST のパラメーター、ドメイン名は検索されない。 /hoge/?vote_id=23 とリクエストがきても、/hoge/ を探す
正規表現は URLconf モジュールをロードする時にコンパイルされるので、高速に動作する。
ビュー作成
簡単に作ってみる
# polls/views.py
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world. You're at the poll index.")
def detail(request, poll_id):
return HttpResponse("Hello, world. You're at the poll %s." % poll_id)
/polls/ にアクセスすると Hello, world. You're at the poll index. が、 /polls/23/ にアクセスすると Hello, world. You're at the poll 23. が表示される。
各ビューは HttpResponse オブジェクトを返すか Http404 のような例外を返さなければいけない。
# polls/views.py
from mysite.polls.models import Poll
from django.http import HttpResponse
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)
最新 5 件の Poll をカンマで区切って日付順に表示させる。 ページのデザインがビューの中に記述されているのは良くない。
Django のテンプレートシステムを使って書く
# polls/views.py
from django.template import Context, loader
from mysite.polls.models import Poll
from django.http import HttpResponse
def index(request):
latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
t = loader.get_template('polls/index.html')
c = Context({
'latest_poll_list':latest_poll_list,
})
return HttpResponse(t.render(c))
settings.py の TEMPLATE_DIRS で設定したディレクトリに polls というディレクトリを作成し、index.html を作成する。
mytemplates/polls/index.html
{% if latest_poll_list %} {% for poll in latest_poll_list %} * [{{ poll.question
}}](/polls/{{ poll.id }}/ "{{ poll.question }}") {% endfor %} {% else %}
<p>No polls are available.</p>
{% endif %}
ページをリロードすると、箇条書きで Poll が表示される。
render_to_response() テンプレートをロードして、Context に値を入れてレンダリングして HttpResponse オブジェクトで返す、というのは良く使うので、一発でできる render_to_response() を使ったほうがいい。
# polls/views.py
from django.shortcuts import render_to_response
from mysite.polls.models import Poll
def index(request):
latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
return render_to_response('polls/index.html',
{'latest_poll_list' : latest_poll_list})
第一関数にテンプレート名の指定、第二引数(オプション)に辞書を指定する。 テンプレートを指定の Context でレンダリングし、HttpResponse オブジェクトにして返す かなり簡素化できた!
詳細ページの作成
# polls/views.py
from django.http import Http404
def detail(request, poll_id):
try:
p = Poll.objects.get(pk=poll_id)
except Poll.DoesNotExist:
raise Http404
return render_to_response('polls/detail.html', {'poll':p})
get_object_or_404 この、get()を実行しオブジェクトが無い時は Http404 を返す、というのは render_to_response() と同じくよく使う関数で、ショートカットが用意されている。
書き換えてみると....
# polls/views.py
from django.shortcuts import render_to_response, get_object_or_404
def detail(request, poll_id):
p = get_object_or_404(Poll, pk=poll_id)
return render_to_response('polls/detail.html', {'poll':p})
リストが空の場合は Http404 を出す get_object_or_404() という関数もある
カスタマイズした 404/500 を使用したい場合はテンプレートディレクトリのルートに それぞれ 404.html/500.html を置く
テンプレートディレクトリの polls/detail.html を作成
# {{ poll.question }}
{% for choice in poll.choice_set.all %}
* {{ choice.choice }}
{% endfor %}
poll.choice_set.all -> poll.choice_set_all()
Poll に属している Choice オブジェクトを取得し for で取り出している
URLconf の単純化
# urls.py
urlpatterns = patterns('',
(r'^polls/$', 'mysite.polls.views.index'),
(r'^polls/(?P<poll_id>\d+)/$', 'mysite.polls.views.detail'),
(r'^polls/(?P<poll_id>\d+)/results/$', 'mysite.polls.views.results'),
(r'^polls/(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
(r'^admin/', include(admin.site.urls)),
)
っていうのは mysite.polls.views と、同じものが書かれていて冗長な部分がある。 patterns の第一引数に prefix を設定すると...
# urls.py
urlpatterns = patterns('mysite.polls.views',
(r'^polls/$', 'index'),
(r'^polls/(?P<poll_id>\d+)/$', 'detail'),
(r'^polls/(?P<poll_id>\d+)/results/$', 'results'),
(r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'),
(r'^admin/', include(admin.site.urls)),
こんな感じにスッキリと書く事ができる。
複数 urlpatterns がある場合はこう書く。
urlpatterns = patterns('',
...
)
urlpatterns += patterns('',
...
)
URLconf の脱カップリング urls.py の設定をプロジェクトから切り離してアプリにくっつける。
urls.py を polls/urls.py にコピーする
プロジェクト側の urls.py を以下のように編集
# urls.py
urlpatterns = patterns('',
(r'^polls/', include('mysite.polls.urls')),
(r'^admin/', include(admin.site.urls)),
)
/polls/23/ にアクセスすると... ^polls/ にマッチすると、polls/ を削って、残りの 23/ を mysite.polls.urls という URLconf に送り、処理させる。
アプリ側の urls.py を以下のように編集
# polls/urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('mysite.polls.views',
(r'^$', 'index'),
(r'^(?P<poll_id>\d+)/$', 'detail'),
(r'^(?P<poll_id>\d+)/results/$', 'results'),
(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
)
polls/ が削られて、poll_id(さっきの例であれば 23/)のみが渡ってくるので polls/ は削除する。
これで /polls/ や /fun_polls/ /content/polls/ などどんな URL ルート下に置けるようになる。 polls アプリケーションは、絶対 URL ではなく、相対 URL だけに注意している。