【Django入門3】ビュー(View)の基本とURLのマッピング

Djangoのビューの機能についての解説ページアイキャッチ

このページにはプロモーションが含まれています

このページでは、Django におけるビュー(View)について解説していきます。

ここまで、Django・フレームワーク自体についての解説を下記ページで、

【Django入門1】Djangoとは?

また、Django で開発するウェブアプリのファイル構成や動作の仕組みについて下記ページで解説を行なってきました。

Djangoの全体像とDjangoのウェブアプリが動作する仕組みについての解説ページアイキャッチ 【Django入門2】Djangoの全体像・ファイル構成・動作の仕組み

ここまでは、主に Django の全体を理解していただくための解説をしてきましたが、ここからは Django における個別の機能や構成についての解説を解説を行なっていきます。

そして、今回解説するのがビューとなります。

Django で開発するウェブアプリの基本構造は MTV モデルとなっています。そして、この V の部分がビューとなります。このように、ビューは Django で開発するウェブアプリの基本構造の1つであり、ウェブアプリにおいて重要な存在となります。

MTVモデルにおけるViewの位置付けを示す図

また、ビューを上手く使いこなすためには URL とビューとのマッピング(関連付け)が重要となります。そのため、このページでは URL とビューとのマッピングについても解説していきます。

このページでは、特にビューの基本についてのみ解説を行いますが、ビューの基本をしっかり理解しておくことで、今後説明するテンプレートやフォーム・モデル等の使い方も理解しやすくなるため、是非このページで解説する内容は理解しておいてください。特に Django 初心者の方向けに詳しく・分かりやすく解説していきたいと思います。

ビューの基本

では、ビューについて解説していきます。

まず、Django におけるビューとは、一言で簡単に言えば「ウェブアプリの機能」となります。

ビューはウェブアプリの機能

ウェブアプリとは、一言で言えばリクエストを受け取り、そのリクエストに応じた機能を実行し、その機能から得られた結果に応じたレスポンスを返却するアプリになります。

ウェブアプリがリクエストに応じた処理とレスポンスの返却を行うことを示す図

これは下記ページでも解説していますが、ウェブアプリは Django フレームワークとプロジェクトから構成されます。さらに、プロジェクトの中には複数のアプリが存在します。

Djangoの全体像とDjangoのウェブアプリが動作する仕組みについての解説ページアイキャッチ 【Django入門2】Djangoの全体像・ファイル構成・動作の仕組み

このアプリが Django フレームワークから実行されることでウェブアプリの機能が動作することになります。より具体的には、Django フレームワークがリクエストを受け取り、そのリクエストに応じて Django フレームワークがアプリの機能を実行することになります。この Django フレームワークから実行される機能を提供するのが「ビュー」となります。さらに、この機能はリクエストを受け取り、そのリクエストに応じたレスポンスを Django フレームワークに返却します。

ViewがDjangoフレームワークとリクエストとレスポンスのやりとりを行う様子

ビューが提供する機能から Django フレームワークにレスポンスを返却すれば、Django フレームワークを経由してウェブサーバーにレスポンスが返却され、さらにウェブサーバーからクライアントにレスポンスが返却されることになります。そして、そのレスポンスを受け取ったクライアントが、そのレスポンスに応じたページの表示等を行うことになります。

このように、ウェブアプリの機能はビューによって提供され、Django フレームワークはクライアントからのリクエストに応じた機能を実行するためにビューを利用します。つまり、ビューとはウェブアプリの機能そのもの、もしくはウェブアプリの機能の集まりと考えられます。実際には、ビューでは機能を実現するための関数やクラスを定義し、それを Django フレームワークから実行してもらうことになります。

ビューが機能そのものであるため、極論すれば、ウェブアプリを開発するためにはビューさえ作成してやれば良いことになります。モデルやテンプレートなど不要なのです。なんですが、ビューだけで機能を実現すると、ビューが複雑になりメンテナンス性が下がります。そのため、Django では、ビューだけで機能を実現するのではなく、モデル、テンプレートとで役割分け、ビューからそれらを有効に利用して機能を実現することが推奨されています。そして、これが MTV になります。

モデル・テンプレートに関しては、この Django 入門 の連載の中の下記ページで解説していきますが、このページでは、ひとまずビューだけでウェブアプリの機能を実現していきたいと思います。このページを読めば、ビューさえあればウェブアプリの機能が一応開発できることは理解していただけるのではないかと思います。

モデルの解説ページアイキャッチ 【Django入門6】モデルの基本 Djangoのテンプレートの解説ページアイキャッチ 【Django入門4】テンプレート(Template)の基本

また、ビューから提供する機能は Django フレームワークから実行されるという点が、ビューの実装においてのポイントになります。Django フレームワークから実行されるため、ビューの機能は Django フレームワークが指定する型のデータを引数で受け取り、さらに Django フレームワークが期待する型のデータを返却する必要があります。要は、Django フレームワークが期待するように動作する機能である必要があるということになります。

このあたりも含めて、次はビューの実装についての解説を行っていきたいと思います。

MEMO

前述でも少し触れましたが、ビューから提供する機能は関数やクラスによって実装することになります

このページでは、この機能を関数で実装することを前提に解説を進めていきます

このように、関数で実装したビューのことを “関数ベースビュー” と呼びます。関数ではなくクラスを定義するようにすることでビューをより簡潔に実装することができるようになるのですが、それについては Django 入門 の連載の下記ページで別途解説していきます(クラスで実装したビューのことは “クラスベースビュー” と呼ばれます)

クラスベースビューの解説ページアイキャッチ 【Django入門15】クラスベースビューの基本

スポンサーリンク

ビューの実装先は views.py

このビューの実装先はプロジェクト内の アプリフォルダ に用意される views.py となります。アプリフォルダ が複数存在するのであれば、各 アプリフォルダ の views.py を実装していく必要があります。前述の通り、基本的に views.py に実装するのは Django フレームワークから実行される関数であり、それらの関数ではウェブアプリの機能を実装することになります。

ビューの実装先がアプリのviews.pyであることを示す図

下記ページでも解説していますが、views.py は startapp コマンドを実行することで自動的に生成されることになります。

Djangoの全体像とDjangoのウェブアプリが動作する仕組みについての解説ページアイキャッチ 【Django入門2】Djangoの全体像・ファイル構成・動作の仕組み

views.py を生成し、ビューを実装していく具体例は 掲示板アプリでビューを利用してみる の章で紹介していきたいと思います。

役割1:リクエストを受け取りレスポンスを返却する

この views.py で定義する関数は Django フレームワークから実行されるという点が1つのポイントになります。Django フレームワークから実行されるため、Django フレームワークが期待するように動作するよう関数を実装していく必要があります。そのために、views.py で定義する関数は最低限下記を満たす必要があります。

  • 引数で “リクエスト” を受け取ること
  • 正常終了時には返却値で “レスポンス” を返却すること
  • 異常が発生したときには例外を発生させること

要は、ビューの関数は、Django フレームワークから引数としてリクエストを受け取り、さらに Django フレームワークにレスポンスを return するように実装する必要があります(もしくは、エラーや問題が発生した際に例外を発生させる必要があります)。

views.pyで定義する関数の引数と返却値の説明図

具体的には、この引数として受け取るリクエストとは HttpRequest のサブクラス のインスタンスであり、return するレスポンスとは HttpResponse のサブクラス のインスタンスとなります。これらの HttpRequestHttpResponse は Django フレームワーク手定義されるクラスになります。

MEMO

この サブクラス には、その サブクラス のスーパークラス(親クラス)も含まれます

つまり、下記のように関数を定義すれば、最低限ですがビューの関数としては成立することになります。Django フレームワークからリクエストを引数 request で受け取り、HttpResponse のインスタンスを返却しているため(HttpResponse() でインスタンスが生成される)、この index 関数は Django フレームワークから実行可能なビューの関数として成立しています。

最低限のビュー
from django.http.response import HttpResponse

def index(request):
    return HttpResponse('Hello World!')

この index 関数から返却されたレスポンスは、Django フレームワークを介してウェブサーバーからクライアントに返却されます。このウェブサーバーからクライアントに返却されるレスポンスは下記のようなデータとなります。

HTTP/1.1 200 OK
Date: Mon, 06 Feb 2023 20:45:16 GMT
〜略〜
Cross-Origin-Opener-Policy: same-origin

Hello World!

1行目がレスポンスのステータスラインとなり、ここにリクエストに対する結果が記述されています。200 という数字は、リクエストに対する処理が正常終了したことを示すステータスコードとなります。この行は HttpResponse() によってインスタンスを生成する際に自動的に設定されますし、HttpResponse() の引数指定によってステータスコード等を変更することも可能です。

MEMO

