【Django入門3】ビューの基本と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のウェブアプリが動作する仕組みについての解説ページアイキャッチ 【Django入門2】Djangoの全体像・ファイル構成・動作の仕組み

そして、そのリクエストに応じて Django フレームワークからアプリの関数やメソッドが実行されることになります。この Django フレームワークから実行される関数やメソッドを提供するのが「ビュー」となります。さらに、実行された関数やメソッドはリクエストを受け取り、そのリクエストに応じたレスポンスを Django フレームワークに返却します。

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

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

このように、クライアントからのリクエストがあった際に、Django フレームワークとの間でリクエストやレスポンスのやりとりを行うのはビューであり、ビューはアプリにおける窓口的存在であると言えます。

もう少し簡単に考えれば、Django フレームワークから利用される関数やクラス(メソッド)の定義を集めたものがビューであるとも言えます。

このページはビューの基本のみを解説していくため、ビューで定義するのは関数であることを前提に解説を進めていきたいと思います。関数ではなくクラスを定義するようにすることでビューをより簡潔に実装することができるようになるのですが、それについては応用編として別途解説ページを公開し、そこで詳細な説明をするようにしていきたいと思います。

スポンサーリンク

ビューの実装先は views.py

このビューの実装先は、プロジェクト内の各アプリに対して用意される views.py となります。アプリ毎に views.py を実装する必要があります。前述の通り、基本的に views.py に実装するのは Django フレームワークから実行される関数(クラスやメソッド)となります。

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

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

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

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

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

前述の通りビューはアプリの窓口であり、アプリの窓口という責務を全うするために様々な役割を担うことなります。

まずビューは、Django フレームワークから利用される上で、最低限下記の条件を満たす必要があります。

  • リクエストを受け取る
  • レスポンスを返却する(or 例外を発生させる)

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

ビューの関数で最低限リクエストを受け取りレスポンスを返却する必要があることを示す図

この引数として受け取るリクエストとは HttpRequest クラスのインスタンスであり、return するレスポンスとは HttpResponse クラスのインスタンスとなります。

MEMO

より正確に言うと、ビューの関数が引数として受け取るリクエストは “HttpRequest クラス、もしくは HttpRequest のサブクラス” のインスタンスとなります

また、ビューの関数が返却するレスポンスとは “HttpResponse クラス、もしくは HttpResponse のサブクラス” のインスタンスとなります

次回以降の連載では、HttpResponse のサブクラスを利用する例も示していきます

つまり、下記のように関数を定義すれば、最低限ですがビューの関数としては成立することになります。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】テンプレートの基本

役割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 をウェブブラウザにアドレスバー等で指定することで表示することができます。

https://daeudaeu.com/django-view/

上記のような URL をウェブブラウザのアドレスバーに指定した際には、ウェブブラウザから daeudaeu.com というドメインが運営されているサーバーに対し、URL が /dango-view/ となるリクエストが送信されます。

URLが送信される様子

そして、リクエストを受け取ったサーバーは /dango-view/ へのリクエストに応じた処理を行い、結果として /dango-view/ のページを表示するための HTML 等がレスポンスとしてウェブブラウザに送信されることになります(そして、それを受け取ったウェブブラウザがページを表示する)。

受け取ったリクエスのURLに応じた処理を実行し、URLに応じたHTMLを生成してレスポンスする様子

また、下記の URL をウェブブラウザに指定した場合は、同様の動作で /dango-model/ のページを表示するための HTML 等がレスポンスとしてウェブブラウザに送信されることになります。この2つの例においてはリクエストの URL が異なるため、レスポンスとして送信される HTML も異なることになります。

https://daeudaeu.com/django-model/

このように、ウェブサイトでは、ユーザーはリクエスト先の URL によって表示したいページを指定できるようになっています。つまり、「ユーザーがやりたいこと(表示したいもの)」は URL によって指定されることになります。

