DreamerDreamのブログ

夢想家の夢です。〜揚げたてのモヤっとしたものをラフレシアと共に〜

Django奮闘記⑤もっと自由な世界を求めてーテンプレートの使い方ー

 

前回

dreamerdream.hateblo.jp

に引き続き、こちらのサイトのサンプルを試していきます。

はじめての Django アプリ作成、その 3 | Django documentation | Django

 

pollsディレクトリ内にtemplatesディレクトリを生成します。

そうするとそこからDjangoが各アプリのテンプレートを探してくれるようです。

raspberrypi:~/dreamer $ mkdir polls/templates

さらにpollsというディレクトリを作り、index.htmlというファイルを作ります。

raspberrypi:~/dreamer $ mkdir polls/templates/polls
raspberrypi:~/dreamer $ nano polls/templates/polls/index.html

何層ものディレクトリだけどDjango内では「polls/index.html」として扱われるそうです。

templatesの中に直接index.htmlを置くとDjangoが混乱するのか、使う人が混乱するのかは今の所わからないけど、どのアプリのindex.htmlの話なのか解りにくくなるそうなのでtemplatesディレクトリ内にもう一度アプリ名のディレクトリを作るのが良いそうです。

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.shortcuts import render
from django.http import HttpResponse
# Create your views here.
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))

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)

 

 

サーバー起動! 

raspberrypi:~/dreamer $ python3 manage.py runserver 0:8000

 

あれあれ?エラー?

f:id:DreamerDream:20180903163328p:plain

しかしそこはpythonさん、ちゃんと理由を教えてくれます。ターミナルでも同じメッセージが出ています。

f:id:DreamerDream:20180904160602p:plain

Questionの名前がみつかりません。

はい、ゴメンナサイ。書いていませんでした。。

f:id:DreamerDream:20180903163709p:plain

書いたよー!もっかい!

f:id:DreamerDream:20180903163915p:plain

あれあれれ?

f:id:DreamerDream:20180904160647p:plain

今度はloaderが見つからないぞと

ゴメンナサイゴメンナサイ!pythonさん!

renderとloaderを見間違えてたなんて。。

f:id:DreamerDream:20180903164031p:plain

もっかいチャンスをください!!

3度目の正直!

f:id:DreamerDream:20180903164209p:plain

ヨッシャァァアアアーーーー!!!

やっと通ったぁあああ!!!

 

 polls/views.pyのindex関数部分

def index(request):

 #latest_question_listにSQLiteから引き出したquestionをリスト化して格納
 latest_question_list = Question.objects.order_by('-pub_date')[:5]#

 #polls/templates/poolls/index.htmlをテンプレートとして呼び出して
 template = loader.get_template('polls/index.html')

 #コンテキストにリストを辞書方式で格納して
 context = {
  'latest_question_list': latest_question_list,
 }

 #テンプレートをコンテキストでレンダリングして値を返す
 return HttpResponse(template.render(context, request)) 

という流れですね。(違ってたら教えてください。)

 

で、この中の「ロードしてコンテキストに格納して値を返す」という一連の流れがよく使われるからrender()関数としてまとめられている。ということで

先ほどのはコメントアウトして、

from django.shortcuts import render
from django.http import HttpResponse
from django.template import loader
from .models import Question
# Create your views here.
'''
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))
'''
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)

 試してみましょう。

raspberrypi:~/dreamer $ python3 manage.py runserver 0:8000

f:id:DreamerDream:20180904085246p:plain

ちゃんと動いた。へー!便利なんですねー

renderは第一引数にリクエスト値、第二引数にテンプレート、第三引数にコンテキストを適用させるようです。

 

ここからは、面倒くさいエラー処理に関する事項です。

値の無いアドレスを読み出すと404エラー表示するというものです。面倒ですが理解するため頑張ります。

raspberrypi:~/dreamer nano polls/views.py

from django.shortcuts import render
from django.http import HttpResponse
from django.template import loader
from .models import Question
from django.http import Http404
# Create your views here.
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})

 

と編集して

detail用のテンプレートを生成

raspberrypi:~/dreamer nano polls/templates/polls/detail.html

中身は

{{ question }}

だけ。

