公開インターフェイスビューの作成

DjangoDjangoBeginner
今すぐ練習

💡 このチュートリアルは英語版からAIによって翻訳されています。原文を確認するには、 ここをクリックしてください

はじめに

このチュートリアルは、データベースのセットアップが終わったところから始まります。私たちはウェブ投票アプリケーションを続け、公開インターフェイス(「ビュー」)の作成に焦点を当てます。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL django(("Django")) -.-> django/CoreConfigurationandRoutingGroup(["Core Configuration and Routing"]) django(("Django")) -.-> django/DatabaseModelsandMigrationsGroup(["Database, Models, and Migrations"]) django(("Django")) -.-> django/UserInterfaceandInteractionGroup(["User Interface and Interaction"]) django/CoreConfigurationandRoutingGroup -.-> django/django_urls("Django Urls") django/CoreConfigurationandRoutingGroup -.-> django/django_exceptions("Django Exceptions") django/DatabaseModelsandMigrationsGroup -.-> django/models("Models") django/DatabaseModelsandMigrationsGroup -.-> django/databases("Databases") django/DatabaseModelsandMigrationsGroup -.-> django/request_and_response("Request and Response") django/DatabaseModelsandMigrationsGroup -.-> django/schemaeditor("SchemaEditor") django/UserInterfaceandInteractionGroup -.-> django/templates("Templates") subgraph Lab Skills django/django_urls -.-> lab-153743{{"公開インターフェイスビューの作成"}} django/django_exceptions -.-> lab-153743{{"公開インターフェイスビューの作成"}} django/models -.-> lab-153743{{"公開インターフェイスビューの作成"}} django/databases -.-> lab-153743{{"公開インターフェイスビューの作成"}} django/request_and_response -.-> lab-153743{{"公開インターフェイスビューの作成"}} django/schemaeditor -.-> lab-153743{{"公開インターフェイスビューの作成"}} django/templates -.-> lab-153743{{"公開インターフェイスビューの作成"}} end

概要

ビューは、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/ も試してみてください。これらは、仮の結果と投票ページを表示します。

Django URL routing diagram

誰かがあなたのウェブサイトからページを要求するとき、たとえば /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.pyindex ビューを更新して、テンプレートを使用するようにしましょう。

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 polls index page

ショートカット: ~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 をインポートする必要はもうありません(detailresultsvote のスタブメソッドがまだある場合は、HttpResponse を残しておく必要があります)。

~django.shortcuts.render 関数は、最初の引数に要求オブジェクト、2番目の引数にテンプレート名、オプションの3番目の引数に辞書を取ります。与えられたコンテキストでレンダリングされた与えられたテンプレートの ~django.http.HttpResponse オブジェクトを返します。

404エラーを発生させる

では、質問の詳細ビューを取り扱いましょう。このビューは、特定の投票に対する質問文を表示するページです。以下がそのビューです。

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> タグで使用するのに適しています。

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

覚えていますか?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"),

URL名の名前空間化

チュートリアルプロジェクトには 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>
URL名前空間化の例

ビューの作成に慣れたら、フォーム処理とコードの削減 を読んで、フォーム処理と汎用ビューの基本を学びましょう。

まとめ

おめでとうございます!あなたは「公開インターフェイスビューの作成」実験を完了しました。あなたの技術を向上させるために、LabExでさらに多くの実験を行って練習することができます。