実際には、ウェブブラウザのアドレスバーに URL を直接指定するのではなく、リンクやボタンをクリックすることでページを表示する機会が多いと思います。この場合も、リンクやボタンのクリック時に表示先の URL が設定されたリクエストが送信されるようになっており、結局 URL によってリクエスト先が決められるという点に変わりはありません。

リンクのクリックによりリンクに設定されたURLに対するリクエストが送信される様子

また、上記の例はウェブサイト閲覧時のものになっていますが、ウェブアプリの場合も同様で、重要なのはリクエスト先の URL に応じて処理やレスポンスを変更する必要があるという点になります。

これを実現するため、Django のビューにおいては、指定される URL 毎に関数を用意し、関数毎に異なる処理の実行及びレスポンスの返却が行われるようにしておく必要があります。こうしておけば、ユーザーから指定された URL に応じた処理の実行・レスポンスの返却を実現することができます。

例えば、リクエストの URL が /login/ である場合にログインページを表示し、さらに /index/ である場合にウェブアプリのトップページを表示するようにウェブアプリを開発したい場合、ビューに login 関数と index 関数を用意する感じになります。

views.pyにURLに応じた関数を用意する様子

さらに、リクエストを受け取った際に、そのリクエストの URL に応じて実行する関数を振り分けるようにすれば、ユーザーからのリクエストの URL に応じた処理やレスポンスを実行することができるようになります。

先程の例であれば、リクエストの URL が /login/ である場合に login 関数を、/index/ である場合に index 関数をそれぞれ実行するように URL に応じた関数の振り分けを行います。

URLに応じて実行する関数を振り分ける様子

このように、リクエスト先の URL 毎に関数を用意し、リクエスト先の URL に応じて実行する関数を振り分けるようにすれば、リクエスト先の URL に応じた処理やレスポンスの返却を実現することができます。

この関数を用意するという点に関してはビューの役割になります。

それに対し、URL に応じて実行する関数を振り分けるのは Django フレームワークの役割となります。ただし、どの URL に対してどの関数を実行するかはウェブアプリ開発者が設定する必要があります。この設定を行なっておけば、その設定に基づいて Django フレームワークが実行する関数の振り分けを行ってくれるようになります。そして、この設定が、ページの題名にもある URL マッピングであり、この設定は urls.py というファイルで行うことになります。これに関しては、後述の ビューと URL とのマッピング で詳細を解説します。

メソッドに応じた処理を行う

また、リクエストに含まれるもう1つの重要な情報がメソッドになります。

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

このメソッドは、リクエストの目的を示すデータになります。

多くの種類のメソッドが存在するのですが、まず覚えておくと良いのが GETPOST になります。

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

メソッドGETの説明図

それに対し、POST はリクエストの目的がデータの送信であることを示すメソッドになります。例えば掲示板などでコメントを投稿したい場合、リクエストの目的はコメントというデータの送信となりますので、この際のリクエストのメソッドとしては POST が指定されます。

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

メソッドPOSTの説明図

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

このように、リクエストではメソッドが指定されるようになっており、メソッドに応じてリクエストの目的が異なります。例えば同じ URL に対してリクエストを行ったとしても、メソッドによってユーザーのリクエストの目的は異なることになります。

そして、Django のビューにおいて重要なのは、このメソッドに応じた処理とレスポンスの返却を行う必要があるという点になります。

例えば、下の図のようなフォームでユーザー登録を行う場合の動作について考えてみましょう!このフォームのページは URL に /signup/ が指定された時に表示されるページとします。さらに、この /singup/ が指定された際にはビューの signup 関数が実行されるものとしたいと思います。

メソッドの説明図1

まず、このページでユーザー登録を行う際には、ユーザーはウェブブラウザでこのページを表示する必要があります。そのため、ページ表示のためのデータを取得するために、メソッドが GET、URL が /signup/ のリクエストを送信することになります。そして、このページを表示するためのデータ(HTML)が取得され、ウェブブラウザに上の図のようなページが表示されることになります。

つまり、この singup 関数では、メソッドが GET の時にフォームのページを表示するための HTML をレスポンスとして返却する必要があることになります。