実行すると、questionのあるものは値が表示され、

f:id:DreamerDream:20180904091315p:plain

無いものは404エラーで下にraise Http404("Question does not exist")で渡した値が表示されています。

f:id:DreamerDream:20180904091326p:plain

 

でこの404エラーもショートカット関数が利用できるのですね。

from django.http import HttpResponse
from django.template import loader
from .models import Question
from django.http import Http404
from django.shortcuts import get_object_or_404, render
# Create your views here.
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 detail(request, question_id):
 question = get_object_or_404(Question, pk=question_id)
 return render(request, 'polls/detail.html', {'question': question})

試してみると、先ほどとメッセージが違います。これが自動生成されたメッセージなんですね。

f:id:DreamerDream:20180904091738p:plain

 

次にdetal.htmlの中身を弄ってquestion内容が表示されるようにします。

raspberrypi:~/dreamer nano polls/templates/polls/detail.html

中身は

<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

で、forループ内でpythonのquestion.choice_set.all()を呼んでいるそうです。(よくわからん)

その結果、polls/(値)を入れるとをの中身が見えます。

f:id:DreamerDream:20180904093344p:plain

なんと、値は全角でも大丈夫なようです

f:id:DreamerDream:20180904093354p:plain

無い値を入れると当然404エラーが返ります。(viewsは弄ってないですから当然ですね)

f:id:DreamerDream:20180904093402p:plain

 

 

次に{%url%}を利用してテンプレート内のハードコートされたコードに依存性を無くすという方法があるようですので試してみます。

raspberrypi:~/dreamer $ nano 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>-->
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}

urls.pyを書き換えます

raspberrypi:~/dreamer $ nano polls/urls.py

# path('<int:question_id>/', views.detail, name='detail'),
path('test/<int:question_id>/', views.detail, name='detail'),

引数の前のURLに好きな名前を付けると

f:id:DreamerDream:20180904094911p:plain

おー、アドレスがdetailではなく、testとなっています。

で、これが何が便利かといいますと・・・・・・・わかりません!

僕の今のレベルではよくわかりませんが、きっと使いこなせると便利なことに気が付くのでしょう!

 

で、この方法をすると、「じゃあ他のアプリ使ったときに名前がかぶらね?」となるってことらしいのですよ。うん、なりそうですね。

その解決方法にurls.pyにapp_nameという名前を設定するそうです。

どれどれ?

raspberrypi:~/dreamenano polls/urls.py

を変更といっても1カ所だけ

from django.urls import path

from . import views

app_name = 'polls' #←ここだけ
urlpatterns = [
path('', views.index, name='index'),
# ex: /polls/5/
# path('<int:question_id>/', views.detail, name='detail'),
path('test/<int:question_id>/', views.detail, name='detail'),
# ex: /polls/5/results/
path('<int:question_id>/results/', views.results, name='results'),
# ex: /polls/5/vote/
path('<int:question_id>/vote/', views.vote, name='vote'),
]

 

うん、この状態でそのままアクセスするとどうなるか試したみたら

f:id:DreamerDream:20180904095810p:plain

怒られた。そりゃそうか

raspberrypi:~/dreamer $ nano 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>-->
<!--<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>-->
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}

実行すると、あれ?やっぱ怒られた?

f:id:DreamerDream:20180904100456p:plain

ずずーと下を見ると「ここがおかしいですよー」とDjangoさんが教えてくれてます。

え?これってセキュリティー的には大丈夫なの?

ともかく、<!---->でコメントアウト出来てると思っていたものが出来ていなかったということですね。テンプレートのコメントアウトはHTML方式ではないのかな?

f:id:DreamerDream:20180904100506p:plain

とりあえず、コメントアウトした行を消し去るとちゃんと見えました。

f:id:DreamerDream:20180904101040p:plain

f:id:DreamerDream:20180904100802p:plain

結構謎な部分を残しながらですが、ひとまずテンプレートってこんな感じで柔軟性のあるコードも書けるんですよー。てことをやんわりと勉強しました。

次回はフォームの書き方

はじめての Django アプリ作成、その 4 | Django documentation | Django

を参考に奮闘します。

 

dreamerdream.hateblo.jp