このページでは詳細な説明は行いませんが、ビューで例外を発生させた際には、その例外の内容に従って Django フレームワークが自動的にエラーとなるステータスコードやボディを設定してレスポンスするようになっています

また、”2行目 〜 最後の行の前の空白行” はヘッダーと呼ばれる部分で、ここにレスポンスの詳細が記述されています。ヘッダーに関しては、HttpResponse() によってインスタンスを生成した際や、ビューから Django フレームワークに返却した後に情報が記述されていくことになります。

これらのステータスラインやヘッダーの情報は、HttpResponse のインスタンスを生成した際や Django フレームワークに返却した後に、ある程度自動的に設定されることになります。

それに対し、ヘッダーの後ろの空白行の後はボディであり、ボディに関しては HttpResponse() で引数で指定した文字列がそのまま設定されることになります。上記の例では 'Hello World!' を指定しているためボディが単なる文字列になっていますが、本来であれば、このボディには HTML が設定されるように HttpResponse のインスタンスを生成する必要があります。

Django の場合、この HTML の設定はテンプレートの仕組みを利用して行うことになります。このテンプレートについては下記ページの次回の連載で解説を行います。ひとまず、このページでは単なる文字列をボディとしてレスポンスするようにしていきたいと思います。

Djangoのテンプレートの解説ページアイキャッチ 【Django入門4】テンプレート(Template)の基本

役割2:リクエストに応じた処理を行う

さて、先ほど示した index 関数は、ビューの関数として必要最低限な条件は満たしており、一応ビューの関数として完成していることになります。

ただ、このビューは Django フレームワークから実行可能となる最低限の条件を満たしているだけであり、ウェブアプリのビューとしてはまだ不十分です。なぜなら、受け取ったリクエストを無視しているからです。具体的には、先ほど示した index 関数はリクエストを引数で受け取っているものの、その引数を無視して単にボディが Hello World! となるレスポンスを返却しています。つまり、受け取ったリクエストを無視し、リクエストに応じた処理が行われていません。

ビューの関数がリクエストを無視する様子

ビューの関数が受け取るリクエストは、クライアントが送信してきたリクエストに基づいて生成されたデータであり、このデータにはクライアントの要求が含まれています。そのため、このリクエストに応えることがウェブアプリには必要となります。なので、本来、ビューの関数は Django フレームワークから受け取ったリクエストに応じた処理を行う必要があります。そして、その処理結果に応じたレスポンスを返却する必要があります。

ビューの関数がリクエストに応じた処理を行い、リクエストに応じたレスポンスを返却する様子

では、リクエストに応じた処理やレスポンスを実現するためにはどうすればよいでしょうか?

この点について、そもそもリクエストとは何なのか?という点を踏まえて説明していきたいと思います。

リクエストのデータ構造

まずはクライアントからウェブサーバーに送信されるリクエストのデータの中身を確認してみましょう!

リクエストのデータの中身の構造は下記のようになっています。

GET /index/ HTTP/1.1
Host: localhost:8000
〜略〜

1行目がリクエストラインとなります。このリクエストラインにリクエストの重要な情報が記載されており、その情報の1つ目が URL2つ目がメソッドとなります。上記においては、/index/ が URL(ルートパス形式)であり、GET がメソッドとなります。

リクエストラインにおけるメソッドとURLの説明図

クライアントから送信されてきたリクエストの内容の大部分は、この URL とメソッドにより決まります。具体的には、クライアントが利用したい機能が、これらの URL とメソッドによって指定されることになります。したがって、クライアントが利用したい機能に対する処理を実行されるように、ウェブアプリはリクエストの URL とメソッドに応じて実行する機能を切り替えることが必要となります。

URLとメソッドによって実行される機能が切り替わる様子

このような、URL とメソッドに応じた機能の実行は、Django で開発するウェブアプリの場合は「URL に応じた関数の実行」と「メソッドに応じた機能の実行」の2段階の処理によって実現されます。ここからは、これらについて説明していきます。

URL に応じた関数を実行する

まず、リクエストの URL に応じた関数の実行について説明していきます。

実は、Django フレームワークには、URL に応じて実行する関数を振り分ける仕組みが存在します。なので、この仕組みを利用すれば、URL に応じて実行する関数を切り替えることが実現できます。

ただし、この仕組みを利用するためには、ウェブアプリが受け付ける URL と、その URL のリクエストを受け取ったときに実行する関数の対応付けの設定を行っておく必要があります。これが、このページの題名にもある “URL マッピング” であり、この設定は urls.py というファイルで行うことになります。これに関しては、後述の ビューと URL とのマッピング で詳細を解説します。

このリクエストの URL に応じた関数の切り替えを実現するためのビューの役割は、各 URL に対応した関数を提供することになります。つまり、views.py には、ウェブアプリが受け付けるリクエストの各 URL に対応する関数を実装しておく必要あります。各関数で URL に応じた機能を実行するようにすれば、urls.pyviews.py、さらに Django フレームワークが連携して動作することで、リクエストの URL に応じた関数の切り替えが実現できることになります。

Djangoフレームワークによって、URLに対応した関数が実行される様子

メソッドに応じた機能を実行する

続いて、メソッドに応じた機能の実行について解説していきます。

このメソッドは、リクエストの目的を示すデータになります。多くの種類のメソッドが存在するのですが、まず覚えておくと良いのが GETPOST になります。

GET はリクエストの目的がデータの取得であることを示すメソッドになります。ウェブページ表示時のリクエストは、ウェブページを表示するためのデータの取得を行うことが目的となりますので、この際のリクエストのメソッドとしては GET が指定されます(実際にリクエストを受け取るのはウェブサーバー等になりますが、図ではそれらのウェブサーバーを省略しています)。

メソッドGETの説明図

それに対し、POST はリクエストの目的がデータの送信であることを示すメソッドになります。より厳密に言えば、”データの新規作成” を示すメソッドになりますが、まずはデータの送信くらいで曖昧に捉えておくので良いと思います。例えば掲示板などでコメントを投稿したい場合、リクエストの目的は “コメントの送信” となりますので、この際のリクエストのメソッドとしては POST が指定されます。

他にもユーザー登録やログイン等を行う際には、ユーザー名やパスワードなどをウェブアプリに送信することになるため、この際にも POST が指定されることになります。

メソッドPOSTの説明図

POST メソッドによって送信されてきたデータをどう扱うかはウェブアプリや URL によって異なりますが、いずれにせよウェブアプリ側では送信されてきたデータを利用して処理を行うことが必要となります。

このように、リクエストではメソッドが指定されるようになっており、メソッドよって “利用したい機能” が異なることになります。例えば、先ほど示した図のように、URL が同じ /signup/ であったとしても、メソッドが GET であれば “ユーザー登録用のページの取得” がクライアントの利用したい機能ということになりますし、メソッドが POST であれば “ユーザー登録用のユーザーの情報の送信” がクライアントの利用したい機能ということになります。

こんな感じで、メソッドによってユーザーの利用したい機能が異なることになるため、ウェブアプリでは、メソッドに応じて実行する機能を切り替えることも必要となります。

そして、ビューを関数で実装する場合、このメソッドに応じて実行される機能を切り替える役割を持つのはビューとなります。

views.pyの関数がリクエストのメソッドに応じて処理を切り替える様子

ただ、この実現方法は簡単です。ビューの関数が引数として受け取る HttpRequest のサブクラス のインスタンスには、データ属性 method に “メソッドの種類” が文字列としてセットされています。したがって、下記のように if 文でメソッドの種類に応じて実行する機能を分岐するようにしてやれば、リクエストのメソッドに応じた機能の切り替えが実現できることなります(下記ではメソッドが POST の場合とそれ以外の場合とで分岐するようにしています)。

メソッドに応じた機能の分岐
def signup(request):
    if request.method == 'POST':
        # メソッドがPOSTの場合の機能を実装

    else:
        # メソッドがPOST以外(GET)の場合の機能を実装

このようにデータ属性 method に応じて実行する機能を分岐するようにしてやれば、メソッドに応じてウェブアプリで実行される機能が切り替わるようになります。さらに URL に応じた関数を実行する で説明したように、各 URL に対応する関数を用意し、urls.py で URL と関数の対応付けの設定を行っておけば、リクエストに応じた機能(処理)の実行、すなわち URL とメソッドに応じた機能の実行が実現できることになります。

URLとメソッドに応じた機能が実行される様子

こんな感じで、リクエストに応じた機能が実行されるよう、urls.pyviews.py を作成することがウェブアプリを開発する上でのポイントになります。

ただし、全てのビューの関数でメソッドに応じた処理の分岐が必要というわけではありません。例えば表示することのみが目的となる URL に対応するビューの関数においては、メソッドが GET であることを前提に実装をしてやれば良いです。

