Help us understand the problem. What is going on with this article?

Djangoのお勉強(4)

More than 1 year has passed since last update.

はじめての Django アプリ作成、その 3の内容にそって試したことや調べた事を記載する。
前回の記事は Djangoのお勉強(3)

ここまででは、DBにデータをセットし、Viewを色々変えながらDBの中味を見るという処理をしてきた。
着目点を絞るために、チュートリアルにセットされたようなurlにレコード番号を指定する処理は、あえて(あえてなんですよ)簡単なテーブル表示に変えたけど、ここはチュートリアルに戻って見る。
DB上のデータはそのまま使えるので、そのまま。

チュートリアルのソース

本稿で勝手に変更したソースのうち、チュートリアルに戻す部分のソースは下記の通り

polls/url.py
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'),
]
polls/views.py
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に変更し、下記のソースを追加

polls/template/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 %}

これでショートカット: render()に記載された内容になる

 エラー送出

polls/views.pyのdetail関数を

polls/views.py
def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

から

polls/views.py
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を下記のように作成する

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()を使って、下記のように書き換えられる

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})

もうちょっとマシな表示にしてみる

「Question object (1) 」ってのは、あまりにもつまんないので、もう少しマシな表示にする
、、と言ってもチュートリアル通りにpolls/template/polls/detail.htmlを書き換える

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を変更した時に、この部分を変更しなくてもいいように改造する。

polls/template/polls/index.html
{% 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..%}

よって、今回の例では

polls/template/polls/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 %}
polls/urls.py
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'),
]

とする事が出来る

# ここまでのソース
ここまでのアプリ内のソースを示す

polls/admin.py
from django.contrib import admin

from .models import Question, Choice

admin.site.register(Question)
admin.site.register(Choice)
polls/apps.py
from django.apps import AppConfig


class PollsConfig(AppConfig):
    name = 'polls'

polls/modules.py
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)
polls/urls.py
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'),
]

polls/views.py
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)
polls/template/polls/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 %}
polls/template/polls/detail.html
<!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>
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away