/signup/に対してGETメソッドのリクエストが送信された際の動作を示す図

さらに、ユーザーはユーザー登録を行うために、フォームにユーザー名やパスワードを入力し、それから登録ボタンを押すことになります。この際には、入力したユーザー名やパスワードがウェブアプリに対して送信されることになるため、リクエストのメソッドは POST である必要があります。URL に関してはページ表示の時同様に /signup/ が指定されるものとしたいと思います。

ボタンクリックによってPOSTリクエストが送信される様子

そうなると、この場合も singup 関数が実行されることになります。ですが、先ほどとは異なりメソッドが POST であり、ユーザーが入力したデータが一緒に送信されてくることになります。

もし、この場合にもメソッドが GET の時と同じ処理を実行してしまうと、単に再度フォームが表示されるだけで、ユーザーの登録が行われないことになります。ユーザーの登録を行うためには、メソッドが POST の場合には、送信されてきたデータを受け取り、さらにそれに応じてユーザーの登録を行うような処理が必要となります。

/signup/に対してPOSTメソッドのリクエストが送信された際の動作を示す図

つまり、このような singup 関数では、メソッドが POST の場合と GET の場合とで処理を切り替え、メソッドに応じた処理を行う必要があることになります。このように、ビューの関数は、複数のメソッドのリクエストを受け取る可能性がある場合、それらのメソッドに応じて処理を切り替えるように実装する必要があります。

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

ただ、この実現方法は簡単です。ビューの関数で引数として受け取る HttpRequest のインスタンスには、 method データ属性にメソッドの種類が文字列としてセットされています。

したがって、下記のように if 文でメソッドの種類に応じて処理が分岐するようにしてやれば、リクエストに応じた処理を実現することが可能となります(下記ではメソッドが POST の場合とそれ以外の場合とで分岐するようにしています)。

メソッドに応じた処理の分岐
def signup(request):
    if request.method == 'POST':
        # メソッドがPOSTの場合の処理
        return HttpResponse(〜略〜)
    else:
        # メソッドがPOST以外(GET)の場合の処理
        return HttpResponse(〜略〜)

このように、views.py に URL に応じた関数を定義しておき、さらに関数の中でメソッドに応じた処理の分岐を行うようにしておけば、リクエスト先の URL だけでなく、メソッドに応じた処理も行うことができるようになります。

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

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

メソッドの説明図1

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

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

リクエストの情報に応じた処理を行う

また、ビューの関数が受け取る HttpRequest のインスタンスにはデータ属性として様々な情報がセットされていますので、これらの情報を利用することで開発できるウェブアプリの幅を広げることができます。

例えば、先程 POST メソッドについて説明しましたが、POST メソッドでのリクエスト時にはデータが送信されてくることになり、このデータはビューの関数が受け取る HttpRequest のインスタンスのデータ属性 POST にセットされています。

ですので、このデータ属性 POST を利用すれば、ユーザーが送信してきたデータを保存したり、加工したりすることができ、これによって様々な機能実現することができるようになります。分かりやすい例で言えば、掲示板へのコメント投稿機能やログイン機能・ユーザー登録機能を実現するようなことも可能です。

また、ログイン機能をウェブアプリに持たせた場合、ビューの関数が受け取る HttpRequest のインスタンスの user データ属性にログイン中のユーザーの情報がセットされることになります。ですので、この user データ属性を利用してログイン中のユーザーの情報を表示したりすることがもできるようになります。

このように、HttpRequest のインスタンスにセットされている情報を利用することで、実現できる機能が増え、開発できるウェブアプリの幅も広げることができます。

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

これらのデータも含めて、リクエストに応じた処理・レスポンスの返却を行うことがビューの関数にとっては重要となります。

スポンサーリンク

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

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

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