それに対し、フォームを表示するようなページの場合、フォームを表示する際には GET メソッドで、さらにフォームに入力したデータを送信する際には GET メソッドでクライアントからリクエストが送信されてくることになるため、メソッドが GET メソッドだけでなく POST である場合も考慮してビューの関数を実装しておく必要があります。

メソッドの説明図1

このフォームに関しては今後の連載における下記ページで解説を行ないますので、詳細に関しては下記ページを参照していただければと思います。

Djangoのフォームの解説ページアイキャッチ 【Django入門5】フォームの基本

リクエストに応じた機能を実装する

ここまで、説明してきたように、ウェブアプリではリクエストされた機能を実行することが重要です。そして、これは URL およびメソッドに応じて実行する機能を切り替えることで実現できます。

このことを理解していただければ、リクエストに応じた機能の実行は実現できると思います。

この、リクエストに応じた機能が実行されることも重要なのですが、もっと重要になるのが “機能自体” の作り込みになります。

この機能は開発するウェブアプリによって異なるため、どんな関数を実装して機能を実現するのかは一概には言えません。ですが、ウェブアプリにとって機能はウェブアプリを特徴づけるものなので、この機能の実装が非常に重要になります。

この機能を実現する上でのポイントを1つ説明しておくと、それはやっぱり、リクエストに応じた処理を実行するように実装するという点になると思います。

ここまでリクエストに関しては URL とメソッドのみに注目してきましたが、リクエストには他にも様々なデータが含まれることになります。これらのデータに応じた処理を実行するようにすることで、リクエストに応じた処理が実現できるように機能を実装していくことが重要です。

例えば、先程 POST メソッドについて説明しましたが、POST メソッドでのリクエスト時にはデータが送信されてくることになり、このデータはビューの関数が受け取る HttpRequest のサブクラス のインスタンスのデータ属性 POST にセットされています。送信されてきたデータを無視すると、ユーザーからのリクエストを無視することになってしまいます。ユーザーからのリクエストに応えるためには、このデータ属性 POST にセットされている情報にも基づいて処理を行うことが必要となります。

一番多いのが、このデータ属性 POST にセットされているデータをデータベースに保存する処理になると思います。例えば、掲示板アプリの場合は、投稿するコメントが送信されてくるため、それをデータ属性 POST から取得してデータベースに保存するような処理が必要となります。

また、ログイン機能をウェブアプリに持たせた場合、ビューの関数が受け取る HttpRequest サブクラス のインスタンスのデータ属性 user にログイン中のユーザーの情報がセットされることになります。なので、例えばマイページなどを表示するリクエストを受け取った場合は、データ属性 user の情報をレスポンスとして返却したり、データ属性 user にセットされたユーザーの情報をデータベースから取得してレスポンスとして返却したりするような処理が必要となります。

このように、メソッドや URL に応じた機能が実行されることだけでなく、機能自体もリクエストに応じた処理を実行するようにビューを実装していく必要があります。具体的には、引数で受け取る HttpRequest のサブクラス のインスタンスにセットされている情報を適切に利用して処理を行うように実装することが必要となります。

リクエストの情報を利用して様々な機能を実現する様子

また、ビューの関数では HttpRequest のサブクラス のインスタンスだけでなく、他の引数も受け取ることが可能です。ビューの関数で HttpRequest のサブクラス のインスタンス以外のデータを引数として受け取る方法については ビューと URL とのマッピング の章で説明しますが、この引数でもクライアントからのリクエストに応じたデータがセットされることになるため、この引数も利用して処理を実行することが重要となります。

とにかく、ビューは「リクエストに応じた機能・処理を実行できるように作ること」が重要となるので、この点は是非覚えておいてください!

スポンサーリンク

役割3:モデルとテンプレートを適切に利用する

また、ビューの役割として重要なのが、モデルやテンプレートを適切に利用することになります。

ウェブアプリの機能ではデータベースの操作

ウェブアプリの機能には、リクエストを受け取った際にデータベースからデータを取得し、その情報を埋め込んだ HTML を生成してレスポンスとして返却するようなものが多いです。

例えば「掲示板の表示機能」は、掲示板に既に投稿されているコメントをデータベースから取得し、その取得したコメントを埋め込んだ HTML をボディとするレスポンスを返却することで実現できます(この投稿済みのコメントはあらかじめデータベースに保存しておく必要があります)。

GETメソッドでデータを取得する様子

また、ウェブアプリの機能は、リクエスト受け取った際にデータベースへのデータの保存を行うようなものも多いです。

例えば「コメント投稿機能」は、送信されてきたコメントをデータベースに保存し、保存の成功 or 失敗の結果を埋め込んだ HTML をボディとするレスポンスを返却することで実現できます。

データベースにPOSTメソッドで送信されてきたデータを保存する様子

このように、ウェブアプリの機能は、データベースの操作によって実現できるものが多いです。そして、レスポンスのボディは、データベースの操作結果(成功 or 失敗 or 取得したデータなど)を埋め込んだ HTML となるものが多いです。

これらのデータベースの操作やデータベースの操作結果を埋め込んだ HTML の生成に関しても、ビューだけで実現しようと思えば可能ではあります。ですが、これらもビューが行うことになるとビューの役割が多くなり、それに伴って views.py のソースコードの分量が多くなってメンテナンス性が下がることになります。

ビューの役割が多くなりすぎることを防ぐため、Django にはビューの他にモデルとテンプレートが存在しています。簡単に言えば、モデルはデータベースの操作を行うことを役割とし、テンプレートは HTML の雛形を提供することを役割としています。

モデルを利用すれば、ビューにはデータベースの操作の処理を実装する必要が無くなり、データベースの操作を行う際は、モデルに対して操作の依頼を行えば良いだけになります。つまり、モデルが提供するメソッドを実行すれば良いだけです。

ビューがモデルにデータベースの操作を依頼する様子

また、テンプレートを利用すれば、HTML の雛形がテンプレートから提供されることになるため、ビューはその雛形に対してデータベースの操作結果を埋め込んでやれば HTML を生成することができることになります。なので、HTML 全体を作成するような処理はビューには不要になります。さらに、このテンプレートファイルからの HTML の生成は、Django フレームワークの提供する render 関数により実現することができます。

テンプレートファイルの説明図

このように、ビューからモデルやテンプレートを利用することで、ビューの役割を減らし、これがビューへの実装量の削減につながります。そして、これによってビューのメンテナンス性が上がります。

ただし、これらを利用する場合、ビューはモデルやテンプレートを適切に利用することが必要となります。例えば、モデルの利用に関して言うと、モデルにデータベースの操作を依頼するメソッドの実行時には、適切な引数が指定されるようにビューを実装する必要があります。また、テンプレートの利用においては、テンプレートに埋め込むデータはビューが用意する必要がありますし、テンプレートからは複数のテンプレートファイルが提供されることになるため、データの埋め込み先として適切なテンプレートファイルが選択できるようにビューを実装する必要があります。

要は、クライアントから送信されてきたリクエストに応えられるように、適切にモデルやテンプレートを利用するようにビューの関数を実装する必要があります。

ViewからModelとTemplateを利用する様子

ここではビューの役割という観点で説明を行いましたが、ここで登場したモデルやテンプレートに関しては Django 入門 の連載の中で後ほど説明をします。ですので、モデルやテンプレートに関しては今後の連載の中で理解していただくとして、このページでは、まずはビューの役割を理解しておいていただければ良いと思います。

ビューに関してのまとめ

ここまでビューの基本について解説してきました。

説明が長くなりましたので、ここで簡単にビューについてまとめておきます。

ビューは「ウェブアプリの機能そのもの」もしくは「機能の集まり」です。ウェブサーバーが受け取ったリクエストは、Django フレームワークを介してビューが受け取ることになります。さらに、ビューが Django フレームワークに対してレスポンスを返却することで、ウェブサーバーからクライアントに対してレスポンスが返却されることになります。

ViewがDjangoフレームワークとリクエストとレスポンスのやりとりを行う様子

ビューの実装先は views.py であり、この views.py には関数やメソッドを実装する必要があります。Django フレームワークと上手くリクエストとレスポンスのやりとりを行うために、ビューの関数は最低限下記を行う必要があります。

  • 引数で “リクエスト” を受け取ること
  • 正常終了時には返却値で “レスポンス” を返却すること
  • 異常が発生したときには例外を発生させること

また、ビューはリクエストに応じた機能の実行とレスポンスの返却を行う必要があります。つまり、どんなリクエストを受け取ったとしても同じ処理を実行して同じレスポンスの返却を行うのではなく、リクエストに応じて実行する処理の内容や返却するレスポンスが切り替えられるようにしておく必要があります。

より具体的には、リクエストには URL とメソッドが指定されるため、その指定された URL とメソッドに応じた機能が実行されるように urls.py や views.py を実装する必要があります。

URLとメソッドによって実行される機能が切り替わる様子

