目的
これからプログラミングを学ぼうとする初心者が「はじめてのDjangoアプリ作成」を順番通りにやって、そのときそのときに思ったこと感じたことを記録する備忘録。+記事を書くことで最後までやり通すためのモチベーション維持。ただいま「はじめてのDjangoアプリ作成その4」やってます。
環境
ThinkPad T440p
Ubuntu Linux 18.04 日本語 Remix
Python 3.6.7
pip 9.0.1
Django 2.1.3
簡単なフォームを書く
<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での記述方法〜
必要なところのみ抜粋・引用。
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?
これによるとchoice_set
ってDjango の ORM
が 自動でつくる RelatedManager
で、クエリセット作るんだよと。じゃあORMって何なの?
DjangoではORM(オブジェクト関係マッピング)を使ってデータベース操作を行う。
RelatedManagerは?
→ Related objects reference
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 もつくる
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におけるクラスベース汎用ビューの入門と使い方サンプル
URLconf の修正
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て何なの?
Djangoには関数ベースのビューと、クラスベースのビューの2種類がある
クラスベースの方は利用する時に as_view というクラスメソッドからviewオブジェクトを生成している
これも予め Django が用意してると。views.IndexView.as_view()
の部分はIndexViewをviewとして使えよということなんだろう。
2つ目と3つ目のパス文字列に一致するパターンの名前が から に変更されたことに注意してください。
ここを変えた理由は単純に文字数が少ないから?
views の修正
最終的なコードは以下のようになるはず。
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 に進んで、投票アプリのテストについて学びましょう。
なあ。初心者は使いこなすために何をどうすればいいのかわからないんだよ
なのでひとまず復習。知識の整理と確認。ある程度全体像を把握してからと思ったが、このままドツボにハマって難しくなったら放り投げそうだ。それより足元固めてこれだけはわかったぜ!というのを増やしたい。