最初のDjangoアプリの作成、パート3 —Djangoドキュメント

提供:Dev Guides
< DjangoDjango/docs/3.2.x/intro/tutorial03
移動先:案内検索

最初のDjangoアプリの作成、パート3

このチュートリアルは、 Tutorial 2 が中断したところから始まります。 私たちはWeb-pollアプリケーションを継続しており、パブリックインターフェイスである「ビュー」の作成に焦点を当てます。

助けを得る場所:

このチュートリアルで問題が発生した場合は、FAQの Geting Help セクションにアクセスしてください。


概要

ビューは、DjangoアプリケーションのWebページの「タイプ」であり、通常、特定の機能を提供し、特定のテンプレートを備えています。 たとえば、ブログアプリケーションでは、次のビューが表示される場合があります。

  • ブログのホームページ–最新のいくつかのエントリを表示します。
  • エントリの「詳細」ページ–単一エントリのパーマリンクページ。
  • 年ベースのアーカイブページ–指定された年のエントリを含むすべての月を表示します。
  • 月ベースのアーカイブページ–指定された月のエントリを含むすべての日を表示します。
  • 日ベースのアーカイブページ–指定された日のすべてのエントリを表示します。
  • コメントアクション–特定のエントリへのコメントの投稿を処理します。

投票アプリケーションでは、次の4つのビューがあります。

  • 質問の「インデックス」ページ–最新のいくつかの質問を表示します。
  • 質問の「詳細」ページ–質問テキストが表示され、結果は表示されませんが、投票するフォームが表示されます。
  • 質問の「結果」ページ–特定の質問の結果を表示します。
  • 投票アクション–特定の質問の特定の選択肢への投票を処理します。

Djangoでは、Webページやその他のコンテンツはビューによって配信されます。 各ビューは、Python関数(またはクラスベースのビューの場合はメソッド)で表されます。 Djangoは、要求されたURL(正確には、ドメイン名の後のURLの部分)を調べてビューを選択します。

今、あなたはウェブ上でME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1Bのような美しさに出くわしたかもしれません。 Djangoでは、それよりもはるかにエレガントな URLパターンが可能であることを知っていただければ幸いです。

URLパターンは、URLの一般的な形式です(例:/newsarchive/<year>/<month>/)。

URLからビューに移動するために、Djangoは「URLconfs」と呼ばれるものを使用します。 URLconfは、URLパターンをビューにマップします。

このチュートリアルでは、URLconfの使用に関する基本的な手順を説明します。詳細については、 URLディスパッチャーを参照してください。


より多くのビューを書く

次に、polls/views.pyにさらにいくつかのビューを追加しましょう。 これらの見解は、議論をするため、わずかに異なります。

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)

次の 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'),
]

ブラウザの「/ polls / 34 /」をご覧ください。 detail()メソッドを実行し、URLで指定したIDを表示します。 「/ polls / 34 / results /」と「/ polls / 34 / vote /」も試してください。これらはプレースホルダーの結果と投票ページを表示します。

誰かがあなたのウェブサイトからページをリクエストすると-たとえば「/ polls / 34 /」、:setting: `ROOT_URLCONF` 設定によってポイントされるため、Djangoは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パスのこの部分に一致するパターンを決定するコンバーターです。 コロン(:)は、コンバーターとパターン名を区切ります。


実際に何かをするビューを書く

各ビューは、要求されたページのコンテンツを含む HttpResponse オブジェクトを返すか、 Http404 などの例外を発生させるという2つのことのいずれかを実行します。 後は君しだい。

ビューはデータベースからレコードを読み取ることができるかどうかはわかりません。 Djangoなどのテンプレートシステム(またはサードパーティのPythonテンプレートシステム)を使用することも、使用しないこともできます。 PDFファイルの生成、XMLの出力、ZIPファイルの作成など、必要なものを、必要なPythonライブラリを使用して実行できます。

Djangoが望んでいるのは、 HttpResponse だけです。 または例外。

便利なので、 Tutorial 2 で説明したDjango独自のデータベースAPIを使用しましょう。 これは、新しいindex()ビューでの1つのスタブです。このビューには、公開日に従って、システム内の最新の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はそこでテンプレートを探します。

プロジェクトの:setting: `TEMPLATES` 設定は、Djangoがテンプレートをロードしてレンダリングする方法を記述します。 デフォルト設定ファイルは、DjangoTemplates バックエンドの :setting: `APP_DIRS ` オプションがに設定されているTrue 。 慣例により、DjangoTemplatesは、:setting: `INSTALLED_APPS` のそれぞれで「templates」サブディレクトリを探します。

作成したtemplatesディレクトリ内に、pollsという別のディレクトリを作成し、その中にindex.htmlというファイルを作成します。 つまり、テンプレートはpolls/templates/polls/index.htmlにある必要があります。 上記のようにapp_directoriesテンプレートローダーがどのように機能するかにより、Django内でこのテンプレートをpolls/index.htmlと呼ぶことができます。

テンプレートの名前空間

これで、テンプレートをpolls/templatesに直接配置する(別のpollsサブディレクトリを作成するのではなく)可能性がありますが、実際には悪い考えです。 Djangoは、名前が一致する最初のテンプレートを選択します。異なるアプリケーションに同じ名前のテンプレートがある場合、Djangoはそれらを区別できません。 Djangoを正しいものに向けることができる必要があります。これを確実にする最良の方法は、名前空間を使用することです。 つまり、これらのテンプレートを、アプリケーション自体にちなんで名付けられた別のディレクトリ内に配置します。


そのテンプレートに次のコードを配置します。

polls / templates / 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 %}

ノート