urls.py に関しては、次の ビューと URL とのマッピング で詳しく解説していきます。

また、ビューでは機能を実現する上でデータベースの操作や HTML の生成を行う必要があります。これらは、モデルやテンプレートの利用によって実現することが可能であり、ビューはこれらのモデルやテンプレートを適切に利用してリクエストに応じた処理やレスポンスの返却が行えるように実装する必要があります。

ViewからModelとTemplateを利用する様子

以上が、ビューの基本のまとめになります。

ただ、ここまでの解説はあくまでもビューの基本的な説明であって、別に上記の実装に拘る必要はありません。

例えば、ビューは関数を用意するのではなくクラスを用意することで実現することもできます。そして、Django フレームワークに用意されているクラスを継承して作成することで、実装が楽になったりします。

このあたりについても、この Django 入門 の連載の後半の下記ページで解説していきます。まずは、関数でビューを作成して、ビューの役割等をしっかり理解していきましょう!

クラスベースビューの解説ページアイキャッチ 【Django入門15】クラスベースビューの基本

ビューと URL とのマッピング

ここからは、ビューと URL とのマッピングについて解説していきます。

ここまでは主に views.py の解説を行ってきましたが、ここからの主役は urls.py となります。

URL に応じた関数を実行する で説明したように、Django フレームワークは URL に応じて実行するビューの関数を切り替えるようになっています。そして、この関数の切り替えは urls.py で設定される “関数と URL とのマッピング” に基づいて行われることになります。

ここからは、このマッピングと urls.py について解説していきます。

スポンサーリンク

関数と URL とのマッピングの必要性

前述のとおり、ビューの関数(views.py の関数)の実行を行うのは Django フレームワークとなります。ウェブアプリがリクエストを受け取ると、Django フレームワークが自動的に views.py の関数を実行してくれます。

ViewがDjangoフレームワークとリクエストとレスポンスのやりとりを行う様子

ただし、受け取ったリクエストの URL に応じて実行する views.py の関数が適切に選択されるようにするためには、あらかじめ URL と views.py の関数とのマッピングを行ない、それを Django フレームワークに設定しておく必要があります。 

マッピングとは関連付けや紐付けなどの意味があり、URL のマッピングとは、下の図のように各 URL と views.py の関数を関連付ける設定のことを言います。

URLと関数とのマッピング設定

このようなマッピングを行なっておけば、Django フレームワークがリクエストを受け取った際、その URL にマッピングされた関数が実行されるようになります。

MEMO

Django では “URL” と関数とのマッピングだけでなく、”URL パターン” と関数とのマッピングを行うことも可能です

最初はマッピングを行うのが URL であることを前提に解説していきますが、後述で URL パターンと関数とのマッピングについても解説していきます

urls.py で URL と関数とをマッピングする

そして、この URL と関数とのマッピングを行うためのファイルが urls.py というファイルになります。

この urls.py は、Django のプロジェクトを作成する際に自動的にプロジェクト設定フォルダ内に生成されるファイルになります。Django フレームワークがリクエストを受け取った時には、この urls.py で設定されたマッピングに基づき、リクエストの URL に対応した関数を Django フレームワークが実行してくれます。

“プロジェクト” や “プロジェクト設定フォルダ” の意味合いに関しては、前回の連載で解説していますので、これらの意味合いを知りたい方は下記ページを読んでみてください。

Djangoの全体像とDjangoのウェブアプリが動作する仕組みについての解説ページアイキャッチ 【Django入門2】Djangoの全体像・ファイル構成・動作の仕組み

urls.py で行うことは urlpatterns というリストの定義となります。このリストの1つ1つの要素で URL と関数とのマッピング設定を指定していくことになります。このような urlpatterns を定義しておけば、各要素で指定された  URL  へのリクエストを Django フレームワークが受け取った際、その URL にマッピングされた関数が実行されるようになります。

したがって、ウェブアプリに指定可能な URL に対応する分だけ urlpatterns のリストの要素としてマッピング設定を指定しておけば、Django フレームワークがそれらの URL を受け付け、それに応じた関数の実行を行うことができるようになります。

URL と関数とのマッピング方法

続いては、この URL と関数とのマッピングの具体的方法について解説していきます。

urlpatterns の各要素にマッピングを指定する

前述の通り、urls.py はプロジェクト作成時にプロジェクト設定フォルダ内に自動的に生成されるファイルになります。自動生成後の urls.py は下記のようなものになります(最初の """""" で囲まれた部分はコメントなので省略しています)。

自動生成されたurls.py
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

上記のように、urls.py が自動生成された時点で urlpatterns が既に用意されており、この urlpatterns の各要素に対してマッピング設定を指定していくことになります。

既に urlpatterns の1つ目の要素として下記が指定されていますが、これもマッピング設定の1つであり、/admin/ から始まる URL のリクエストを受け取った際に管理画面のページを表示するためのマッピング設定となります。

adminのURLパターン
path('admin/', admin.site.urls)

ウェブアプリにおいて管理画面は便利な機能なので、特に理由がなければ上記の1つ目の要素はそのまま残しておくと良いと思います。

ということで、2つ目の要素以降に自身のウェブアプリで受け付ける URL に対してマッピング設定を指定していくことになります。基本的には、上記の1つ目の要素の指定の仕方と同様の方法でマッピング設定を指定することになります。つまり、2つ目の要素以降に path 関数を指定していくことになります。

マッピング設定を path 関数により生成する

この path 関数は、引数に指定されたパラメータに応じたマッピング設定を生成する関数になります(具体的には、path 関数の返却値は URLPatternURLResolver というクラスのインスタンスになるのですが、このページでは “マッピング設定” という言葉で説明をしていきます)。

そして、マッピング設定を生成するにあたって path 関数に最低限指定する必要のある引数は routeview になります。

path関数に最低限必要な引数
path(route, view)

route にはウェブアプリがリクエストを受け付ける URL を「ルートパス形式」で指定します(単なる URL だけでなく URL パターンを指定することも可能です。これについては後述で解説します)。ルートパス形式の URL とは、簡単に言えば URL における「ドメイン名よりも後ろ側の部分」になります。ただし、引数 route には最初の / を飛ばして指定する必要があります。例えば、下記の場合は app1/user/ を指定することになります。

route引数に指定するURLの例

さらに view には、route で指定された URL に対するリクエストを受け取った際に Django フレームワークに実行してほしい関数(関数オブジェクト)を指定します。

path引数に指定する関数の例

この routeview を指定して path 関数を実行すれば、引数に応じた URL と関数とを紐づけるためのマッピング設定が生成され、path 関数から返却されることになります。あとは、この返却値を urlpatterns に格納しておけば、Django フレームワークが urlpatterns の各要素に指定されたマッピング設定に基づき、URL に応じた関数の実行を行ってくれるようになります。

つまり、URL と関数とのマッピングは、マッピングしたい「URL」と「関数」を引数に指定した状態の path 関数を urlpatterns の各要素として記述しておくことで実現することができます。

urlpatternsへの要素の追加
from django.urls import path

urlpatterns = [
    path(route1, func1),
    path(route2, func2)
    path(route3, func3)
]

これにより、クライアントからリクエストされた URL に対するマッピング設定が urlpatterns 内に存在する場合、その URL にマッピングされた関数が実行されることになります。

クライアントからリクエストされた URL に対するマッピング設定が urlpatterns 内に存在しない場合は、関数を実行することなく Django フレームワークが Page not found エラー(404 エラー)を返却することになります。つまり、ウェブアプリに指定可能な URL は、マッピング設定を行なった URL のみとなります。

マッピングの実装例

例えば、/index/ という URL へのリクエストを受け取った際にビューの index 関数が、さらに /user/ という URL へのリクエストを受け取った際にビューの user 関数がそれぞれ実行されるようにしたい場合は、下記のようにurls.py を変更すれば良いことになります(コメント部分は省略します)。

urlpatternsへの要素の追加
from django.contrib import admin
from django.urls import path
from アプリ名 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index)
    path('user/', views.user)
]

ポイントは、ビューの関数はアプリの views.py で定義されるため、上記の3行目のように、その views.py をインポートしてから path 関数の引数でビューの関数を指定する必要があるという点になります。

アプリの urls.py に分離する

上記のように、プロジェクト設定フォルダの urls.py を書き換えてやることで URL と関数のマッピングを行うことは可能なのですが、実は上記のようにプロジェクト設定フォルダの urls.py のみで urlpatterns の全要素を指定するような書き方は推奨されていません。

実際には、アプリフォルダ内にも urls.py を用意し、各アプリで受け付ける URL に対するマッピング設定に関しては、そのアプリフォルダ内の urls.py で指定することが推奨されています。アプリフォルダ内の urls.py においても、今までの説明と同様に urlpatterns の各要素にマッピング設定(path 関数の返却値)を指定を行うことで各 URL に対する関数のマッピングを行います。

