---
title: "はじめての Django アプリ作成、その 3"
version: 4.0
locale: ja
source: https://docs.djangoproject.com/ja/4.0/intro/tutorial03/
canonical: https://djangodocs.dev/ja/4.0/intro/tutorial03/
---
# はじめての Django アプリ作成、その 3

This tutorial begins where [Tutorial 2](/ja/4.0/intro/tutorial02/) left off. We're
continuing the web-poll application and will focus on creating the public
interface -- "views."

> **困ったときは:**
>
> このチュートリアルの実行に問題がある場合は、FAQ の [Getting Help](/ja/4.0/faq/help/) セクションに進んでください。

## オーバービュー

A view is a "type" of web page in your Django application that generally serves
a specific function and has a specific template. For example, in a blog
application, you might have the following views:

- Blog ホームページ - 最新エントリーをいくつか表示
- エントリー詳細ページ - 1エントリーへのパーマリンク (permalink) ページ
- 年ごとのアーカイブページ - 指定された年のエントリーの月を全て表示
- 月ごとのアーカイブページ - 指定された月のエントリーの日をすべて表示
- 日ごとのアーカイブページ - 指定された日の全てのエントリーを表示
- コメント投稿 - エントリーに対するコメントの投稿を受付

投票アプリケーションでは、以下4つのビューを作成します:

- 質問 "インデックス" ページ -- 最新の質問をいくつか表示
- 質問 "詳細" ページ -- 結果を表示せず、質問テキストと投票フォームを表示
- 質問 "結果" ページ -- 特定の質問の結果を表示
- 投票ページ -- 特定の質問の選択を投票として受付

Django では、ウェブページとコンテンツはビューによって提供されます。各ビューは単純に 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 は「URLconf」と呼ばれているものを使います。URLconf はURLパターンをビューにマッピングします。

このチュートリアルでは URLconf の基本的な使い方を知ってもらいます。より詳しくは [URL ディスパッチャ](/ja/4.0/topics/http/urls/) を参照してください。

## もっとビューを書いてみる

それではもう少しviewを `polls/views.py` に追加していきましょう。これから追加するviewでは引数をとります。

*`polls/views.py`*

