Python+Djangoを使ってみる(チュートリアルをやってみる:第7回)


前回は、「Modelを覚える」という話でした。
今回は、「Viewを作る」ということをします。

Djangoのチュートリアルはここです。
ちなみに「tutorial02」は飛ばします。実際にやったことはやったのですが、チュートリアルが詳細だったので、特にコメントすることはありませんでした。

1.ざっくり言うと何をするのか?

MVCでいうViewは設計者目線で言えば「Boundary」です。Boundaryは、ブラウザに表示したり、ファイルを書き出したり、逆に入力を受け付けたり・・・など、システムと人との境界に位置する部分のことです。
Boundaryは入出力に特化して存在すべきで、ビジネスロジックやデータの実体からは切り離されているべきだ・・・ですね。

Djangoでは、(デザインを含めて)画面本体をTemplateとして分離しているようです。
このTemplateに対してデータをアタッチして表示させるのがViewの役目だと考えてよさそうです。
一般的なFrameworkでのView-Controllerの関係がTemplate-Viewとなります。用語が混乱していますが。

まとめると、
・Viewを叩くトリガーを知る
・ViewがTemplateをゴニョゴニョする仕組みを知る
・Templateが表示されると完了
という流れを覚えます。

2.URLを分解してViewを叩く

最近のほとんどのFrameworkがそうなのですが、URLの文字列に「何を」「どのように」やるのかを埋め込むのが流行のようです。
(用語が混乱しそうなのですが)MVCにおいて「どのControllerのどのActionに対してどのような変数を与えるか」というのをURLで構成した文字列で指示します。

まずは、その仕組みを理解します。
ここ(URL dispatcher)をさらっと眺める必要がありそうです。

・ブラウザからあるURLの表示要求が来ます。今回は「http://localhost:8000/polls/」です。
・その要求が「mysite/urls.py」に流れてきます。これはプロジェクト本体のファイルです。


from django.conf.urls import patterns, include, url

urlpatterns = patterns('',
    url(r'^polls/', include('polls.urls')),
)

・URL dispatcherは、「urlpatterns」を探します。上を見ると・・・ありましたね。
・正規表現に従って一致するurlを探します。・・・ありましたね。
・pollsAppの中のurlsを見に行けと言われます。・・・includeの意味を本当は正確に理解する必要がありそうですけど。
・で、要求が「polls/urls.py」に流れてきます。この時、既に判断に使った「polls」は除かれています。


from django.conf.urls import patterns, url
from polls import views

urlpatterns = patterns('',
    url(r'^$', views.index, name='index')
)

・正規表現に従って一致するurlを探します。・・・ありましたね。
・「views」の「index」を実行しなさい・・・と言われます。
・で、コイツは何か?と言うと、「polls/views.py」です。つまり、URLの解釈を引き渡しながら最終的に“そこ”のviews.pyを叩くのですね。


from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world. You're at the poll index.")

・これは、例えばPHPにおけるZendFrameworkと同じようなことをしています。つまりhttpのレスポンスをreturnするとそのまま表示してくれるのですね(ここをPDFダウンロードにしても別に構わないはず)。

2.ViewのAction

例えばこんなことをしたいのです。
・/polls/ で投票のリストを表示する
・/polls/5/ で5番目の投票を表示する
・/polls/5/results/ で5番目の投票結果を表示する
・/polls/5/vote/ で5番目の投票を行う
これらは、ActionをURLで指示しているということを意味しています。

そこで「polls/urls.py」を修正します。


from django.conf.urls import patterns, url
from polls import views

urlpatterns = patterns('',
    # ex: /polls/
    url(r'^$', views.index, name='index'),
    # ex: /polls/5/
    url(r'^(?P<poll_id>\d+)/$', views.detail, name='detail'),
    # ex: /polls/5/results/
    url(r'^(?P<poll_id>\d+)/results/$', views.results, name='results'),
    # ex: /polls/5/vote/
    url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'),
)

次に「polls/views.py」を修正します。


def detail(request, poll_id):
    return HttpResponse("You're looking at poll %s." % poll_id)

def results(request, poll_id):
    return HttpResponse("You're looking at the results of poll %s." % poll_id)

def vote(request, poll_id):
    return HttpResponse("You're voting on poll %s." % poll_id)

これで、URLを解釈して(urls.py)、所定のview(views.py)のActionを叩いた・・・ということになります。

3.Modelを使ってみる

上記はすべて静的なHTMLを表示していますが、実際にはそんなことはありません。
そこで、Modelを使って動的なデータを表示してみます。


from django.http import HttpResponse
from polls.models import Poll

