はじめての Django アプリ作成、その 3の内容にそって試したことや調べた事を記載する。
前回の記事は Djangoのお勉強(3)
ここまででは、DBにデータをセットし、Viewを色々変えながらDBの中味を見るという処理をしてきた。
着目点を絞るために、チュートリアルにセットされたようなurlにレコード番号を指定する処理は、あえて(あえてなんですよ)簡単なテーブル表示に変えたけど、ここはチュートリアルに戻って見る。
DB上のデータはそのまま使えるので、そのまま。
チュートリアルのソース
本稿で勝手に変更したソースのうち、チュートリアルに戻す部分のソースは下記の通り
from django.urls import path
from . import views
urlpatterns = [
# ex: /polls/
path('', views.index, name='index'),
# ex: /polls/5/
path('<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'),
]
from django.http import 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)
template/pollsTmpを消して、template/pollsに変更し、下記のソースを追加
{% 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 %}
これでショートカット: render()に記載された内容になる
エラー送出
polls/views.pyのdetail関数を
def detail(request, question_id):
return HttpResponse("You're looking at question %s." % question_id)
から
from django.http import Http404
from django.shortcuts import render
from .models import Question
# ...
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})
に変更し、テンプレートpolls/template/polls/detail.htmlを下記のように作成する
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Detail</title>
</head>
<body>
{{ question }}
</body>
</html>
views.detail(の説明をする
question = Question.objects.get(pk=question_id)
は、question_id(この場合urlの一番右側の数字)をprimary keyとして検索してレコードを持ってくるという事になる。
"pk"について、何でいきなり出てくるのかわからなかったので、色々ググったんだけど、どうもprimary keyの事らしい。
"id"も指定できるらしい。
DB屋さんじゃないから、わかんないけど、DB屋さんの間では、常識なんだろうか。。。
上記の式が失敗すると、
raise Http404("Question does not exist")
が呼ばれて404エラーになる。
それ以外は、
return render(request, 'polls/detail.html', {'question': question})
が呼ばれて、detail.htmlテンプレートの中の{{ question }}に、Questionテーブルのオブジェクトが表示される。(実際には「Question object (1) 」のようなつまんない表示をする)
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})
の部分は、get_object_or_404()を使って、下記のように書き換えられる
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})
もうちょっとマシな表示にしてみる
「Question object (1) 」ってのは、あまりにもつまんないので、もう少しマシな表示にする
、、と言ってもチュートリアル通りにpolls/template/polls/detail.htmlを書き換える
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
これで、選択したQuestion のquestion_textと、このQuestion のidに紐付けられているChoiceテーブルのレコードのchoice_textを表示する。
choice_setは、Questionに紐づけされているChoiceテーブルを表すらしい。
「らしい」というのは、ドキュメントを探したけど出てこなくて、ぐぐったらかすかに"<テーブル名>_set"で、紐づけされているテーブルを表すような記述があったから。
ここの文脈的にもそうだし。
テンプレート中のurlのハードコードを解消する
polls/template/polls/index.htmlテンプレート内に
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
とurlがハードコードされているが、url名を変更した場合、テンプレート内のこの部分も変更しなければならなくて美しくないので、polls/urls.pyを変更した時に、この部分を変更しなくてもいいように改造する。
{% 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 %}
タグ{% url %}は、
{% url 'urlパターン名' v1 v2..%}
の形式をとり、urlパターン名は、urls.py内で指定されたnameを持つurlパターンを指定する。
v1 v2、、、は、引数を示し、urlパターン内で定義された変数を指定する。
今回の場合urlパターンは
path('<int:question_id>/', views.detail, name='detail'),
のように設定されているので、int:question_idに対応する、question_idを指定する。
今回question_idの値は、latest_question_listイテレータから得られるquestionオブジェクトのidメンバなので、question.idになる。
これにより、テンプレートを変更しなくても、urls.pyのurlパターンの定義を変更するだけで、このテンプレートが生成するLINK先が変更される。
urlパターン名は、他のアプリで同じ名前が使われていると衝突する。
これを回避する為に、urlパターン名にアプリ名を指定する事が出来る
アプリ名は<アプリ名>/urls.pyの中に
app_name = 'polls'
を追加して指定する。
テンプレートでは、urlパターン名の手前に"アプリ名:"を追加する
{% url 'アプリ名:urlパターン名' v1 v2..%}
よって、今回の例では
{% 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 %}
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
# ex: /polls/
path('', views.index, name='index'),
# ex: /polls/5/
path('<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'),
]
とする事が出来る
# ここまでのソース
ここまでのアプリ内のソースを示す
from django.contrib import admin
from .models import Question, Choice
admin.site.register(Question)
admin.site.register(Choice)
from django.apps import AppConfig
class PollsConfig(AppConfig):
name = 'polls'
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
# ex: /polls/
path('', views.index, name='index'),
# ex: /polls/5/
path('<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'),
]
from django.http import HttpResponse
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)
{% 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 %}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Detail</title>
</head>
<body>
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
</body>
</html>