```python
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()`](/ja/4.0/ref/urls/#django.urls.path) コールを追加して、新しいviewを `polls.urls` モジュールと結びつけます。

*`polls/urls.py`*

```python
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/" も試してください。結果と投票ページのプレースホルダがそれぞれ表示されます。

誰かがWebサイトの 「/polls/34/」 をリクエストすると、 Django は [`ROOT_URLCONF`](/ja/4.0/ref/settings/#std-setting-ROOT_URLCONF) に指定されている Python モジュール `mysite.urls` をロードします。そのモジュール内の `urlpatterns` という変数を探し、順番にパターンを検査していきます。 `polls/` にマッチした箇所を見つけた後、一致した文字列 (`"polls/"`) を取り除き、残りの文字列である "34/" を次の処理のために 『polls.urls』 の URLconf に渡します。これは `'<int：question_id>/'` に一致し、結果として下記のように `detail()` が呼び出されます。

```
detail(request=<HttpRequest object>, question_id=34)
```

The `question_id=34` part comes from `<int:question_id>`. Using angle
brackets "captures" part of the URL and sends it as a keyword argument to the
view function. The `question_id` part of the string defines the name that
will be used to identify the matched pattern, and the `int` part is a
converter that determines what patterns should match this part of the URL path.
The colon (`:`) separates the converter and pattern name.

## 実際に動作するビューを書く

各ビューには二つの役割があります: 一つはリクエストされたページのコンテンツを含む [`HttpResponse`](/ja/4.0/ref/request-response/#django.http.HttpResponse) オブジェクトを返すこと、もう一つは [`Http404`](/ja/4.0/topics/http/views/#django.http.Http404) のような例外の送出です。それ以外の処理はユーザ次第です。

ビューはデータベースからレコードを読みだしても、読み出さなくてもかまいません。 Django のテンプレートシステム、あるいはサードパーティの Python テンプ レートシステムを使ってもよいですし、使わなくてもかまいません。 PDF ファイルを生成しても、 XML を出力しても、 ZIP ファイルをその場で生成してもかまいません。 Python ライブラリを使ってやりたいことを何でも実現できます。

Django にとって必要なのは [`HttpResponse`](/ja/4.0/ref/request-response/#django.http.HttpResponse) か、あるいは例外です。

簡単のため、 [チュートリアルその 2](/ja/4.0/intro/tutorial02/) で解説した Django のデータベース API を使ってみましょう。試しに次のような `index()` ビューを作ります。これは、システム上にある最新の 5 件の質問項目をカンマで区切り、日付順に表示するビューです:

*`polls/views.py`*

```python
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`](/ja/4.0/ref/settings/#std-setting-TEMPLATES) には、Django がどのようにテンプレートをロードしレンダリングするかが書かれています。デフォルトの設定ファイルでは、 `DjangoTemplates` バックエンドが設定されており、その [`APP_DIRS`](/ja/4.0/ref/settings/#std-setting-TEMPLATES-APP_DIRS) のオプションが `True` になっています。規約により、 `DjangoTemplates` は [`INSTALLED_APPS`](/ja/4.0/ref/settings/#std-setting-INSTALLED_APPS) のそれぞれの "templates" サブディレクトリを検索します。

先ほど作成した `templates` ディレクトリ内で `polls` というディレクトリを作成し、さらにその中に `index.html` というファイルを作成してください。つまり、テンプレートは `polls/templates/polls/index.html` に書く必要があります。 `app_directories` テンプレートローダは前述のように動くため、Django 内でこのテンプレートを単に `polls/index.html` のように参照できます。

> **テンプレートの名前空間**
>
> 作ったテンプレートを (`polls` という別のサブディレクトリを作らずに) 直接 `polls/templates` の中に置いてもいいのではないか、と思うかもしれませんね。しかし、それは実際には悪い考えです。Django は、名前がマッチした最初のテンプレートを使用するので、もし *異なる* アプリケーションの中に同じ名前のテンプレートがあった場合、Django はそれらを区別することができません。そのため、Django に正しいテンプレートを教えてあげる必要がありますが、一番簡単な方法は、それらに *名前空間を与える*  ことです。アプリケーションと同じ名前をつけた *もう一つの* ディレクトリの中にテンプレートを置いたのは、そういうわけなのです。

テンプレートには次のコードを書きます:

*`polls/templates/polls/index.html`*

```html+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 %}
```

> **Note**
>
> チュートリアルを短くするために、すべてのテンプレートの例では不完全なHTMLを使用しています。独自のプロジェクトでは [完全な HTML ドキュメント](https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/Getting_started#anatomy_of_an_html_document) を使用する必要があります。

このテンプレートを使用するために `polls/views.py` の `index` ビューを更新してみましょう:

*`polls/views.py`*

```python
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/" を開くと、箇条書きのリストが表示されるはずです（リストには、  [チュートリアルその2](/ja/4.0/intro/tutorial02/) で作った "What's up" という質問が入っていますね）。リンクは質問の詳細ページを指します。

### ショートカット: [`render()`](/ja/4.0/topics/http/shortcuts/#django.shortcuts.render)

