Django チュートリアルを進めていく3 (docker-composeを使って)
どうもこんにちは、ごっちです。
前回の続きです。
今回も公式のチュートリアルに則って進めます。
このアプリケーションで作りたいビュー
- Question の一覧ページ — index
- Question の詳細ページ — detail
- Question の結果ページ — result
- 投票ページ
ビューをごりごり作っていく
polls]views.py
を編集します
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)
これだけあってもなにもできないので、polls/urls.py
でpolls.urls
と結びつけます。
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]
ここで実際に http://localhost:8000/polls/1/
にアクセスしてみると、しっかりと You're looking at question 1.
とか表示されます。
URLの設定はここで好きなようにできるけど、スマートにいきたいよね。。っと公式には書いてあります。
今のままの def index:
では何も index として機能していないので、書き直します。
polls/views.py
を編集します。
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)
そろそろ HTML を触っていきたいですね。HTMLのテンプレートを用意します。
pollsディレクトリ配下に templates
ディレクトリを作成します。Djangoは templates
からテンプレートを探すようです。
そしてその templates
ディレクトリ配下に polls
ディレクトリを作ります。これで名前空間を与えます。
っで、今は index のビューを作るので、いま作った 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 %}
このテンプレートを使うために、polls/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))
あと、いちいち HttpResponse()
を書くのは非常にめんどくさいので、ショートカットを読み込んで書き直します。
from django.shortcuts import get_object_or_404, 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)
これで HttpResponse()
を書く必要もなくなりましたし、 import する必要もなくなりました。
ここで、 http://localhost:8000/polls/
にアクセスすると、きちんとリストになっています!
こうなっているはずです。
404
/detail
にアクセスしたときに存在しないれこーとの数字を入力したときに 404 エラーを表示させないといけません。
polls/views.py
を編集します。
from django.http import Http404
from django.template import loader
from .models import Question
# ...
def detail(request, question_id):
try:
question = get_object_or_404(Question, pk=question_id)
except Question.DoesNotExist:
raise Http404("Question dose not exist.")
return render(request, 'polls/detail.html', {'question': question})
これで、リクエストしたIDが存在しない場合は404として処理されます。
render
するので、 polls/templates/polls/detail.html
を作ります
ショートカット
ここもいちいち、try
して、存在しない場合は except
をするのはめんどくさいので、そのへんのものもインポートして polls/views.py
を編集します。
from django.shortcuts import get_object_or_404, render
from .models import Question
# ...
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/detail.html', {'question': question})
なんで、 ObjectDoesNotExist
例外をキャッチしないで、ヘルパー関数の get_object_or_404()
を使うんでしょうね?
チュートリアルのドキュメントによると
答えは、モデルレイヤとビューレイヤをカップリングしてしまうからです。 Django の最も大きな目標の一つは、ルーズカップリングの維持にあります。いくつかの制御カップリングは、 django.shortcuts モジュールの中にあります
とのことです。
detail のテンプレート
もう少し polls/templates/polls/detail.html
を編集します。
<h1>{{ question }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
選択肢のテキストも表示させます。
index のハードコード部分の削除
polls/templates/polls/index.html
のリンクの部分がハードコーディングされているので修正します。
<li><a href="{% url 'detail' question.id %}">{{question.question_text}}</a></li>
これで、 polls/urls.py
の設定が変更されても対応ができます。
URL の名前空間
同一プロジェクトのほかのアプリとURLが衝突しないように、 polls/urls.py
を編集します。アプリケーションの名前空間を設定するのには、 app_name
を追加します。
from django.conf.urls import url
from . import views
app_name = 'polls'
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]
名前空間を追加したので、さっきの polls/templates/polls/index.html
のaタグのリンクを修正します。
<li><a href="{% url 'polls:detail' question.id %}">{{question.question_text}}</a></li>
いい感じにできあがってきているはずです。
今日はここまで。。
所感
今回はコマンドでごにょごにょすることはなかったですね。まぁ、サーバーを立ち上げるのにコマンドを入力しますけどね!!
References
- はじめての Django アプリ作成、その 3, https://docs.djangoproject.com/ja/1.11/intro/tutorial03/
- Django チュートリアルを進めていく2 (docker-composeを使って), https://medium.com/@gggooottto/django-%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB%E3%82%92%E9%80%B2%E3%82%81%E3%81%A6%E3%81%84%E3%81%8F2-docker-compose%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6-4e67a945ddf
- YutaGoto/django_tutorial_project, https://github.com/YutaGoto/django_tutorial_project
Originally published at gist.github.com.