Django公式 チュートリアル その4
Djangoをマスターしたいので、
チュートリアルにて一連の作業を学びます。
関連記事
- Django 仮想環境からインストールまで
- Django 公式チュートリアル
- Django公式 チュートリアル その1
- Django公式 チュートリアル その2
- Django公式 チュートリアル その3
- Django公式 チュートリアル その4
- Django公式 チュートリアル その5
- Django公式 チュートリアル その6
(個人的な学習ノートです)
前回Djangoのインストールから仮想環境の設定を行いました。
そのままチュートリアルに進みます!
「はじめてのDjangoアプリ作成 その4」
学習環境
macOS Sierra 10.12
python3.6
Django1.11
XAMPP
MySQL(MariaDB10.1.21)
テキストエディッタ:ATOM
参考サイト
はじめての Django アプリ作成、その 4 | Django documentation | Django
上記サイトに沿って進めます。
しんどい感じだけどもうちょい頑張ります!
Web 投票アプリケーションの開発を例にして、簡単なフォー ム処理とコードの縮小化を中心に解説
簡単なフォームを書く
前回作成した投票詳細テンプレート (「polls/detail.html」) を更新して、HTML の <form> 要素を挿入
( polls/templates/polls/detail.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>
<簡単な説明>
■各質問の選択肢のラジオボタンが表示
■フォームの action を {% url 'polls:vote' question.id %} に設定し、method="post" を設定
- method="post" を使用する (method="get" ではなく) ことは重要。なぜなら、フォームの送信はサーバ側のデータの更新につながるため。
- サーバ側のデータを更新するフォームを作成する場合、 method="post" を推奨。Django 固有のものではなく、いわば Web 開発の王道です。
■ forloop.counter は、ttag:for タグのループが何度実行されたかを表す値
■POST フォーム(データを改ざんされる恐れのある) を作成しているので、クロス サイトリクエストフォージェリを心配する必要がある。
送信されたデータを処理するための Django のビューを作成
チュートリアル3で、作成した、
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
を変更。
( polls/views.py )
from django.shortcuts import get_object_or_404, render from django.http import HttpResponseRedirect, HttpResponse 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: 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,)))
このコードには、これまで扱っていなかったことがいくつか入っている
■request.POSTは送信したキーの名前でデータにアクセスできる辞書のようなオブジェクト
- request.POST['choice'] は、選択された選択肢の ID を文字列として返す
- request.POSTの値は常に文字列
- GET データにアクセスするために同様に request.GETを提供している。ただし、このコードでは、POST を経由した呼び出しでないとデータを更新させないようにするために、request.POSTを明示的に使用。
- コードでKeyErrorをチェックし、 choice がない場合にはエラーメッセージ付きの質問フォームを再表示
HttpResponseRedirectを返す
■この例では、 HttpResponseRedirect コンストラクタの中で reverse()関数を使用
- この関数を使うと、ビュー関数中での URL のハードコードを防げる
- 関数には、制御を渡したいビューの名前と、そのビューに与える URL パターンの位置引数を与える
- この例では、 チュートリアル3で設定した URLconf を使っているので、 reverse()を呼ぶと、次のような文字列が返っくる
'/polls/3/results/'
- この 3 は question.id の値
- リダイレクト先の URL は 'results' ビューを呼び出し、最終的なページを表示
チュートリアル3でも触れたように、 request は HttpRequest オブジェクト
<h1>{{ question.question_text }}</h1> <ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li> {% endfor %} </ul> <a href="{% url 'polls:detail' question.id %}">Vote again?</a>
http://127.0.0.1:8000/polls/1/ にアクセスして確認してみましょう。
汎用ビューを使う(コードが少ないことはいいことだ!)
detail() ( チュートリアル3 ) と results() ビューはとても簡単で、冗長。
投票の一覧を表示する index() ビュー も同様。
これらのビューは基本的な Web開発の一般的なケース。
URL を介して渡されたパラメータに従ってデータベースからデータを取り出し、テンプレートをロードして、レンダリングしたテンプレートを返す。
これはよくあることなので、 Django では、汎用ビュー 「generic view」 というショートカットを提供している。
汎用ビューとは、よくあるパターンを抽象化して、 Python コードすら書かずにアプリケーションを書き上げられる状態にしたもの。
これまで作成してきた poll アプリを汎用ビューシステムに変換して、 コードをばっさり捨ててしまいましょう。
変換にはほんの数ステップしかか かりません。
そのステップ:
- URLconf を変換する。
- 古い不要なビューを削除する。
- 新しいビューに Djangoの汎用ビューを設定する。
URLconf の修正
URLconf の polls/urls.py を開き、次のように変更
( polls/urls.py )
from django.conf.urls import url from . import views app_name = 'polls' urlpatterns = [ url(r'^$', views.IndexView.as_view(), name='index'), url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'), url(r'^(?P<pk>[0-9]+)/results/$', views.ResultsView.as_view(), name='results'), url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'), ]
2 つ目と 3 つ目の正規表現でのマッチパターンの名前が <question_id> から <pk> に変更されたことに注意
views の修正
次に、古い index 、 detail 、と results のビューを削除し、代わりに Django の汎用ビューを使用。
これを行うには、 polls/views.py ファイルを開き、次のように変更します:
( polls/views.py )
from django.shortcuts import get_object_or_404, render from django.http import HttpResponseRedirect from django.urls import reverse from django.views import generic from .models import Choice, Question class IndexView(generic.ListView): template_name = 'polls/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 = 'polls/detail.html' class ResultsView(generic.DetailView): model = Question template_name = 'polls/results.html' def vote(request, question_id): ... # same as above, no changes needed.
ここでは、ListView と DetailView を使用します。
これらのビューはそれぞれ、「オブジェクトのリストを表示する」および「あるタイプのオブジェクトの詳細ページを表示する」という二つの概念を抽象化。
- 各汎用ビューは自分がどのモデルに対して動作するのか知らせておく必要があります。これは、 model 属性を使用して提供される。
- DetailView 汎用ビューには、 "pk" という名前で URL からプライマリキーをキャプチャして渡すことになっている、 汎用ビュー向けに question_id を pk に変更。
デフォルトでは、 DetailView 汎用ビューは <app name>/<model name>_detail.html という名前のテンプレートを使う。
この場合、テンプレートの名前は "polls/question_detail.html" 。
template_name 属性は Django に自動生成されたデフォルトのテンプレート名ではなく、指定した名前を使うように伝えるために使われる。
results リストビューにも template_name を指定。
これは、 結果ビューと詳細ビューがお互い DetailView であるにも関わらず、レンダリングされたとき違った外観を持っているため。
同様に、 ListView 汎用ビューは <app name>/<model name>_list.html というデフォルトのテンプレートを使うので、 template_name を使って ListView に既存の "polls/index.html" テンプレートを使用するように伝える。
このチュートリアルの前の部分では、 question や latest_question_list といった変数の入ったコンテキストをテンプレートに渡していました。
DetailView には、 question という変数が自動的に渡される。
なぜなら、 Django モデル (Question) を使用していて、 Django はコンテキスト変数にふさわしい名前を決めることができるからです。
一方で、 ListView では、自動的に生成されるコンテキスト変数は question_list になる。
これを上書きするには、 context_object_name 属性を与え、 latest_question_list を代わりに使用すると指定。
この代替アプローチとして、新しいデフォルトのコンテキスト変数を一致するようにテンプレートを変えることもできる。
しかし、ただ Django に使用したい変数名を伝えるほうが簡単だろう。
サーバを実行して、新しく汎用ビューベースにした投票アプリケーションを使ってみまる。
次回は、「初めてのDjangoアプリ作成、その5」に 取り掛かかります。