見出し画像

【サンプルコード付き】Django Viewをやさしく解説してみる①(View基礎知識編 ~関数ビュー、クラスベースビューの基礎を理解~)

1. Viewって何をやってるの?

View(ビュー)の作成は、DjangoでWeb開発する場合避けて通れないです。
Viewがどういった処理を担っているのか、下図のイメージで掴みましょう。

View 機能イメージ

Viewは、WebアプリにHTTPリクエストを送り、レスポンスが返るまでのフローの中で非常に重要な箇所を担っています。
Viewが役割を果たすことで、Webアプリ上では目的のページが適切に表示されていきます。
役割をもう少し細かく分類すると、Viewの役割は大きく3つです。
あるURLにアクセス(HTTPリクエストに当たる)して、あるWebページを表示させたい(レスポンスに当たる)場合を想定してください。

■Viewの役割
・表示するHTMLファイルを選択する
・表示する内容をカスタムするための変数を準備する
変数をHTMLファイルの指定位置に埋め込み、表示ページを完成させる

という具合で、Webアプリの動作の中でも根幹となるバックエンドの処理を担っています。
役割が分かったところで、早速解説していきます。

2. 解説コンテンツ

本記事では、DjangoのViewの基礎知識と、筆者のおすすめの記述方法を説明します。

まず解説する基礎知識を以下にリストアップします。
Viewの作成にあたっては、どれも知っておくと段違いに理解しやすくなりますので是非習得して欲しいです。

■基礎知識のリスト⇨理解して欲しいこと
リクエストの種類 ⇨ 同じURLアクセスでも処理を変える仕組み
関数ベースView  ⇨ View記述のフォーマット①
クラスベースView ⇨ View記述のフォーマット②
Viewの継承      ⇨ 別Viewでコーディング済みの機能を利用する仕組み
汎用ビュー    ⇨ 継承用にDjangoが標準で備えるクラスベースView
ミックスイン   ⇨ 継承以外でViewに機能を追加する仕組み

この辺りのルールさえ理解できれば、あとは実現したい機能をPythonでコーディングするだけです。

余談ですが、Pythonのコーディングに自信がない方は、ドットインストール(プログラミングの動画解説サービス)がオススメです。
動画一本あたり5分以下で、1日もあればPythonの基礎文法内容は理解できますので、費用対効果は抜群です。

https://dotinstall.com/lessons/basic_python_v3

それでは、話を戻してViewの基礎知識について解説していきましょう!

3.1 View基礎知識 ~リクエストの種類~

一言にURLにアクセスすると言っても、実はアクセスの種類は4種類もあります。それぞれの違いを表にまとめました。

View リクエスト種類表

皆さんがブラウザのURLを手入力してアクセスしているときは、Read(get)をしていることに該当します。
筆者の感触では、どんなWebサービスでもReadの使用頻度は桁違いに多く、残りは横並びな印象です。
ちなみに筆者のポートフォリオは、”get””post”のみで実装しています。

3.2 View基礎知識 ~関数ベースView~

Viewの記述フォーマット①です。
↓実際に書き方の例を見てみましょう。

View 関数ベースビュー記述

引数によく使われる例として、requestがあります。
Webアプリ開発に用いる場合は「表示ページの状態」を戻り値にセットすることが多く、renderメソッドのなかでrequestをよく使います。

その他に、HTMLに埋め込みたいデータを作成するのに必要な引数も受け取れます。その際、タプルで受け取るものは*args辞書型で受け取るものは**kwargsに格納されています。
必要に応じて、def 関数名(request, *args, **kwargs)と引数に含めてあげましょう。それぞれ関数の処理内では、args、kwargsでアクセス可能です。

関数ベースViewの特徴、クラスベースViewとの違いは次のクラスベースViewの説明でまとめて行います。

3.3 View基礎知識 ~クラスベースView~

Viewの記述フォーマット②です。
↓こちらも実際に書き方の例を見ていきましょう。

View クラスベースビュー記述