各アプリにurls.pyを用意する様子

ただし、Django フレームワークからはアプリフォルダ内の urls.py は直接読み込まれません。Django フレームワークが直接読み込む urls.py はプロジェクト設定フォルダ内のもののみになります。そのため、プロジェクト設定フォルダ内の urls.py が読み込まれた際に、プロジェクト設定フォルダ内の urls.py から各アプリフォルダ内の urls.py が読み込まれるようにしておく必要があります。

プロジェクト設定のurls.pyから各アプリのurls.pyを読み込む様子

このアプリの urls.py の読み込みは、path 関数の引数 view に対して include('アプリ名.urls') を指定することにより実現できます。

アプリのurls.pyの読み込み
path('アプリに対応するURL/', include('アプリ名.urls')),

例えば、プロジェクト内にアプリ名 app1 のアプリとアプリ名 app2 のアプリが存在する場合は、アプリ名.urls の部分にはそれぞれ app1.urlsapp2.urls を指定することになります。

上記のように引数を指定した path 関数をプロジェクト設定フォルダの urls.py で urlpatterns の要素に指定しておけば、プロジェクト設定フォルダ内の urls.py が Django フレームワークから読み込まれる際、アプリ名.urls、すなわち アプリ名 フォルダ内の urls.py も読み込まれるようになります。

さらに、リクエスト先の URL が /アプリに対応するURL/ から始まる場合、読み込んだ アプリ名.urlsurlpatterns に指定されたマッピング設定に基づいて実行される関数が振り分けられるようになります。/アプリに対応するURL/ の部分は、分かりやすい名前であれば自由に決めて問題ありません(そのまま アプリ名/ とすることが多いと思います)。

例えば、先ほどと同様にプロジェクト内に app1 というアプリと app2 というアプリが存在するとします。この場合、まずはそれぞれのアプリのフォルダ内に urls.py を作成します。今回は例として下記のような urls.pyapp1app2 の両方のフォルダの中に作成することとしたいと思います。

1行目では、from . import views によって同じフォルダ内の viewsimport しています。

アプリのurls.pyの例
from . import views
from django.urls import path

urlpatterns = [
    path('index/', views.index),
    path('user/', views.user),
]

続いてプロジェクト設定フォルダ内の urls.py を下記のように変更します。includeimport が必要である点に注意してください。

アプリのurls.pyの読み込み例
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app_1/', include('app1.urls')),
    path('app_2/', include('app2.urls')),
]

このように各 urls.py を用意しておけば、Django フレームワークがプロジェクト設定フォルダ内の urls.py を読み込んだ際に、app1.urlsapp1 フォルダ内の urls.py)と app2.urlsapp2 フォルダ内の urls.py)を読み込んでくれるようになります。この読み込みが行われるのは include 関数が実行されるからになります。

さらに、Django フレームワークがリクエストを受け取った際、そのリクエストの URL が /app_1/ から始まる場合、app1.urlsurlpatterns に従って実行する関数が振り分けられます。

より具体的には、URL が /app_1/index/ である場合、app1 内の views.py で定義された index 関数が実行されます。また URL が /app_1/user/ である場合、app1 内の views.py で定義された user 関数が実行されます。

URLに応じて読み込み先のurls.pyが変化する様子

同様に、リクエストの URL が /app_2/ から始まる場合、app2.urlsurlpatterns に従って実行する関数が振り分けられることになります。

このように、アプリ毎に urls.py を用意し、プロジェクト設定フォルダ内の urls.py からアプリ毎の urls.py を読み込むようにすることで、プロジェクト全体が受け付ける全 URL に対して関数をマッピングしていくのが Django 公式からも推奨されているマッピングの方法となります。

実際、上記の例のマッピングであれば、プロジェクト設定フォルダの urls.py を下記のように変更しても実現することが可能です。

1つのurls.pyでマッピングを全て定義する例
from django.contrib import admin
from django.urls import path
from app1 import views as app1_views
from app2 import views as app2_views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app_1/index/', app1_views.index)
    path('app_1/user/', app1_views.user)
    path('app_2/index/', app2_views.index)
    path('app_2/user/', app2_views.user)
]

ただし、受け付ける URL が多くなるとプロジェクト設定フォルダ内の urls.py への記述量が多くなり、ソースコードが読みにくくなりますし、またプロジェクトとアプリの依存性が高くなり、アプリの他のプロジェクトへの流用もしにくくなります。

これらを解決するのが、ここで紹介したアプリ毎に urls.py を分離する方法になります。1つ1つの urls.py への記述量が減るのでソースコードが読みやすくなりますし、各アプリのマッピング設定はアプリフォルダ内の urls.py で指定されるのでプロジェクトとアプリの依存性も減ります。

こういったメリットがありますので、マッピング設定はアプリ毎に分離して指定するようにしたほうが良いです。

スポンサーリンク

URL に名前を付ける

さて、ここまでの説明のように、path 関数に routeview を指定することで URL と関数のマッピングを行うことが可能です。path 関数へは URL と関数のマッピングを行うために routeview は必ず指定する必要があります。

path 関数への name 引数の指定

また、path 関数には追加で name 引数を指定することができ、name 引数に名前を指定することで、route に指定する URL に名前を付けることができます。

path関数へのname引数の指定
path(route, view, name='名前')

この name 引数で指定する名前を URL 名 と呼ばせていただきます。上記のように path 関数で name 引数を指定して URL に URL 名 をつけた場合、この URL 名 から URL を逆引きして取得するようなことができるようになります。

URL名からURLを取得する様子

例えば、先程の説明時に利用した app_1/urls.py に下記のように記述すれば、name に指定した 'app_1-top' から URL /app_1/index/ を取得することができるようになります。

URLに名前をつける
path('index/', views.index, name='app_1-top')

URL 名 をつけるメリット

続いて、この name 引数の指定によって URL 名 をつけるメリットについて解説していきます。

結論を言うと、このメリットは「後からの URL の変更が楽になる」という点にあります。

例えば、ウェブにはリダイレクト機能が存在し、この機能によって特定の URL にリクエストを行なったクライアントを別の URL に誘導することができるようになっています。

リダイレクトの説明図

このリダイレクトは、Django においてはビューの関数から redirect 関数の返却値を返却することで実現できます。redirect 関数では引数に誘導先の URL や URL 名 を指定することができます(他にも関数オブジェクトも指定可能ですが、その説明は省略します)。

つまり、上記の例のように path 関数でマッピングした場合、/app_1/index/ への誘導を行うための redirect 関数への引数の指定の仕方としては、例えば下記のパターンが存在することになります。

redirect関数への引数指定1
from django.shortcuts import redirect

return redirect('app_1-top')
redirect関数への引数指定2
from django.shortcuts import redirect

return redirect('/app_1/index/')

前者の場合、URL 名 から URL が逆引きされ、その URL へのリダイレクトが行われることになります。

前者・後者のどちらでもリダイレクトを実現することは可能なのですが、推奨される方法は前者の URL 名 を指定する方法になります。これは、URL 名 で指定を行った方が、後から URL の変更が必要になった際の修正が楽になるからです。

ウェブアプリでは自身のウェブアプリが受け付ける URL を様々な場面で指定することになります。上記のリダイレクトはその例の1つで、例えばテンプレートファイルでも他のページへのリンクを設定するのに URL を指定する必要があります。そして、こういった URL の記述は URL 名 でも代替することが可能となります。 

URL 名 ではなく URL を直接指定していた場合、もし後から URL を変更するようなことになれば、その URL を指定している箇所全てを修正する必要があります。

URL名をつけるメリットの説明図1

ですが、URL 名 で指定していた場合、URL を変更したとしても変更が必要な箇所は urls.py のみとなります。urls.py に記述する URL を変更してやれば、あとは URL 名 から逆引きした際に変更後の URL に変換されることになります。

URL名をつけるメリットの説明図2

つまり、URL 名 を付けておき、ウェブアプリ内のスクリプトやテンプレートファイルに URL ではなく URL 名 を指定するようにしておけば、後から URL を変更したくなった際にも楽に変更を行うことができます。この変更が楽な点を考慮すると、URL を直接指定するのは urls.py のみとし、あとは URL 名 で指定するようにした方が良いです。

URL の一部を引数に指定する(URL のパターン化)

ここまでの説明の通り、urls.py を適切に作成しておくことで、Django フレームワークがリクエストを受け取った際に urls.py に従って URL に応じてビューの関数が実行されるようになります。

ただ、関数を実行する際には引数が指定されることになります。では、Django フレームワークがビューの関数を実行する際には、どのような引数が指定されることになるのでしょうか?

Djangoフレームワークからビューの関数に渡される引数の説明図

