今回もDjangoの公式サイトから学ぼうと思います。
Contents
準備
djangoがインストールされているか確認
$ python -m django –version
なければインストール
$ pip install django==2.1
サーバーを起動
$ python manage.py runserver
Viewを書く
views.pyに基本的な機能を果たす関数を書きます。
from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the polls index.") def detail(request, question_id): return HttpResponse("You're looking at question %s." % question_id) def results(request, question_id): response = "You're looking at the results of question %s." return HttpResponse(response % question_id) def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id)
views.pyとurls.pyを紐づけます。
urls.pyに次のpath()コールを追加すると、新しいviews.pyの関数をpolls.urlsモジュールと結びつけます。
from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), path('<int:question_id>/', views.detail, name='detail'), path('<int:question_id>/results/', views.results, name='results'), path('<int:question_id>/vote/', views.vote, name='vote'), ]
ブラウザを開いて、http://127.0.0.1:8000/polls/100/と打ち込む。
detailsメソッドが実行され、
You’re looking at question 100.と表示される。
実際に動作するviewを書く
各viewには2つの役割があります。
・リクエストされたページのコンテンツを含むHttpResponseオブジェクトを返す事
・Http404のような例外の送出
Django にとって必要なのはHttpResponseか、あるいは例外です。
その2のAPI を使ってみましょう。試しに次のようなindex()ビューを作ります。これは、システム上にある最新の 5 件の質問項目をカンマで区切り、日付順に表示するビューです。
from django.http import HttpResponse from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] output = ', '.join([q.question_text for q in latest_question_list]) return HttpResponse(output)
このコードには問題があります。ビューの中で、ページのデザインがハードコードされています。ページの見栄えを変更するたびに、 Python コードを編集する必要があります。 Django のテンプレートシステムを使って、ビューから使用できるテンプレートを作成し、Python からデザインを分離しましょう。
最初にpolls ディレクトリの中に、 templates ディレクトリを作成します。
$ cd polls
templatesディレクトリを作成
$ mkdir templates
$ cd templates
pollsディレクトリを作成
$ mkdir polls
$ cd polls
index.htmlファイルを作成
$ touch index.html
公式サイトからの引用
最初に、
polls
ディレクトリの中に、templates
ディレクトリを作成します。 Django はそこからテンプレートを探します。プロジェクトの
TEMPLATES
には、Django がどのようにテンプレートをロードしレンダリングするかが書かれています。デフォルトの設定ファイルでは、DjangoTemplates
バックエンドが設定されており、そのAPP_DIRS
のオプションがTrue
になっています。規約により、DjangoTemplates
はINSTALLED_APPS
のそれぞれの “templates” サブディレクトリを検索します。先ほど作成した
templates
ディレクトリ内でpolls
というディレクトリを作成し、さらにその中にindex.html
というファイルを作成してください。つまり、テンプレートはpolls/templates/polls/index.html
に書く必要があります。app_directories
テンプレートローダは前述(訳注: APP_DIRS の説明)のように動くため、Django 内でこのテンプレートを単にpolls/index.html
のように参照できます。
polls/templates/polls/index.htmlを記述
{% if latest_question_list %} <ul> {% for question in latest_question_list %} <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li> {% endfor %} </ul> {% else %} <p>No polls are available.</p> {% endif %}
views.pyを修正
from django.http import HttpResponse from django.template import loader from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] template = loader.get_template('polls/index.html') context = { 'latest_question_list': latest_question_list, } return HttpResponse(template.render(context, request))
ブラウザで “/polls/” を開くと、箇条書きのリストが表示されるはずです(リストには、 チュートリアルその2 で作った “What’s up” という質問が入っていますね)。
render()
renderによって、loaderやHttpResponseをimportする必要がなくなります。
render()関数は、第1引数にrequestオブジェクトを第2引数にテンプレート名を、第3引数(任意)に辞書を受け取ります。
この関数はHttpResponseを返します。
from django.shortcuts import render from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] context = {'latest_question_list': latest_question_list} return render(request, 'polls/index.html', context) def detail(request, question_id): return HttpResponse("You're looking at question %s." % question_id) def results(request, question_id): response = "You're looking at the results of question %s." return HttpResponse(response % question_id) def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id)
Http404
views.pyを書き換えます。
このビューは、リクエストした ID を持つ質問が存在しないときにHttp404を送出します。
from django.shortcuts import render from django.http import Http404 from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] context = {'latest_question_list': latest_question_list} return render(request, 'polls/index.html', context) def detail(request, question_id): try: question = Question.objects.get(pk=question_id) except Question.DoesNotExist: raise Http404("Question does not exist") return render(request, 'polls/detail.html', {'question': question}) def results(request, question_id): response = "You're looking at the results of question %s." return HttpResponse(response % question_id) def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id)
ショートカット:get_object_or_404
from django.shortcuts import get_object_or_404, render from django.http import Http404 from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] context = {'latest_question_list': latest_question_list} return render(request, 'polls/index.html', context) def detail(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/detail.html', {'question': question}) def results(request, question_id): response = "You're looking at the results of question %s." return HttpResponse(response % question_id) def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id)
polls/detail.htmlを記述
detail.htmlを作ります。
<h1>{{ question.question_text }}</h1> <ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }}</li> {% endfor %} </ul>
テンプレート内のハードコードされたURLを削除
このハードコードされた、密結合のアプローチの問題は、プロジェクトにテンプレートが多数ある場合、URLの変更が困難になってしまうことです。しかし、polls.urlsモジュール の path()関数で name 引数を定義したので、テンプレートタグの{%url%}を使用して、URL 設定で定義されている特定の URL パスへの依存をなくすことができますと公式サイトにあります。
{% if latest_question_list %} <ul> {% for question in latest_question_list %} <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li> {% endfor %} </ul> {% else %} <p>No polls are available.</p> {% endif %}
urls.pyも変更
from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), path('<int:question_id>/', views.detail, name='detail'), path('<int:question_id>/results/', views.results, name='results'), path('<int:question_id>/vote/', views.vote, name='vote'), # added the word 'specifics' path('specifics/<int:question_id>/', views.detail, name='detail'), ]
URL名の名前空間
djangoがurl名を区別する為に書き換えます。
公式サイトから引用
このチュートリアルプロジェクトが持つアプリは
polls
アプリ1つだけです。実際の Django プロジェクトでは、5個、10個、20個、あるいはそれ以上のアプリがあるかもしれません。 Django はどうやってこれらの間の URL 名を区別するのでしょうか? 例えば、polls
アプリはdetail
ビューを含みますが、同じプロジェクトにブログのためのアプリがあり、そのアプリも同名のビューを含むかもしれません。{% url %}
テンプレートタグを使ったとき、 Django はどのアプリのビューに対して url を作成すればいいでしょうか? これを Django にどう知らせればいいでしょうか。答えは、 URLconf に名前空間を追加すること、です。どうぞ
polls/urls.py
ファイル内でapp_name
を追加し、アプリケーションの名前空間を設定してください。
from django.urls import path from . import views app_name = 'polls' urlpatterns = [ path('', views.index, name='index'), path('<int:question_id>/', views.detail, name='detail'), path('<int:question_id>/results/', views.results, name='results'), path('<int:question_id>/vote/', views.vote, name='vote'), ]
index.htmlの書き換え
名前空間つきの詳細ビューを指すように書き換え
{% if latest_question_list %} <ul> {% for question in latest_question_list %} <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li> {% endfor %} </ul> {% else %} <p>No polls are available.</p> {% endif %}
この回は、1度読んだだけだと難しいかもしれませんね。ブラウザでの確認は次のその4を終えたあとで行います。