classの引数には、継承するクラスを記述します。継承の詳細は次のセクションで解説します。今は過去に作成したクラスベースビューを再利用するくらいのイメージでいいです。
そして、クラス関数の引数にはself、requestを格納した記述がWebアプリ開発におけるベースになります。

関数ベースビューとの違いですが、処理の上での機能に差はありません。
差があるところを列挙すると、以下です。

■関数ベースとクラスベースでの書き方の差異
継承用クラスのインポートが追加が必要
クラス定義の記述が必要
・対象のリクエストメソッドをdefで宣言
defの引数にselfが必要
変数の利用にはselfの接頭辞が必要

どうでしょうか?
classの扱いは基本ではありますが、defのみで書く時と比べて書き方のルールが多いので、関数ベースビューの方が初めは書きやすいと思います。

ただ、ルールが多いということは、逆に「何が書いてあるか分かりやすい」というメリットがあります。また、クラスベースの記述を続けることは、後ほど説明する継承により長期的にコード量を削減できます。

以上から、筆者はクラスベースでの記述をオススメします。

3.4 Viewの基礎知識 ~Viewの継承~

クラスベースビューで書く場合、継承の仕組みを上手く使うことでコード量を大幅に削減できます。
↓まずは継承のメリットを確認できるコードを見ていきましょう。

View 継承の解説

この例の様に、表示するHTMLは異なっても、埋め込みに使う変数が共通なURLがある場合、継承のメリットは計り知れません。
共通で使いたい定義を持つベースビューを自作し、クラス宣言文の()内に継承元として記述することで継承完了です。これにより、継承元のすべての記述を引き継ぐことができます。

今回のサンプルでも用いましたが、この継承の仕組みを効率的に活用するためのDjango標準ビューがあります。
次の章で説明しますが、汎用ビューと呼ばれ、Webアプリで実装する標準的な動作を行うためのコードをデフォルトで含むビューが用意されています。
TemplateViewもその一種で、次の章でそのほかにどの様なものがあり、それぞれがどんな機能を持っているのかを解説していきます。

3.5 Viewの基礎知識 ~汎用ビュー~

Djangoに標準で備えられている汎用ビューについて解説します。
これらの機能を把握して、うまく継承することを習慣化すれば書くコード量は激減し、開発もスピーディになることでしょう。
ぜひ、全てマスターして皆さんのポートフォリオ制作に織り込める様になってください!

■TemplateView
HTMLファイルのページ表示
のために用意された汎用ビューです。
これを継承することで、この上なく少ないコード量でHTMLファイルを表示するページを作れます。
template_nameに表示させたいHTMLファイルを、templateディレクトリから選択し名称を転記するだけです。
簡単すぎますね。HTMLでデザインを作り込むのがメインのトップページの表示にはぜひこれを使いましょう。

#<TemplateViewの例文コード>
#インポート
from django.views.generic import TemplateView

class IndexView(TemplateView):    #template_nameのみ記述必要
   template_name = 'index.html'

■CreateView
新しいモデルオブジェクトを作成する
ために用意された汎用ビューです。
これを継承することで、HTMLファイルの表示、ユーザーが入力するフォームの自動生成、そしてフォームへの入力情報でモデルオブジェクトの作成ができます。
template_name、作成先モデル、使用フィールド、作成成功時のリダイレクトURLを記述することで準備は完了。
HTMLファイルのコードの中で、フォーム表示させたい箇所に{{ form }}と入力してあげることで作成できます。
TemplateViewと比べると作業量は多いですが、form.pyを準備するよりもかなり簡単です。ユーザーからのコンタクトフォームにはぜひこれを使いましょう。

#<CreateViewの例文コード>
#インポート
from django.views.generic import CreateView
from django.urls import reverse_lazy
from .models import Contact

class ContactCreateView(CreateView):
   template_name = 'contact/create.html'            #4点の記述が必要
   model = Contact
   fields = ['title', 'body']
   success_url = reverse_lazy('sample_app:index')