これは今までの解説の中でも説明しましたが、Django フレームワークがビューの関数を実行する際には必ず HttpRequest のサブクラス のインスタンスが引数に指定されることになります。基本的には、この HttpRequest のサブクラス のインスタンスのみが引数に指定されることになります。

Djangoフレームワークからビューの関数にHttpRequestのインスタンスが渡される様子

URL の一部を引数としてビューに渡す

ですが、Django では urlpatterns の各要素における path 関数の route 引数への URL の指定方法によって、URL の一部を引数としてビューの関数に渡すことができるようになります。つまり、ビューの関数に指定される引数を増やすことができます。

具体的には、path 関数の route 引数に指定する URL において、変数としてビューの関数に渡して欲しい URL の部分を <型名:引数名> という形式で記述することで、リクエストされた URL の <型名:引数名> 部分がビューの関数の引数 引数名 に指定されるようになります。

ビューの関数の引数の追加

例えば、プロジェクト設定フォルダの urls.py を下記のように記述し、

URLの引数化の例(プロジェクト設定)
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app_1/', include('app1.urls')),
]

さらに、app1 のアプリの urls.py を下記のように記述したとしましょう。

URLの引数化の例(アプリ)
from . import views
from django.urls import path

urlpatterns = [
    path('index/', views.index),
    path('user/<str:username>/', views.user)
]

このように各 urls.py を作成した場合、リクエストの URL が /app_1/user/文字列/ の形式である際に views.pyの  user 関数(views.user)が実行されることになります。そして、その実行時には、文字列 の部分が引数 username に指定される形で user 関数に渡されることになります。

引数にURLの一部が指定される様子

例えば、下記の URL へのリクエストがあった場合は、user 関数が実行され、その際に引数 username に文字列 'YamadaHanako' が指定されることになります。

/app_1/user/YamadaHanako/

同様に、下記の URL へのリクエストがあった場合は、user 関数が実行され、その際に引数 username に文字列 'YamadaTaro' が指定されることになります。

/app_1/user/YamadaTaro/

つまり、URL に応じて関数の引数に渡されるデータが変化することになります。例えば、下記のように user 関数を定義しておけば、print 関数で出力される文字列は URL に応じて変化することになります。

ビューの関数への引数の追加
def user(request, username):
    print(username)
    # 略

このように、pathroute 引数に指定する URL において、URL の部分を <型名:引数名> という形式で記述しておけば、実行されるビューの関数に引数として情報を渡すことができるようになります。

URL のパターン化

また、route 引数に指定する URL の一部を <型名:引数名> と記述することで、ウェブアプリが受け付ける URL のパターン化が行えるようにもなります。要は、1つの path 関数によるマッピング設定が、特定の1つの URL に対してだけでなく、パターンに当てはまる全ての URL に対して適用されるようになります。

URLのパターン化の説明図

例えば下記の形式の URL をリクエストされた際に ユーザー名 で指定されたユーザーの詳細情報を表示する例で考えてみましょう。

/app_1/user/ユーザー名/

URL のパターン化を行わない場合、各ユーザーの詳細情報を表示するための URL に対して個別にマッピング設定を行う必要があるため、ウェブアプリにユーザー登録している全ユーザー分の URL を pathroute 引数に指定する必要があります。

個別のURLにマッピング設定を行う例
from . import views
from django.urls import path

urlpatterns = [
    path('user/YamadaHanako/', views.user),
    path('user/YamadaTaro/', views.user),
    path('user/TakahashiJiro/', views.user),
    path('user/SatoSaburo/', views.user),
    path('user/TanakaShiro/', views.user),
〜略〜
]

しかし、pathroute 引数に URL のパターンを指定すれば、1つのみのマッピング設定(path 関数)で全ユーザーに対応することができます。

URLパターンにマッピング設定を行う例
from . import views
from django.urls import path

urlpatterns = [
    path('user/<str:username>/', views.user),
]

このように、pathroute 引数に指定する URL の一部を<型名:引数名> とすることで URL のパターン化を行うことができ、1つの path 関数により複数の URL に対応することができます。さらに、<型名:引数名> の部分はビューの関数で引数として受け取ることができるようになります。

URLのパターン化の説明図

また、<型名:引数名>型名 に指定できるのは、ここまで利用してきた str だけでなく int なども指定可能です。<型名:引数名>型名 に指定可能なものの一覧は下記の Django の公式チュートリアルから確認可能です。

https://docs.djangoproject.com/en/4.1/topics/http/urls/#path-converters

引数名 には基本的に好きなものを指定して良いですが、ビューの関数の他の引数と被らないようにする必要があります。また、引数名 に指定した引数はビューの関数の引数に用意しておく必要があるので注意してください。

また、URL のパターン化は正規表現などでも実現可能です。ただ、上記のように <型名:引数名> によるパターン化が一番簡単だと思いますので、まずはこの方法でのパターン化を覚えておくと良いと思います。

掲示板アプリでビューを利用してみる

ビューや URL のマッピングに関する解説は以上となります。

続いては、今までの復習の意味も込めて、実際に views.pyurls.py の実装を行なっていきたいと思います。

この Django 入門 に関しては連載形式の解説としており、この連載を通じて簡単な「掲示板ウェブアプリ」を開発していこうと思います。今回は、この掲示板ウェブアプリにおける views.pyurls.py の実装を行い、ビューの作成と URL のマッピングを行なっていきます。

ただし、今回解説した内容はビューと URL マッピングのみですので、これだけで実現できるウェブアプリはとても掲示板とは呼べないものになります…。ですが、今後、Django の様々な要素や機能について解説を行い、それらの実装を加えていくことで、それなりの掲示板ウェブアプリを開発することができます。開発を通して様々なことが学べると思いますので、是非掲示板ウェブアプリの開発にも取り組んでみていただければと思います。また、今回ここで開発するウェブアプリより、プロジェクトの設定や views.pyurls.py の実装さえしてしまえば、一応動作可能なウェブアプリが作れることも理解していただけるのではないかと思います。

また、今回は特に実装観点での解説ページの初回となりますので、プロジェクト・アプリの作成から解説をしていきます。

スポンサーリンク

掲示板アプリのプロジェクト一式の公開先

この Django 入門 の連載を通して開発している掲示板アプリのプロジェクトは GitHub の下記レポジトリで公開しています。

https://github.com/da-eu/django-introduction

さらに、ここから説明していく内容の変更を加えたプロジェクトも下記のリリースで公開しています。ソースコードの変更等を行うのが面倒な場合など、必要に応じて下記からプロジェクト一式を取得してください。

https://github.com/da-eu/django-introduction/releases/tag/django-view

事前準備:Django のインストール

もし、まだ Django のインストールをしていない方は、この機会に Django のインストールをしておきましょう!

Django は下記のように pip を利用してインストールすることができます。

% python -m pip install django

アンインストールも pip を利用すれば下記のように行うことができますので、不必要になればすぐに消すことも可能です。

% python -m pip uninstall django

プロジェクト・アプリの作成

では、プロジェクトとアプリを作成していきます。プロジェクトやアプリの意味合いについては下記ページで解説していますので、詳しく知りたい方は下記ページをご覧ください。

Djangoの全体像とDjangoのウェブアプリが動作する仕組みについての解説ページアイキャッチ 【Django入門2】Djangoの全体像・ファイル構成・動作の仕組み

プロジェクトに関しては下記の形式のコマンドで作成することができます。

% django-admin startproject プロジェクト名

また、アプリは、上記コマンドで作成される プロジェクト名 のフォルダの中で下記の形式のコマンドで作成することができます。

% python manage.py startapp アプリ名

そして、これらのコマンドを実行すれば、プロジェクトやアプリに最低限必要なファイルが自動的に作成されることになります。

今回は、プロジェクト名は testproject、アプリ名は forum にしたいと思います。

ということで、まずは下記コマンドを実行してプロジェクトを作成します。これにより下記コマンドを実行したフォルダ内に testproject というフォルダが作成されます。

% django-admin startproject testproject

次に、cd コマンドで testproject フォルダに移動し、

% cd testproject

さらに下記コマンドでアプリを作成します。

% python manage.py startapp forum

以上により、プロジェクトとアプリが作成され、アプリ開発に最低限必要なファイルやフォルダが作成されることになります。

スポンサーリンク

プロジェクトの設定(アプリの登録)

続いて、プロジェクトの設定を行います。

先ほど cd コマンドで testproject フォルダに移動しましたが、そのフォルダの中にもう1つ testproejct フォルダが存在するはずです。このフォルダには主にプロジェクトの設定を行うファイルが集められており、その1つのファイルとして settings.py が存在します。

次は、この settings.py を変更してプロジェクトの設定を行います。

アプリの登録

まずは、プロジェクトへのアプリの登録を行います。