例えば、掲示板を表示するページに対する URL へのリクエストを受け取った際には、掲示板にすでに投稿されているコメントを取得し、その取得したコメントを埋め込んだ HTML をレスポンスとして返却します。こうすれば、その HTML を受け取ったウェブブラウザ側で、掲示板に投稿済みのコメントを表示することができます(この投稿済みのコメントはあらかじめデータベースに保存しておく必要があります)。

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

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

例えば、ウェブアプリにコメントを送信するリクエストを受け取った際には、そのコメントの内容をデータベースへ保存します。

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

こうしておけば、後からデータベースからコメントの内容を取得し、掲示板として各コメントの一覧を表示するようなことができるようになります。

データベースから取得したコメントを埋め込んだHTMLをレスポンスする様子

このように、ウェブアプリでは、リクエストを受け取った際にデータベースの操作や、データベースから取得した情報を埋め込んだ HTML の生成が必要になるものが多いです。

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

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

そのため、ビューからは直接データベースの操作を行う必要はありません。データベースの操作を行う際は、モデルに対して操作の依頼を行えば良いだけになります。つまり、モデルが提供するメソッドを実行すれば良いだけです。

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

ただし、モデルへの依頼内容はビューが決める必要があります。これは、実装的な観点で言えば、実行したいデータベースの操作に応じたメソッドの実行、さらには、そのメソッドへの引数の設定は views.py で定義する関数に実装する必要があることを意味します。

モデルに依頼する内容はビューが適切に決める必要があることを示す図

また、HTML の雛形はテンプレートから提供されることになるため、ビューはその雛形に対して埋め込みたい情報を埋め込むだけで良いことになります。この HTML の雛形はファイルであり、このファイルのことをテンプレートファイルと呼びます。

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

また、テンプレートファイルへの情報の埋め込みは、Django フレームワークの提供する render 関数により実現することができます。この render 関数に「テンプレートファイルのパス」と「テンプレートファイルに埋め込みたいデータ(コンテキスト)」を引数に指定して実行すれば、返却値としてテンプレートファイルにデータを埋め込んだ結果の HTML をボディとするレスポンス(HttpResponse のインスタンス)を取得することができます(実際には render 関数には HttpRequest のインスタンスも指定する必要があります)。

render関数でテンプレートファイルにデータを埋め込んだHTMLを生成して取得する様子

つまり、ビューは、適切に引数を指定して render 関数を実行し、その返却値を return するだけで、Django フレームワークに対してレスポンスを返却することができます。

ただし、テンプレートではテンプレートファイルを複数種類用意するケースが多いです。例えば特定のユーザーの詳細情報を表示するページと全ユーザーの一覧を表示するページではページの構造が異なるため、テンプレートファイルとしても別々に用意しておく必要があります。

そのためビューは、受け取ったリクエストに応えるために、複数存在するテンプレートの中から適切なものを選択する必要があります。そして、そのテンプレートファイルのパスを render 関数の引数として渡す必要があります。

表示するページに応じたテンプレートが用意されている様子

また、テンプレートファイルに埋め込むデータを準備するのもビューの役割となります。このデータをコンテキストと呼びます。コンテキストはデータベースから取得したデータをセットして作成することが多いです。

このように、ビューはデータベースの操作や HTML の生成を行う際にモデルやテンプレートを利用することができます。これにより、役割をビューとモデルとテンプレートとで分担することができ、各部分の実装が簡潔になり、保守しやすくなります。

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

ただし、受け取ったリクエストによってデータベースへの操作内容やレスポンスとして返却したい HTML は異なるはずなので、リクエストに応じられるよう、ビューは適切に実行するモデルのメソッドや使用するテンプレートファイルの選択を行う必要があります。

ここではビューの役割という観点で説明を行いましたが、ここで登場したモデルやテンプレートに関しては後のほどの連載の中で説明をします。また、その際に、ビューからモデルやテンプレートを利用する際の実装例の紹介も行います。ですので、モデルやテンプレートに関しては今後の連載の中で理解していただくとして、このページでは、まずはビューの役割を理解しておいていただければ良いと思います。

