初心者がDjangoのチュートリアルで何をやり何を考えているか(4)

## 目的
これからプログラミングを学ぼうとする初心者が「はじめてのDjangoアプリ作成」を順番通りにやって、そのときそのときに思ったこと感じたことを記録する備忘録。＋記事を書くことで最後までやり通すためのモチベーション維持。ただいま「はじめてのDjangoアプリ作成その4」やってます。

## 環境
ThinkPad T440p
Ubuntu Linux 18.04 日本語 Remix
Python 3.6.7
pip 9.0.1
Django 2.1.3


## 簡単なフォームを書く

```html
<h1>{{ question.question_text }}</h1>

{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}

<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
{% for choice in question.choice_set.all %}
    <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
    <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
{% endfor %}
<input type="submit" value="Vote">
</form>
```

初出の form、action、method について。この記事がわかりやすかった。
 → [formタグのaction,method属性の使い方と特性〜html,slimでの記述方法〜](https://qiita.com/mikuhonda/items/f3126380d3340f3d8a2b)
必要なところのみ抜粋・引用。
>formタグ
>入力フォームや送信フォームを表示させたい時に使う。
>formタグの間に他のタグを入れることによって様々な部品を配置することができる

>action属性
>フォームの送信ボタンを押して送信されるデータの送信先を指定する

>method属性
>送信するときの転送方法を指定する


これも初出の `csrf_token` はクロスサイトリクエストフォージェリとかいう攻撃から身を守るためのおまじないみたいなものらしい。予め Django に middleware として入っている。この`csrf_tokenタグ`を`<form>`タグの内側に入れとけとのこと。
→ https://docs.djangoproject.com/en/dev/ref/csrf/


`choice_set.all`がどっから出てきたのかわからなかったのでググる。同じように感じている人が世界にはいるらしい。stackoverflowに同じ質問があった。

→ [Django tutorial: What is choice_set?](https://stackoverflow.com/questions/2048777/django-tutorial-what-is-choice-set)

これによると`choice_set`ってDjango の `ORM` が 自動でつくる `RelatedManager`で、クエリセット作るんだよと。じゃあORMって何なの？

→ [Django データベース操作 についてのまとめ](
https://qiita.com/okoppe8/items/66a8747cf179a538355b)
>DjangoではORM（オブジェクト関係マッピング）を使ってデータベース操作を行う。


RelatedManagerは？
→  [Related objects reference](https://docs.djangoproject.com/en/2.1/ref/models/relations/)

>class RelatedManager : 
>A “related manager” is a manager used in a one-to-many or many-to-many related context.

つまりだ。`choice_set.all`って Django がええ感じに自動的につくってくれたモノってことでいいか。で`choice`という名前のラジオボタンを作って、id はループ何回したかで番号つけて、その番号を返す(valueが送信される値）と。それからlabelタグでフォーム部品とラベルを関連付けると。

次に vote の views もつくる

```python
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse

from .models import Choice, Question
# ...
def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:pluralize
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):/
        # Redisplay the question voting form.
        return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "You didn't select a choice.",
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()
        # Always return an HttpResponseRedirect after successfully dealing
        # with POST data. This prevents data from being posted twice if a
        # user hits the Back button.
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

```
pluralize : これは複数形にするときに使うものらしいが、なぜ複数形にするのかよくわからない。選択するのはラジオボタンだったら1個しか選べないはず。ん？と思ったが、集計で、1 vote が 2 votes になる変わることに気がついた。おそらくこれと絡んで`selected_choice.votes` が複数形になってるんだろう。

次に results の views.py を追加して、それからresults.html テンプレートを作成する。この辺りは一緒な感じなので以下略。

## 汎用ビューを使う: コードが少ないのはいいことだ

で、今まで index、detail、result の view をつくってきたけれども、それらをひとまとめにしたのが汎用ビューってことらしい。ひな型をつくってそれぞれにカスタマイズして出すよってことなのかしらん。

>これらのビューは基本的な Web開発の一般的なケースを表します。すなわち、 URL を介して渡されたパラメータに従ってデータベースからデータを取り出し、テンプレートをロードして、レンダリングしたテンプレートを返します。これはきわめてよくあることなので、 Django では、汎用ビュー（generic view）というショートカットを提供しています。

リストにしてどこでそれをやるのかまとめると下のようになるはず。
1. URLを参照する → URLconf=urls.py 
2. データベースからデータを取り出す → views.py
3. テンプレートをロードする  → views.py + template
4. レンダリングしたテンプレートを返す  → views.py

ちなみにこのサイトもわかりやすかったのでご参考までに。
→  [Djangoにおけるクラスベース汎用ビューの入門と使い方サンプル](https://qiita.com/felyce/items/7d0187485cad4418c073)

## URLconf の修正

```python
from django.urls import path

from . import views

app_name = 'polls'
urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
    path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
    path('<int:question_id>/vote/', views.vote, name='vote'),
]
```
as_view って何？って思ったからググる。そのままが出てくる。 

→ [Djangoのクラスベースビューのas_viewて何なの？](https://docs.djangoproject.com/ja/2.1/intro/tutorial04/)
>Djangoには関数ベースのビューと、クラスベースのビューの2種類がある
クラスベースの方は利用する時に as_view というクラスメソッドからviewオブジェクトを生成している

これも予め Django が用意してると。` views.IndexView.as_view()`の部分はIndexViewをviewとして使えよということなんだろう。


>2つ目と3つ目のパス文字列に一致するパターンの名前が <question_id> から <pk> に変更されたことに注意してください。

ここを変えた理由は単純に文字数が少ないから？

## views の修正

最終的なコードは以下のようになるはず。

```python
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic

from .models import Choice, Question


class IndexView(generic.ListView):
    template_name = 'pollsapp/index.html'
    context_object_name = 'latest_question_list'

    def get_queryset(self):
        """Return the last five published questions."""
        return Question.objects.order_by('-pub_date')[:5]


class DetailView(generic.DetailView):
    model = Question
    template_name = 'pollsapp/detail.html'


class ResultsView(generic.DetailView):
    model = Question
    template_name = 'pollsapp/results.html'

def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        # Redisplay the question voting form.
        return render(request, 'pollsapp/detail.html', {
            'question': question,
            'error_message': "You didn't select a choice.",
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()
        # Always return an HttpResponseRedirect after successfully dealing
        # with POST data. This prevents data from being posted twice if a
        # user hits the Back button.
        return HttpResponseRedirect(reverse('pollsapp:results', args=(question.id,)))

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'pollsapp/index.html', context)

def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'pollsapp/detail.html', {'question': question})

def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'pollsapp/results.html', {'question': question})
```
コピペしまくり（タイポで動かないのは嫌だ！）だが正常に動いた。エラーが出ることなく普通に動いてくれるというのが単純に嬉しい。

>フォームや汎用ビューを使いこなせるようになったら、 チュートリアルその5 に進んで、投票アプリのテストについて学びましょう。

**なあ。初心者は使いこなすために何をどうすればいいのかわからないんだよ**

なのでひとまず復習。知識の整理と確認。ある程度全体像を把握してからと思ったが、このままドツボにハマって難しくなったら放り投げそうだ。それより足元固めてこれだけはわかったぜ！というのを増やしたい。