はじめに
このチュートリアルは、データベースのセットアップが終わったところから始まります。私たちはウェブ投票アプリケーションを続け、公開インターフェイス(「ビュー」)の作成に焦点を当てます。
このチュートリアルは、データベースのセットアップが終わったところから始まります。私たちはウェブ投票アプリケーションを続け、公開インターフェイス(「ビュー」)の作成に焦点を当てます。
ビューは、Djangoアプリケーション内の「種類」の1つのWebページで、一般的に特定の機能を果たし、特定のテンプレートを持っています。たとえば、ブログアプリケーションでは、次のようなビューがあるかもしれません。
私たちの投票アプリケーションでは、次の4つのビューがあります。
Djangoでは、Webページやその他のコンテンツはビューによって提供されます。各ビューはPython関数(クラスベースのビューの場合はメソッド)で表されます。Djangoは、要求されたURL(正確には、ドメイン名の後のURLの部分)を調べることでビューを選択します。
今、あなたがWeb上で時間を過ごしてきた中で、ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B
のような美しいものに出会ったことがあるかもしれません。Djangoが私たちにそれよりもはるかにエレガントな URLパターン を許可してくれることを知って嬉しいでしょう。
URLパターンはURLの一般的な形式です - たとえば:/newsarchive/<year>/<month>/
。
URLからビューに到達するために、Djangoは「URLconf」と呼ばれるものを使用します。URLconfはURLパターンをビューにマッピングします。
このチュートリアルでは、URLconfの使用方法に関する基本的な説明を行い、詳細については/topics/http/urls
を参照してください。
では、polls/views.py
にいくつかのビューを追加しましょう。これらのビューは少し異なります。引数を取るからです。
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)
次の ~django.urls.path
呼び出しを追加することで、これらの新しいビューを polls.urls
モジュールに接続します。
polls/urls.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"),
]
次に、サーバーを再度起動します。
cd ~/project/mysite
python manage.py runserver 0.0.0.0:8080
Web 8080 タブに切り替えて、/polls/34/
にアクセスします。detail()
メソッドが実行され、URLに提供したIDが表示されます。/polls/34/results/
と /polls/34/vote/
も試してみてください。これらは、仮の結果と投票ページを表示します。
誰かがあなたのウェブサイトからページを要求するとき、たとえば /polls/34/
の場合、Djangoは ROOT_URLCONF
設定によって指定されているため、mysite.urls
Pythonモジュールを読み込みます。urlpatterns
という名前の変数を見つけ、順番にパターンを辿ります。'polls/'
で一致するものを見つけた後、一致するテキスト("polls/"
)を取り除き、残りのテキスト("34/"
)を 'polls.urls' URLconfに送り、さらに処理します。そこでは '<int:question_id>/'
と一致し、次のように detail()
ビューが呼び出されます。
detail(request=<HttpRequest object>, question_id=34)
question_id=34
の部分は <int:question_id>
から来ています。角括弧を使うことで、URLの一部を「キャプチャ」し、ビュー関数にキーワード引数として渡します。文字列の question_id
の部分は、一致したパターンを識別するために使用される名前を定義し、int
の部分は、URLパスのこの部分に一致するパターンを決定するコンバーターです。コロン(:
)はコンバーターとパターン名を区切ります。
各ビューは、2つのことのいずれかを行う責任があります。要求されたページのコンテンツを含む ~django.http.HttpResponse
オブジェクトを返すこと、または ~django.http.Http404
などの例外を発生させることです。それ以外はあなた次第です。
あなたのビューはデータベースからレコードを読み取ってもよいし、読み取らなくてもよいです。Djangoのようなテンプレートシステムを使ってもよいし、サードパーティのPythonテンプレートシステムを使ってもよいし、使わなくてもよいです。PDFファイルを生成したり、XMLを出力したり、オンザフライでZIPファイルを作成したり、あなたが望むことは何でも、あなたが望むPythonライブラリを使って行うことができます。
Djangoが望むのは、その ~django.http.HttpResponse
だけです。または例外です。
便利なので、チュートリアル2で扱ったDjango自身のデータベースAPIを使いましょう。ここに新しい index()
ビューの試しです。このビューは、公開日付に基づいて、システム内の最新の5つの投票質問をコンマ区切りで表示します。
polls/views.py
ファイルを編集して、次のように変更します。
from django.http import HttpResponse
from.models import Question
def index(request):
latest_question_list = Question.objects.order_by("-pub_date")[:5]
output = ", ".join([q.question_text for q in latest_question_list])
return HttpResponse(output)
## Leave the rest of the views (detail, results, vote) unchanged
ただし、ここに問題があります。ページのデザインがビューにハードコードされています。ページの見た目を変更したい場合、このPythonコードを編集する必要があります。ですから、Djangoのテンプレートシステムを使って、デザインをPythonから分離しましょう。ビューが使用できるテンプレートを作成します。
まず、polls
ディレクトリに templates
という名前のディレクトリを作成します。Djangoはそこにあるテンプレートを探します。
あなたのプロジェクトの TEMPLATES
設定は、Djangoがテンプレートを読み込み、レンダリングする方法を記述しています。デフォルトの設定ファイルは、APP_DIRS <TEMPLATES-APP_DIRS>
オプションが True
に設定された DjangoTemplates
バックエンドを構成しています。慣例として、DjangoTemplates
は各 INSTALLED_APPS
内の "templates" サブディレクトリを探します。
先ほど作成した templates
ディレクトリ内に、さらに polls
という名前のディレクトリを作成し、その中に index.html
という名前のファイルを作成します。つまり、あなたのテンプレートは polls/templates/polls/index.html
にあるはずです。上記の app_directories
テンプレートローダーの動作方法のため、Django内でこのテンプレートを polls/index.html
として参照できます。
今、私たちはテンプレートを直接 polls/templates
に置くことができるかもしれません(別の polls
サブディレクトリを作成する代わりに)が、実際には悪い考えです。Djangoは名前が一致する最初のテンプレートを選びます。もし異なるアプリケーションに同じ名前のテンプレートがあった場合、Djangoはそれらを区別できません。
私たちはDjangoに正しいものを指すことができるようにする必要があり、これを確実にする最良の方法は、それらを 名前空間化 することです。つまり、それらのテンプレートをアプリケーション自体に名付けられた 別の ディレクトリの中に置くことです。
そのテンプレートに次のコードを入れます。
{% 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 %}
注:
チュートリアルを短くするため、すべてのテンプレートの例は不完全なHTMLを使用しています。あなた自身のプロジェクトでは、完全なHTMLドキュメント を使用する必要があります。
では、polls/views.py
の index
ビューを更新して、テンプレートを使用するようにしましょう。
from django.http import HttpResponse
from django.template import loader
from.models import Question
def index(request):
latest_question_list = Question.objects.order_by("-pub_date")[:5]
template = loader.get_template("polls/index.html")
context = {
"latest_question_list": latest_question_list,
}
return HttpResponse(template.render(context, request))
そのコードは polls/index.html
という名前のテンプレートを読み込み、コンテキストを渡します。コンテキストは、テンプレート変数名をPythonオブジェクトにマッピングする辞書です。
再度サーバーを起動します。
python manage.py runserver 0.0.0.0:8080
ブラウザで "/polls/" にアクセスしてページを読み込むと、チュートリアル2の "What's up" の質問が含まれる箇条書きリストが表示されるはずです。リンクは質問の詳細ページを指しています。
~django.shortcuts.render
テンプレートを読み込み、コンテキストを埋め、レンダリングされたテンプレートの結果を含む ~django.http.HttpResponse
オブジェクトを返すのは、非常に一般的な慣用句です。Djangoはショートカットを提供しています。以下は、書き直された完全な index()
ビューです。
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)
これをすべてのビューで行った後、~django.template.loader
と ~django.http.HttpResponse
をインポートする必要はもうありません(detail
、results
、vote
のスタブメソッドがまだある場合は、HttpResponse
を残しておく必要があります)。
~django.shortcuts.render
関数は、最初の引数に要求オブジェクト、2番目の引数にテンプレート名、オプションの3番目の引数に辞書を取ります。与えられたコンテキストでレンダリングされた与えられたテンプレートの ~django.http.HttpResponse
オブジェクトを返します。
では、質問の詳細ビューを取り扱いましょう。このビューは、特定の投票に対する質問文を表示するページです。以下がそのビューです。
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})
ここでの新しい概念は、要求されたIDの質問が存在しない場合、ビューが ~django.http.Http404
例外を発生させることです。
後で polls/detail.html
テンプレートに何を入れるかについて説明しますが、すぐに上記の例を動作させたい場合は、ただ次の内容のファイルがあれば始めることができます。
{{ question }}
~django.shortcuts.get_object_or_404
~django.db.models.query.QuerySet.get
を使用し、オブジェクトが存在しない場合に ~django.http.Http404
を発生させるのは、非常に一般的な慣用句です。Djangoはショートカットを提供しています。以下が書き直された detail()
ビューです。
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})
~django.shortcuts.get_object_or_404
関数は、最初の引数にDjangoモデルを取り、任意の数のキーワード引数を取り、それらをモデルのマネージャーの ~django.db.models.query.QuerySet.get
関数に渡します。オブジェクトが存在しない場合、~django.http.Http404
を発生させます。
なぜ、ヘルパー関数 ~django.shortcuts.get_object_or_404
を使って、より上位のレベルで ~django.core.exceptions.ObjectDoesNotExist
例外を自動的にキャッチしたり、モデルAPIが ~django.http.Http404
を ~django.core.exceptions.ObjectDoesNotExist
の代わりに発生させたりしないのでしょうか?
それは、モデル層をビュー層に結合させてしまうからです。Djangoの最も重要な設計目標の1つは、緩やかな結合を維持することです。django.shortcuts
モジュールには、いくつかの制御された結合が導入されています。
~django.shortcuts.get_list_or_404
関数もあります。これは ~django.shortcuts.get_object_or_404
と同じように機能しますが、~django.db.models.query.QuerySet.get
の代わりに ~django.db.models.query.QuerySet.filter
を使用します。リストが空の場合、~django.http.Http404
を発生させます。
投票アプリケーションの detail()
ビューに戻りましょう。コンテキスト変数 question
が与えられた場合、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 }}
の例では、まずDjangoはオブジェクト question
の辞書検索を行います。それが失敗した場合、属性検索を試みます。この場合、それは機能します。属性検索が失敗した場合、リストインデックス検索を試みるでしょう。
メソッド呼び出しは {% for %}<for>
ループで行われます。question.choice_set.all
は、Pythonコード question.choice_set.all()
として解釈され、Choice
オブジェクトの反復可能オブジェクトを返し、{% for %}<for>
タグで使用するのに適しています。
覚えていますか?polls/index.html
テンプレート内の質問へのリンクを書いたとき、リンクは次のように部分的にハードコードされていました。
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
このハードコードされた密結合のアプローチの問題は、多くのテンプレートがあるプロジェクトでURLを変更するのが難しくなることです。ただし、polls.urls
モジュールの ~django.urls.path
関数で名前引数を定義したので、{% url %}
テンプレートタグを使用することで、URL設定で定義された特定のURLパスに対する依存を排除することができます。
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
この機能は、polls.urls
モジュールで指定されたURL定義を参照することによって機能します。detail
というURL名がどこで定義されているかを正確に見ることができます。以下に示します。
## the 'name' value as called by the {% url %} template tag
path("<int:question_id>/", views.detail, name="detail"),
投票の詳細ビューのURLを別のものに変更したい場合、たとえば polls/specifics/12/
のようなものに変更したい場合は、テンプレート(または複数のテンプレート)で行うのではなく、polls/urls.py
で変更します。
テンプレート自体をまったく変更する必要はありません。
## added the word'specifics'
path("specifics/<int:question_id>/", views.detail, name="detail"),
チュートリアルプロジェクトには polls
という1つのアプリケーションのみがあります。本格的なDjangoプロジェクトでは、5つ、10つ、20つ以上のアプリケーションがある場合もあります。Djangoはそれらの間でURL名をどのように区別するのでしょうか?たとえば、polls
アプリケーションには detail
ビューがあり、同じプロジェクト内のブログ用のアプリケーションにもそれがあるかもしれません。{% url %}
テンプレートタグを使用してURLに対してDjangoがどのアプリケーションビューを作成するかを知らせるにはどうすればよいでしょうか?
答えは、URLconfに名前空間を追加することです。polls/urls.py
ファイルで、アプリケーション名前空間を設定するための app_name
を追加しましょう。
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/index.html
テンプレートを以下のように変更します。
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
名前空間化された詳細ビューを指すようにします。
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
ビューの作成に慣れたら、フォーム処理とコードの削減 を読んで、フォーム処理と汎用ビューの基本を学びましょう。
おめでとうございます!あなたは「公開インターフェイスビューの作成」実験を完了しました。あなたの技術を向上させるために、LabExでさらに多くの実験を行って練習することができます。