def index(request):
    latest_poll_list = Poll.objects.order_by('-pub_date')[:5]
    output = ', '.join([p.question for p in latest_poll_list])
    return HttpResponse(output)

いくつか指摘しておかなければならない箇所があります。


latest_poll_list = Poll.objects.order_by('-pub_date')[:5]

Pollは「models.py」で定義したものです。デフォルトのマネジャ「objects」でアクセスします。この「objects」に対してクエリセット「order_by」を実行します。
[:5]は“スライス”で、インデックス0からインデックス5のスライサに含まれるデータを取得します。スライサは“データの間に挟み込む”ので、実際のデータとしては0番目から4番目を取得します。

次。


output = ', '.join([p.question for p in latest_poll_list])

まずは“リスト内包表記”から。


[p.question for p in latest_poll_list]

これはPythonのエレガントな書き方です(たぶん、処理も早い)。
“for…in”文なので、リストをグルグル回すのは想像できますが、
結局“[p.question,p.question,p.question,p.question,p.question]”(pは次々に変わっている)というリストが出来上がります。


output = ', '.join([a,b,c,d,e])

これは、“文字列連結のメソッド”です。実際にはリスト要素を ‘, ‘で連結するのですが、ここら辺はPHPなどとは感覚が異なるのでしょう。joinメソッドはタプルも連結しますし、文字列も連結(文字と文字の間に入っていく)します。

ちょっと話が逸れましたが、「このindexメソッドは問題がある」とチュートリアルには書いてあります。


There’s a problem here, though: the page’s design is hard-coded in the view. 
If you want to change the way the page looks, you’ll have to edit this Python code. 

なので、Templateを使います。

4.Templateを使う

tutorial02を飛ばしたので、大切な話が抜けてしまいました。
それはTemplateの置き場所です。どこでもいいとか、そんな所に置いてはダメだとか、色々ありますが、簡単に話を済ませます。

・まずは、Projectディレクトリ(外側のmysite)の直下にtemplatesディレクトリを作ります。
(たぶん、チュートリアルの記述では「pollsディレクトリ直下」にtemplatesディレクトリを作ることを想定しているようですが、ここは“私の趣味”でわざと一つ上に作ります。)
・(内側の)mysite/settings.pyを修正します。templateディレクトリへのフルパスを記述します。
(チュートリアルに従うと’/home/yanaka/Workspace/mysite/polls/templates’,になる。)
※追記
INSTALLED_APPSにAppが記載されていれば、その下のtemplatesディレクトリは自動的に探してくれるようです。
なのでAppにtemplatesを含めれば、 TEMPLATE_DIRSは空でも良いようです。
“フルパスで書け”というのが気持ち悪かったのですが、どうやらtemplatesはAppの下に入れてTEMPLATE_DIRSを書かないことにしそうです。


TEMPLATE_DIRS = (
    '/home/yanaka/Workspace/mysite/templates',
)

・templatesディレクトリの直下にpollsディレクトリを作ります。(こんな感じでtemplatesを集約できる。)
・templates/pollsの直下にindex.htmlを作ります。中身は以下。


{% if latest_poll_list %}
    
{% else %}
    

No polls are available.

{% endif %}

これがtemplateです。
・このtemplateを使うようにviewに変更を加えます。


def index(request):
    latest_poll_list = Poll.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = Context({
        'latest_poll_list': latest_poll_list,
    })
    return HttpResponse(template.render(context))

「http://127.0.0.1:8000/polls/」にアクセスしてみましょう。
正しく動いていれば完了です。

今回は以上です。
意外に量が多かったので飛ばし気味でしたが、View-Templateの関係についてはそんなにややこしいものではないので、慣れればOKだと思います。

ちょっと独り言。
“私の趣味”で、templateディレクトリを1階層上に置いてAppとは切り離しています。
何よりもTemplateがファイルの位置からしてビジネスロジックから切り離されているのは素晴らしいです。
いくら「疎結合にしよう」と思っていても、近くにあるとついつい雑に書いてしまうものですから。
しかし、AppをPackagingして再利用する場合には、やはりTemplateはAppディレクトリの下にあるべきなんだろうか・・・?という気がしないでもないです。
ここら辺は、「その時になればなんとでもなる」ことが分かったので、今はこれで良しとします。

チュートリアルはまだ4,5,6と続くのですが、一応記事としては打ち切りとします。

実際にモノを作り出しますので、今後はTipsをメインに記事に出来たらと思います。

The following two tabs change content below.

谷中

システム開発チームと検証チームのマネージャー。 「疎結合 小さなクラス 分業制」 を裏スローガンとし、これが実現できてこそ、幸せな開発者人生を過ごせるという確信のもと、上流から設計まで口を挟んだり挟まなかったりしています。

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>