ビューに関してのまとめ

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

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

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

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

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

  • リクエストを受け取る
  • レスポンスを返却する(or 例外を発生させる)

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

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

URLに応じて実行する関数を振り分ける様子

指定された URL に応じた処理は、例えば views.py に指定可能な各 URL に応じた関数を用意しておき、さらに指定された URL に応じて実行する views.py の関数を振り分けるようにすることで実現することができます。

views.py の関数を実行するのは Django フレームワークであり、この Django フレームワークから実行される views.py の関数の振り分けを実現するために必要な設定が URL のマッピングとなります。これについては、次の ビューと URL とのマッピング で解説したいと思います。

また、指定されたメソッドに応じた処理は、views.py に用意した関数の中でメソッドに応じた処理の分岐を実装することで実現することができます。

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

URL やメソッドだけでなく、ビューの関数が受け取るリクエストには様々な情報が含まれます。これらの情報を上手く利用することで、開発可能なウェブアプリの幅を広げることができます。例えば POST メソッドで送信されてきたデータやログイン中のユーザーの情報などを利用することが可能です。

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

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

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

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

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

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

より具体的には、Django フレームワークには Views というクラスが用意されており、これを継承することでリクエストのメソッドに応じて実行されるビューのメソッドを自動的に振り分けることもできるようになります。つまり、1つのビューの関数の中でリクエストのメソッドに応じた処理の分岐を行う必要はなく、リクエストのメソッドごとにクラスのメソッドを用意することで、リクエストのメソッドに応じた処理の実行の実現を行うことが可能となります(リクエストにもクラスにもメソッドがあって文章がちょっと複雑ですね…)。

こういったクラスをベースとしたビューの作り方についても別途ページを公開して解説していきたいと思います。まず、このページでは、ビューの役割や関数をベースとしたビューの作り方を理解していただければと思います。

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

さて、ここまで解説してきたように、ビューはリクエストに応じた処理やレスポンスの返却を行う必要があります。そして、リクエストでは URL が指定されるため、URL に応じた処理やレスポンスの返却を行う必要があることになります。

これを実現する方法の1つは、指定可能な各 URL に応じた関数を views.py に定義し、URL に応じて実行する views.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 フレームワークから読み込まれて利用されるファイルとなります。

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

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 に応じた関数の実行を行ってくれるようになります。

実際には、urlpatterns の各要素には path 関数の返却値を指定するのではなく、path 関数自体を指定することになります。こうしておけば、Django フレームワークから urls.py が読み込間れる際に path 関数が実行され、 urlpatterns の各要素に path 関数の返却値であるマッピング設定がセットされることになります。

つまり、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フレームワークからビューの関数に渡される引数の説明図

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

ここまでの解説でも何回か説明しましたが、ビューの関数には必ず HttpRequest のインスタンスが引数指定されます。基本的には、この HttpRequest のインスタンスのみが引数に指定されることになります。

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

ですが、Django では urlpatterns の各要素における pathroute 引数への 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 のパターンを指定すれば、全ユーザー分の 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 の様々な要素や機能について解説を行い、それらの実装を加えていくことで、それなりの掲示板ウェブアプリを開発することができます。開発を通して様々なことが学べると思いますので、是非掲示板ウェブアプリの開発にも取り組んでみていただければと思います。

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

スポンサーリンク

事前準備: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つ目のポイントは全ての関数が下記を満たしている点となります。

  • リクエストを受け取る
  • レスポンスを返却する(or 例外を発生させる)

全ての関数は引数を受け取るようになっており、第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 の /forum/ よりも後ろ側の部分のパスに応じて実行されるビューの関数が振り分けられるようになります。

具体的には、リクエストされた 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 におけるビューはウェブアプリの窓口的存在です。自身で開発を行うプロジェクト内において、Django フレームワークからまず実行される関数はビューとなります。ビューはリクエストを受け取り、レスポンスの返却 or 例外の発生を行う必要がある点や、リクエストに応じた処理・レスポンスの返却を行う必要がある点がポイントとなると思います。

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

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

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

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

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