プロジェクトに登録されているアプリは settings.py で定義される INSTALLED_APPS というリストで管理されており、このリストを変更することでプロジェクトに登録されているアプリを変更することが可能です。

今回はプロジェクトへのアプリの登録を行いますので、登録したいアプリを INSTALLED_APPS に追加を行います。登録したいアプリは、先ほど作成した forum となりますので、下記のようにリストに 'forum' を追加します。これにより、プロジェクトにアプリ forum が登録され、プロジェクト起動時に forum の各種ファイルが読み込まれるようになります。

アプリの登録
INSTALLED_APPS = [
    'forum', # 追加
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

言語・タイムゾーンの設定

また、ウェブアプリで表示される言語や、ウェブアプリで扱う時刻のタイムゾーンについても settings.py で設定可能です。これは別に必須ではないですが、今回は言語を日本語、タイムゾーンも日本時間に変更したいと思います。

言語に関しては settings.pyLANGUAGE_CODE で、タイムゾーンに関しては settings.pyTIME_ZONE でそれぞれ設定することが可能ですので、これらを下記のように変更します。

言語とタイムゾーンの設定
LANGUAGE_CODE = 'ja' # 変更

TIME_ZONE = 'Asia/Tokyo' # 変更

ビューの作成

続いて、このページの本題の1つであるビューを作成していきます。

ビューの例

前述の通り、このビューは views.py への実装を行なって作成していくことになります。この views.py は forum フォルダ内に存在するはずです。今回は、views.py を下記のように変更したいと思います。

views.py
from django.http import HttpResponse, Http404
from django.shortcuts import redirect

class User:
    def __init__(self, id, username, email, age):
        self.id = id
        self.username = username
        self.email = email
        self.age = age

import datetime
class Comment:
    def __init__(self, id, text, date):
        self.id = id
        self.text = text
        self.date = date

users = [
    User(1, 'Yamada Taro', 'taro@yamada.jp', 18),
    User(2, 'Yamada Hanako', 'hanako@yamada.jp', 22),
    User(3, 'Sato Saburo', 'saburo@sato.jp', 53),
    User(4, 'Takahashi Shiro', 'shiro@takahashi.jp', 64)
]

comments = [
    Comment(1, 'おはようございます', datetime.datetime(2023, 3, 4, 12, 4, 0)),
    Comment(2, 'いい天気ですねー', datetime.datetime(2023, 4, 5, 16, 21, 0)),
    Comment(3, '明日もよろしくお願いします', datetime.datetime(2000, 12, 25, 1, 55, 0)),
    Comment(4, 'おやすみなさい', datetime.datetime(2024, 1, 1, 1, 37, 0)),
    Comment(5, '山路を登りながら、こう考えた。智に働けば角が立つ。情に棹させば流される。意地を通とおせば窮屈だ。とかくに人の世は住みにくい。', datetime.datetime(2012, 10, 8, 3, 49, 0)),
]

def index_view(request):
    return redirect('comments')

def users_view(request):
    body = ''
    
    for user in users:
        body += '<a href="/forum/user/{id}/">{name}</a>'.format(id=user.id,name=user.username)
        body += '\n<br>'

    return HttpResponse(body)

def user_view(request, user_id):
    if user_id > len(users) or user_id < 1:
        raise Http404('Not found user')

    body = ''
    user = users[user_id - 1]

    body += user.username
    body += ','
    body += user.email
    body += ','
    body += str(user.age)
    body += '\n<br>'

    return HttpResponse(body)

def comments_view(request):
    body = ''
    
    for comment in comments:
        body += '<a href="/forum/comment/{id}/">{text}</a>'.format(id=comment.id,text=comment.text)
        body += '\n<br>'

    return HttpResponse(body)

def comment_view(request, comment_id):
    if comment_id > len(comments) or comment_id < 1:
        raise Http404('Not found comment')

    body = ''
    comment = comments[comment_id - 1]

    body += comment.text
    body += ','
    body += '{0:%Y年%m月%d日}'.format(comment.date)
    body += '\n<br>'

    return HttpResponse(body)

ビューの説明

この views.py には5つの関数を定義しており、それぞれの役割を簡単に書くと下記のようになります。

  • index_view:トップページを表示する(リダイレクトするだけ)
  • users_view:ユーザー一覧ページを表示する
  • user_view:特定のユーザーの詳細情報ページを表示する
  • comments_view:投稿済みコメント一覧ページを表示する
  • comment_view:特定のコメントの詳細情報ページを表示する

ユーザーはリスト users で管理しており、コメントはリスト comments で管理しています。さらに、各ユーザーは User クラスのインスタンスとして、各コメントは Comment クラスのインスタンスとして扱うようにしています。

また、User クラスの各インスタンスには id (ID)・username (名前)・email (メールアドレス)・age (年齢) を、Comment クラスの各インスタンスには id (ID)・text (本文)・date (投稿日時) を設定できるようにしています。

ユーザーやコメントは事前にリストで用意していますが、フォームを利用することで、ユーザーの登録やコメントの投稿をユーザーが行えるようになります。また、モデルを利用することで、これらのユーザーやコメントをデータベースで管理できるようになります(データベースで管理するために、上記で説明した各クラスは、モデルクラスに置き換えることになります)。

ですが、フォームやモデルについてはまだ説明していないため、まずは事前にリストで用意したものを利用してビューの働きだけを確認できるようにしています。

また、上記の5つの関数は、リクエストが GET メソッドであることを前提とした作りになっています。前述の通り、リクエストには POST メソッドのものも存在しますが、これに関してもフォームの説明をした後に実装例を紹介するようにしたいと思います。

さらに、index_view 以外の関数では、自身の関数の中で HTML を生成し、それをボディとするレスポンスを返却するようになっています(例外が発生しない限り)。これらの HTML の生成に関しては、これも前述の通り、基本的にはテンプレートを利用して実現することになり、これによって各種関数がもう少しスッキリすることになります。

続いて、これらの5つのビューの関数のポイントを説明していくと、1つ目のポイントは全ての関数が下記を満たしている点となります。

  • 引数で “リクエスト” を受け取ること
  • 正常終了時には返却値で “レスポンス” を返却すること
  • 異常が発生したときには例外を発生させること

全ての関数は引数を受け取るようになっており、第1引数 request で Django フレームワークからリクエストを受け取るようになっています。

また、users_viewcomments_view に関しては必ず HttpResponse のインスタンスをレスポンスとして返却するようになっています。さらに、user_viewcomment_view に関しても、基本的には HttpResponse のインスタンスをレスポンスとして返却するようになっているのですが、引数で指定された user_idcomment_id に対応するユーザーやコメントが存在しない場合は Http404 の例外を発生させるようになっています。

index_view の場合は redirect 関数の返却値を返却することになりますが、redirect 関数の返却値は HttpResponsePermanentRedirect or HttpResponseRedirect のクラスのインスタンスとなり、どちらも HttpResponse のサブクラスであるため、index_view もレスポンスを返却することになります。

これらの例のように、ビューの関数は必ずレスポンスの返却 or 例外の発生を行う必要がある点がポイントになります。

2つ目のポイントは、user_viewcomment_viewrequest 以外の引数を受け取るように作られている点になります。したがって、URL の一部を引数に指定する(URL のパターン化) で解説した内容に従い、request 以外の引数を Django フレームワークから受け取れるように urls.py を作成する必要があります。

3つ目のポイントとなるのは index_view で実行している redirect 関数の引数で、この引数には URL の名前を指定しています。具体的には comments という名前を指定しています。したがって、URL に名前を付ける で解説した内容に従い、urls.py で何らかの URL には comments という名前をつけておく必要があります。

特に2つ目と3つ目のポイントに関しては主に urls.py に対するポイントでもあるのですが、大事なのは views.pyurls.py は特に関係性が高く、これらの両方で話が合うように作成する必要がある点になります。

これらのポイントを踏まえた上で、続いて urls.py の変更・作成を行なっていきたいと思います。

URL のマッピング

ビューは作成したものの、ビューは単に作成しただけでは Django フレームワークから実行されません。

前述の通り、URL とビューのマッピングを行う必要があります。

ということで、このマッピングを行うため、次は urls.py を作成していきたいと思います。今回はアプリ側にも urls.py を用意し、プロジェクト側の urls.py からアプリ側の urls.py を読み込むようにすることでマッピングを実現していきます。

アプリの urls.py の作成

まずは、forum フォルダの中に urls.py を作成し、中身を下記のように変更してください。

forum/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index_view, name='index'),
    path('comments/', views.comments_view, name='comments'),
    path('comment/<int:comment_id>/', views.comment_view, name='comment'),
    path('users/', views.users_view, name='users'),
    path('user/<int:user_id>/', views.user_view, name='user'),
]

プロジェクトの urls.py の変更

さらに、testproject フォルダの中にある urls.py を下記のように変更します。

forum/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('forum/', include('forum.urls'))
]

