Djangoのお勉強(４) 

[はじめての Django アプリ作成、その 3](https://docs.djangoproject.com/ja/2.0/intro/tutorial03/)の内容にそって試したことや調べた事を記載する。
前回の記事は [Djangoのお勉強(3)  ](https://qiita.com/ShimantoAkira/items/701cc891bb976a632cdd)

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

# チュートリアルのソース

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

```python: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'),
]
```

```python: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に変更し、下記のソースを追加

```python: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()](https://docs.djangoproject.com/ja/2.0/intro/tutorial03/#a-shortcut-render)に記載された内容になる

#　エラー送出
polls/views.pyのdetail関数を

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

```python: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を下記のように作成する

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

```python: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を書き換える

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

```html: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](https://docs.djangoproject.com/ja/2.0/ref/templates/builtins/#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..%}

よって、今回の例では

```html: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 %}
```
```html: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'),
]

```

とする事が出来る

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

```html:polls/admin.py
from django.contrib import admin

from .models import Question, Choice

admin.site.register(Question)
admin.site.register(Choice)
```

```html:polls/apps.py
from django.apps import AppConfig


class PollsConfig(AppConfig):
    name = 'polls'

```

```html: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)
```


```html: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'),
]

```

```html: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)
```


```html: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 %}
```

```html: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>
```