■UpdateView
既存のモデルオブジェクト情報を変更する
ために用意された汎用ビューです。
これを継承することで、HTMLファイルの表示、ユーザーが入力するフォームの自動生成、そしてフォームへの入力情報で既存のモデルオブジェクトの更新ができます。
CreateViewの”更新”版ですね。

ただし、更新できるのはURLから特定される1つのオブジェクトのみです。
URLの構成が/posts/<int:pk>/updateのように、オブジェクトを特定するための情報を含んでいないと使えません。この場合だと、pk(プライマリーキー。idのこと。)です。うまく作動しない場合は、urls.pyの記述を見直してみてください。
UpdateViewの場合も、form.pyを準備するよりもかなり簡単に実装できます。掲示板投稿をユーザーに更新させるためには、ぜひこれを使いましょう。

#<UpdateViewの例文コード>
#インポート
from django.views.generic import UpdateView
from django.urls import reverse_lazy
from .models import BoardPost

class BoardPostUpdateView(UpdateView):
   template_name = 'board_post/update.html'        #4点の記述が必要
   model = BoardPost
   fields = ['title', 'body']
   success_url = reverse_lazy('sample_app:index')

■DeleteView
既存のモデルオブジェクトを削除する
ために用意された汎用ビューです。
これを継承することで、HTMLファイルの表示、モデルオブジェクトの削除ができます。
こちらもUpdateViewと同じく、URLにモデルオブジェクトを特定する情報が必要になります。
また、HTML内で<form></form>タグをPOSTメソッドで継承後のビューを呼び出すように送らないと使えません。
意外と書くコード量が減らないためか、あまり使用頻度は高くないそうです。掲示板投稿をユーザーに消去させるためには、ぜひこれを使いましょう。

#<DeleteViewの例文コード>
#インポート
from django.views.generic import DeleteView
from django.urls import reverse_lazy
from .models import BoardPost

class BoardPostDeleteView(DeleteView):
   template_name = 'board_post/update.html'        #3点の記述が必要
   model = BoardPost
   success_url = reverse_lazy('sample_app:index')

■ListView
複数データを取得する
ために用意された汎用ビューです。
これを継承することで、HTMLファイルの表示、同一モデルのオブジェクトの複数取得、ページ送りで確認可能な表示機能の実装ができます。
template_name、表示モデル、ページあたりの表示件数を記述することで準備完了です。
HTMLファイルのコードの中で、{{ object_list }}をfor文で取り出し、1件ずつ目的の表示方法で表示させられます。ちなみにクラス内部でcontext_object_listに名前を記述しておくと、HTML内で{{ object_list }}以外の名前を使えます。

ページネーションを自動で設定可能なのはメリット大きすぎますね。投稿記事一覧のリスト表示のページには、ぜひこれを使いましょう。

#<ListViewの例文コード>
#インポート
from django.views.generic import ListView
from .models import BlogPost

class BlogPostListView(ListView):
   template_name = 'blog_post/list.html'    #3点の記述が必要
   model = BlogPost
   context_object_name = 'blog_posts'       #ここは任意。書かなければ{{ object_list }}で呼び出し可能
   paginate_by = 10

■DetailView
既存のモデルオブジェクト1つを取得する
ために用意された汎用ビューです。
これを継承することで、HTMLファイルの表示、特定オブジェクトの取得が可能になります。
template_name, 使用モデルを記述することで、URLから特定されるオブジェクトを{{ object }}に格納してHTMLファイル内で表示可能です。
こちらもGetリクエストの定義文を書かなくていい分、簡潔にかけています。投稿記事の詳細表示ページには、ぜひこれを使いましょう。

#<DetailViewの例文コード>
#インポート
from django.views.generic import DetailView
from .models import BlogPost

class BlogPostDetailView(DetailView):
   template_name = 'blog_post/detail.html'    #2点の記述が必要
   model = BlogPost
   context_object_name = 'blog_post'          #ここは任意。書かなければ{{ object }}で呼び出し可能

3.6 View基礎知識 ~ミックスイン~