マッピングによるアプリの動作

これにより、URL が /forum/ から始まるリクエストを Django フレームワークが受け取った際、Django フレームワークが forum フォルダの urls.py に従って関数を実行するようになります。

具体的には、リクエストされた URL (ルートパス形式) に対し、Django フレームワークから実行されるビューの関数は下記のようになります。

  • /forum/index_view
  • /forum/comments/comments_view
  • /forum/comment/comment_view
  • /forum/users/users_view
  • /forum/user/user_view

特にポイントになるのが /forum/comment//forum/user/ に対する URL とビューの関数とのマッピングで、これらの URL に対応する comment_viewuser_view では引数を受け取るようになっています。そのため、URL の一部を関数の引数として受け取れるよう、forum フォルダの urls.py におけるこれらの URL のマッピングにおいては、URL の一部を <型名:引数名> という形式で記述しています。

例えば、/forum/comment/ に対する URL マッピングの設定は下記のようになっており、リクエストされた URL が /forum/comment/ から始まる場合、/forum/comment/ の後ろ側の整数が views.py における comment_view に引数 comment_id として渡されるようになります。

URLの一部の引数化
path('comment/<int:comment_id>/', views.comment_view, name='comment'),

また、各マッピングを行うために実行する path 関数では name を設定するようにしており、これによって URL に名前がつけられることになります。今回は index_view で実行する redirect 関数の引数に、URL の名前である 'comments' を指定していますので、この redirect 関数の実行によって、comments の名前に対応する URL の /forum/comments/ にリダイレクトするためのレスポンスが Django フレームワークに返却されることになります。

今回は URL の名前を利用している箇所が1つのみとなりますが、次の連載で説明するテンプレートを導入すると、頻繁に利用するようになります。

以上で、ウェブアプリは完成となります。

スポンサーリンク

動作確認

最後に動作確認を行なっておきましょう!

開発用ウェブサーバーの起動

まずは、ウェブアプリにアクセスできるよう、Django 開発用ウェブサーバーを起動します。この開発用ウェブサーバーの起動は、manage.py が存在するフォルダ(プロジェクトの testproject フォルダの中)で下記コマンドを実行することで実現できます。

% python manage.py runserver

これにより、ウェブブラウザ等のクライアントからリクエストを受け取るウェブサーバーが起動することになります。

コメントの表示の確認

ということで、次はウェブブラウザを開き、アドレスバーに下記 URL を指定します。

http://localhost:8000/forum/comments/

これにより、ウェブブラウザ(クライアント)から上記で示した localhost という名前のサーバーに対して接続が行われ、さらに /forum/comments/ の URL に対してリクエストが送信されることになります。この時のリクエストのメソッドは GET になります。

また、localhost は自分自身の PC を指しており、この PC では runserver の実行により開発用ウェブサーバーが動作してクライアントからのリクエストを待ち受けています。そして、上記のリクエストが送信されてくると、開発用ウェブサーバーがリクエストを受け取り、Django フレームワークを介して /forum/comments/ にマッピングされた関数、すなわち views.pycomments_view が実行されます。さらに、comments_view が実行されてレスポンスを返却すると、Django フレームワークを介して開発用ウェブサーバーからクライアントに対してレスポンスが返却されることになります。

レスポンスを受け取ったウェブブラウザは、レスポンスのボディ等に応じてページを表示します。上記の URL をアドレスバーに指定した場合は、下図のようなページが表示されるはずです。

/forum/comments/へのリクエストによって表示されるページ

このページはコメント一覧を示すページとなっており、各コメント文がリンクとなっています。リンクをクリックすれば、先ほどと同様の流れで /forum/comment/id/ にリクエストが送信されます。この場合は、views.pycomment_view 関数が実行されることになり、id の部分の整数が引数 comment_id として渡されることになります。また、id 部分はコメントの ID を示す整数で、リンクごとに異なる値が設定されるようになっています。

そのため、リンクのクリックにより、クリックしたリンクに応じたコメントの詳細ページが表示されることになります。

/forum/comment/3/へのリクエストによって表示されるページ

ユーザーの表示の確認

同様に、下記の URL をウェブブラウザのアドレスバーに指定すれば、

http://localhost:8000/forum/users/

今度は users_view が実行され、下の図のようなユーザー一覧ページが表示されることになります。

/forum/users/へのリクエストによって表示されるページ

さらに、これも先ほどと同様にユーザー名をクリックすれば、今度は /forum/users/id/ に対してリクエストが送信されることになり(id はユーザーの ID)、user_view が実行され、クリックしたユーザーに応じたユーザーの詳細情報が表示されるようになっています。

/forum/user/2/へのリクエストによって表示されるページ

また、下記をアドレスバーに指定した場合は、index_view が実行されることになり、この場合は /forum/comments/ にリダイレクトされることになります。

http://localhost:8000/forum/

そのため、下記をアドレスバーに指定した時と同じページが表示されるはずです。

http://localhost:8000/forum/comments/

例外発生の確認

最後に例外発生時の動作を確認しておきましょう!まずは下記の URL をアドレスバーに指定してみてください。

http://localhost:8000/forum/comment/10/

この場合は、下図のようなページが表示されるはずです。

例外の発生例1

前述で示した views.py において、管理しているコメント、すなわちリスト comments の各要素に設定されている id15 のみです。そのため、comment_view の引数 comment_id にそれ以外の整数が指定された場合は、下記の if 文が成立して raise により例外が発生するようになっています。

例外の発生
if comment_id > len(comments) or comment_id < 1:
    raise Http404('Not found comment')

このような例外が発生した際には、例外に応じたエラーのページが表示されるよう Django フレームワークがレスポンスを返却するようになっています。上記の場合は Http404 という例外を発生させているため、レスポンスのステータスコードは 404 となります。また、上記はコメントの例になりますが、ユーザーに関しても同様です。

このように、ビュー等での処理中に問題があった場合には例外を発生させてそれをユーザーに伝えることも重要になります。もちろん、問題があることを伝える文章や HTML を HttpResponse のボディに設定して返却するのでも良いです。ダメなのは、問題があるからといってレスポンスの返却も例外の発生も行わないことです。

また、urls.py で URL のマッピングを行なっていない URL に対してリクエストが送信された際は下の図のようなエラーページが表示されることになります。この場合は、ビューの関数が実行されることなく、Django フレームワーク側でレスポンスが自動的に生成されて返却されることになります。

例外の発生例2

以上で、特に今回実装したビューに関する動作確認は完了となります。

リストや各インスタンスの内容が表示されるだけなので、とても「掲示板アプリ」にも見えないかもしれないですが、後々の説明の中でコメントの投稿などもできるようになり、少しずつ掲示板としてのアプリに近づいていくことになりますので少々我慢していただければと思います。

また、現状、ユーザーとコメントが完全に独立していますが、これらはリレーションによって互いに関連付けられ、ユーザーはコメントの投稿者として扱うこともできるようになります。また、これに関しては実例を示す予定はないですが、リレーションを利用することでコメントとコメントを関連付け、コメントに対する返信なども行えるようにすることも可能です。このリレーションに関しても後ほどの連載の中で説明しますが、それまではユーザーとコメントは独立して扱うことになりますので、これに関しても了承していただけると幸いです。

今回はウェブブラウザからの操作によってビューの関数が実行される様子を確認しただけですが、このビューの関数が実行されるようになっただけでもウェブアプリ開発においては大きな一歩を前進できたと考えて良いと思います。

極端な話をすれば、ビューの関数が実行されるようになれば、あとはビューの関数を作り込むことで自身の好きなウェブアプリを開発可能です。ただ、効率的に開発をするためには、テンプレートやモデルなども利用したほうが良いので、この辺りを次の連載以降で是非理解していただければと思います!

まとめ

このページでは、Django におけるビューについて解説を行いました!また、ビューと関係の深い URL マッピングについても解説しています。

Django におけるビューはウェブアプリの機能そのものになります。ビューの実装先となる views.py では、ウェブアプリの機能を関数(or クラス)で実装していくことになります。これらの関数は、リクエストを受け取り、レスポンスの返却 or 例外の発生を行う必要がある点や、リクエストに応じた処理・レスポンスの返却を行う必要がある点がポイントとなると思います。

また、ビューの関数が Django フレームワークから実行されるようにするためには、urls.py での URL のマッピングも必要となります。

極端な話、ビューだけでもウェブアプリは作ることができてしまうのですが、それではメンテナンス性の悪いアプリとなってしまうため、適切にテンプレートやモデルを利用するほうが良いです。

その1つであるテンプレートについては次の連載の下記ページで解説していますので、是非次のページも読んでみてください!

Djangoのテンプレートの解説ページアイキャッチ 【Django入門4】テンプレート(Template)の基本

同じカテゴリのページ一覧を表示