テンプレートをロードしてコンテキストに値を入れ、テンプレートをレンダリングした結果を [`HttpResponse`](/ja/4.0/ref/request-response/#django.http.HttpResponse)  オブジェクトで返す、というイディオムは非常によく使われます。 Django はこのためのショートカットを提供します。これを使って `index()` ビューを書き換えてみましょう:

*`polls/views.py`*

```python
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)
```

全部の view をこのように書き換えてしまえば、 [`loader`](/ja/4.0/topics/templates/#module-django.template.loader) や [`HttpResponse`](/ja/4.0/ref/request-response/#django.http.HttpResponse) を import する必要はなくなります (`detail` 、 `results` 、 `vote` を引き続きスタブメソッドにするなら、 `HttpResponse` はそのままにしておいたほうがいいでしょう)。

[`render()`](/ja/4.0/topics/http/shortcuts/#django.shortcuts.render) 関数は、第1引数として request オブジェクトを、第2引数としてテンプレート名を、第3引数（任意）として辞書を受け取ります。この関数はテンプレートを指定のコンテキストでレンダリングし、その [`HttpResponse`](/ja/4.0/ref/request-response/#django.http.HttpResponse) オブジェクトを返します。

## 404 エラーの送出

さて、質問詳細ビューに取り組みましょう。このページは、指定された投票の質問文を表示するものです。ビューは次のようになります:

*`polls/views.py`*

```python
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`](/ja/4.0/topics/http/views/#django.http.Http404) を送出します。

`polls/detail.html` テンプレートに何を書けばよいかは後で解説しますが、さしあたって上の例題を動かしたければ、単に:

*`polls/templates/polls/detail.html`*

```html+django
{{ question }}
```

と書いておいてください。

### ショートカット: [`get_object_or_404()`](/ja/4.0/topics/http/shortcuts/#django.shortcuts.get_object_or_404)

[`get()`](/ja/4.0/ref/models/querysets/#django.db.models.query.QuerySet.get) を実行し、オブジェクトが存在しない場合には [`Http404`](/ja/4.0/topics/http/views/#django.http.Http404) を送出することは非常によく使われるイディオムです。 Django はこのためのショートカットを提供しています。ショートカットを使って、 `detail()` ビューを書き換えてみましょう:

*`polls/views.py`*

```python
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()`](/ja/4.0/topics/http/shortcuts/#django.shortcuts.get_object_or_404) 関数は、Django モデルを第一引数に、任意の数のキーワード引数を取り、モデルのマネージャの [`get()`](/ja/4.0/ref/models/querysets/#django.db.models.query.QuerySet.get) 関数に渡します。オブジェクトが存在しない場合は [`Http404`](/ja/4.0/topics/http/views/#django.http.Http404) を発生させます。

> **設計思想**
>
> なぜ [`ObjectDoesNotExist`](/ja/4.0/ref/exceptions/#django.core.exceptions.ObjectDoesNotExist) 例外を高水準で自動的にキャッチせず、ヘルパー関数 [`get_object_or_404()`](/ja/4.0/topics/http/shortcuts/#django.shortcuts.get_object_or_404) を使うのでしょうか、また、なぜモデル API に [`ObjectDoesNotExist`](/ja/4.0/ref/exceptions/#django.core.exceptions.ObjectDoesNotExist) ではなく、 [`Http404`](/ja/4.0/topics/http/views/#django.http.Http404) を送出させるのでしょうか?
>
> 答えは、モデルレイヤとビューレイヤをカップリングしてしまうからです。 Django の最も大きな目標の一つは、ルーズカップリングの維持にあります。いくつかの制御カップリングは、 [`django.shortcuts`](/ja/4.0/topics/http/shortcuts/#module-django.shortcuts) モジュールの中にあります。

[`get_list_or_404()`](/ja/4.0/topics/http/shortcuts/#django.shortcuts.get_list_or_404) という関数もあります。この関数は  [`get_object_or_404()`](/ja/4.0/topics/http/shortcuts/#django.shortcuts.get_object_or_404) と同じように動きますが、 [`get()`](/ja/4.0/ref/models/querysets/#django.db.models.query.QuerySet.get) ではなく、  [`filter()`](/ja/4.0/ref/models/querysets/#django.db.models.query.QuerySet.filter) を使います。リストが空の場合は [`Http404`](/ja/4.0/topics/http/views/#django.http.Http404) を送出します。

## テンプレートシステムを使う

投票アプリの `detail()` ビューに戻りましょう。コンテキスト変数を  `question` とすると、 `polls/detail.html` テンプレートは次のようになります:

*`polls/templates/polls/detail.html`*

```html+django
<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 %}`](/ja/4.0/ref/templates/builtins/#std-templatetag-for) ループの中で行われています。 `question.choice_set.all` は、 Python コードの `question.choice_set.all()` と解釈されます。その結果、`Choice` オブジェクトからなるイテレーション可能オブジェクトを返し、 [`{% for %}`](/ja/4.0/ref/templates/builtins/#std-templatetag-for) タグで使えるようになります。

テンプレートの詳しい動作は  [テンプレートガイド](/ja/4.0/topics/templates/) を参照してください。

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

`polls/index.html` テンプレートで質問へのリンクを書いたとき、リンクの一部は次のようにハードコードされていました:

```html+django
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
```

このハードコードされた、密結合のアプローチの問題は、プロジェクトにテンプレートが多数ある場合、URLの変更が困難になってしまうことです。しかし、 `polls.urls` モジュール の [`path()`](/ja/4.0/ref/urls/#django.urls.path) 関数で name 引数を定義したので、テンプレートタグの `{％url％}` を使用して、URL 設定で定義されている特定の URL パスへの依存をなくすことができます:

```html+django
<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 %}` テンプレートタグを使ったとき、 Django はどのアプリのビューに対して url を作成すればいいでしょうか？ これを Django にどう知らせればいいでしょうか。

答えは、 URLconf に名前空間を追加すること、です。どうぞ `polls/urls.py` ファイル内で `app_name` を追加し、アプリケーションの名前空間を設定してください。

*`polls/urls.py`*

```python
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`*

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

以下の形にし、名前空間つきの詳細ビューを指すようにします:

*`polls/templates/polls/index.html`*

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

ビューを書けるようになったら、 [チュートリアルその4](/ja/4.0/intro/tutorial04/)  に進んで、簡単なフォームの処理と汎用ビューについて学びましょう。