Djangoに標準で備えられている、継承とは別の機能拡張方法です。(厳密には継承と同じなのですが、今回は分かりやすさの都合こう説明します。)
↓まずはミックスインの実例をコードでみて行きましょう。

画像5

ミックスインをうまく活用することで、一般的によく使うであろう特殊な機能を簡単に織り込むことができます。

使い方はめちゃくちゃ簡単です。
使用したいミックスインをインポートクラス宣言文の継承先の直前にミックスインを記述すれば準備完了です。
よく使うミックスインを2つ紹介します。

■LoginRequiredMixin
上記の例文コードで使用しているものです。
名前の通りですね。ログインユーザーでないとアクセスできない制限をかけるために用意されたミックスインです。
これをミックスインすることで、ログインしていないユーザーがアクセスした際にはページ表示させず、ログインページにリダイレクトさせることができます。
この場合、リダイレクト先のログインURLは/accounts/loginとなっている必要があるので、そこだけ注意です。

■UserPassesTestMixin
こちらもアクセス制限用のミックスインです。
UserPassesTestMixinは制限をカスタムできる点がポイントです。
制限の掛け方は、def test_func(self):という関数を定義し、その中でreturnに変数をセットすれば準備完了です。
このセットした変数がFalseの場合にアクセス制限が機能し、アクセスしたユーザーにはstatus code 403エラーのページを表示させる動作をします。

↓一応こちらもサンプルコードです。

#<UserPassesTestMixinの例文コード>
#インポート
from django.views.generic import DetailView
from .models import CustomUser
from django.contrib.auth.mixins import UserPassesTestMixin

class Mypage(UserPassesTestMixin, DetailView):    #Mixin記述
   template_name = 'accounts/mypage.html'
   model = CustomUser
   context_object_name = user
   
   def test_func(self, request, **kwargs):        #アクセス制限対象を定義(今回はログインユーザーと、マイページの所有者が同じか確認)
       login_user = self.request.user
       mypage_owner = super().get_context_data()
       return (login_user == mypage_owner)         #戻り値がTrueの時のみ許可

4. オススメの記述方法

最後に筆者のオススメの記述方法を紹介して終わります。


■ビュー構造の選択
・特別な理由がない限りクラスベースビューを使う
・利用可能な場合は汎用ビューを継承する
・汎用ビューを使う方法がすぐに思いつかない場合はViewを継承し、get or postメソッドで記述

◎ビュー構造のポイント
delete、updateは正直使わなくても困らないので使用しない。リクエストの種類をget, postに絞ることでわかりやすくなる。
getならページ表示のためのアクセス時。postならモデル操作(作成、更新、削除)時のアクセス時。


■継承Tips
・どのページでも共通で使う変数をセットした汎用ビューを自作View化しておき、全てそれを継承して作る

◎継承のポイント
どのページでも共通でメニューバーを表示させるページ構造を作りたい場合がよくある。その場合は、メニューバー内の項目のうち、モデルから情報を受け取る必要があるものに対して、ベースビューとする自作ビュー準備行っておく(筆者のポートフォリオの場合、カテゴリ名など)


■ミックスインTips
・ログイン後の使用を予定するページではログイン制約をかける
・モデルオブジェクトの編集などのページでは本人制約をかける

◎ミックスインのポイント
掛け方の考え方は、各ページで”触るとまずい人”に制約をかける

5. 最後に

いかがでしたか?

Viewの作成は、Djangoでのアプリ開発で非常に重要な役割を担っています。
また、他のMVCのフレームワークであってもControllerの作成に同じ考え方が必要になります。
どのフレームワークで行うにしろ、この記事で説明した情報を土台にできると思うので、ぜひ知識を自分のものにしていただけると嬉しいです!

また、別のDjangoファイルの解説記事も充実させていきます!
お楽しみに!

よろしければサポートをお願いします🙇‍♂️ いただいたサポートは、クリエイターの活動資金として使わせていただきます😌 活動を通してえた経験を、また記事として皆さんと共有します👍