チュートリアルを短くするために、すべてのテンプレートの例では不完全なHTMLを使用しています。 独自のプロジェクトでは、完全なHTMLドキュメントを使用する必要があります。


次に、polls/views.pyindexビューを更新して、テンプレートを使用します。

polls / views.py

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オブジェクトにマッピングする辞書です。

ブラウザで「/ polls /」を指定してページを読み込むと、 Tutorial 2 の「What'sup」の質問を含む箇条書きが表示されます。 リンクは質問の詳細ページを指しています。

ショートカット: render()

テンプレートをロードし、コンテキストを埋めて、レンダリングされたテンプレートの結果で HttpResponse オブジェクトを返すことは非常に一般的なイディオムです。 Djangoはショートカットを提供します。 index()の完全なビューは、次のように書き直されています。

polls / views.py

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)

これらすべてのビューでこれを実行すると、ローダーHttpResponse をインポートする必要がなくなることに注意してください(まだHttpResponseを保持する必要があります) detailresults、およびvoteのスタブメソッドがあります。

render()関数は、最初の引数として要求オブジェクト、2番目の引数としてテンプレート名、オプションの3番目の引数として辞書を取ります。 指定されたコンテキストでレンダリングされた指定されたテンプレートの HttpResponse オブジェクトを返します。


404エラーを発生させる

それでは、質問の詳細ビュー(特定の投票の質問テキストを表示するページ)に取り組みましょう。 ビューは次のとおりです。

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})

ここでの新しい概念:要求されたIDの質問が存在しない場合、ビューは Http404 例外を発生させます。

そのpolls/detail.htmlテンプレートに何を入れることができるかについては少し後で説明しますが、上記の例をすばやく機能させたい場合は、次のものだけを含むファイルを使用してください。

投票/テンプレート/投票/詳細.html

{{ question }}

今のところ始めましょう。

ショートカット: get_object_or_404()

オブジェクトが存在しない場合は、 get()を使用し、 Http404 を上げるのが非常に一般的なイディオムです。 Djangoはショートカットを提供します。 これがdetail()ビューで、書き直されました。

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})

get_object_or_404()関数は、最初の引数としてDjangoモデルを取り、任意の数のキーワード引数を取り、モデルのマネージャーの get()関数に渡します。 オブジェクトが存在しない場合は、 Http404 が発生します。

哲学

上位レベルで ObjectDoesNotExist 例外を自動的にキャッチする代わりに、ヘルパー関数 get_object_or_404()を使用するのはなぜですか、またはモデルAPIに Http404 の代わりに Http404 を発生させるのはなぜですか。 X215X] ObjectDoesNotExist ?

これは、モデルレイヤーをビューレイヤーに結合するためです。 Djangoの最も重要な設計目標の1つは、緩い結合を維持することです。 django.shortcuts モジュールには、いくつかの制御された結合が導入されています。


get_list_or_404()関数もあります。これは、 get()の代わりに filter()を使用することを除いて、 get_object_or_404()と同じように機能します。 。 リストが空の場合、 Http404 が発生します。


テンプレートシステムを使用する

投票アプリケーションのdetail()ビューに戻ります。 コンテキスト変数questionが与えられた場合、polls/detail.htmlテンプレートは次のようになります。

投票/テンプレート/投票/詳細.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に対して辞書ルックアップを実行します。 これに失敗すると、属性ルックアップが試行されます。この場合は機能します。 属性ルックアップが失敗した場合は、リストインデックスルックアップを試行します。

メソッド呼び出しはで発生します :ttag: `{%for%} ` ループ:question.choice_set.all Pythonコードとして解釈されますquestion.choice_set.all() 、の反復可能オブジェクトを返しますChoiceオブジェクトであり、での使用に適しています :ttag: `{%for%} ` 鬼ごっこ。

テンプレートの詳細については、テンプレートガイドを参照してください。


テンプレート内のハードコードされたURLの削除

polls/index.htmlテンプレートに質問へのリンクを書き込んだとき、リンクは次のように部分的にハードコーディングされていたことを思い出してください。

<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

このハードコードされた緊密に結合されたアプローチの問題は、多数のテンプレートを使用するプロジェクトのURLを変更することが困難になることです。 ただし、polls.urlsモジュールの path()関数でname引数を定義したため、{% 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を別のURLに変更する場合は、テンプレート(または複数のテンプレート)で行うのではなく、polls/specifics/12/のようなものに変更します。polls/urls.pyで変更します。

...
# added the word 'specifics'
path('specifics/<int:question_id>/', views.detail, name='detail'),
...

名前空間のURL名

チュートリアルプロジェクトには、pollsという1つのアプリしかありません。 実際のDjangoプロジェクトでは、5、10、20以上のアプリが存在する可能性があります。 Djangoはそれらの間でURL名をどのように区別しますか? たとえば、pollsアプリにはdetailビューがあるため、ブログ用の同じプロジェクトのアプリである可能性があります。 {% url %}テンプレートタグを使用するときに、DjangoがURLに対して作成するアプリビューを認識できるようにするにはどうすればよいですか?

答えは、URLconfに名前空間を追加することです。 polls/urls.pyファイルで、先に進み、app_nameを追加して、アプリケーションの名前空間を設定します。

polls / urls.py

from django.urls import path

from . import views

app_name = 'polls'
urlpatterns = [
    path('', views.index, name='index'),
    path('<int:question_id>/', views.detail, name='detail'),
    path('<int:question_id>/results/', views.results, name='results'),
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

次に、polls/index.htmlテンプレートを次の場所から変更します。

polls / templates / polls / index.html

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

名前空間の詳細ビューを指すには:

polls / templates / polls / index.html

<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

ビューの記述に慣れたら、このチュートリアルのパート4 を読んで、フォーム処理と汎用ビューの基本を学習してください。