Django: Viewの基本とTemplate View

この記事の内容
  • DjangoのViewについて
  • views.pyについて
  • TemplateViewの使い方

この記事の対象者
  • Web開発に興味を持っている方
  • Djangoの学習者
  • Pythonエンジニア

この記事の構成
  1. DjangoのView
  2. 関数ベースとクラスベース
  3. TemplateView

前回までの記事

Django: DBを扱うmodelsについて

Django: URLの設計について

はじめに

ModelとURLに触れ、DjangoのWeb開発の心臓であるViewまできました。

前提条件として、
Modelについての記事で作成したmodels.pyと
URLについての記事で作成したurls.pyを以下に共有しておきます。

models.py

from django.db import models


class Category(models.Model):

    name = models.CharField(max_length=31, verbose_name="カテゴリ名")

    def __str__(self):
        return self.name

    class Meta:

        verbose_name_plural = "カテゴリ"


class Article(models.Model):

    title = models.CharField(max_length=62, verbose_name="タイトル")
    slug = models.SlugField(verbose_name="URLスラッグ(英語)")
    image = models.ImageField(upload_to="article_image", blank=True, verbose_name="記事のイメージ写真", help_text="登録しない場合は、デフォルトのイメージ写真を使用する")
    categories = models.ManyToManyField(Category, verbose_name="カテゴリ")
    body = models.TextField(verbose_name="本文")
    status = models.PositiveSmallIntegerField(default=1, verbose_name="公開ステータス", help_text="1:下書き, 2:公開")
    liked = models.IntegerField(default=0, verbose_name="いいね数")
    is_deleted = models.BooleanField(default=False, verbose_name="削除フラグ")
    creator = models.CharField(max_length=31, verbose_name="投稿者")
    created = models.DateTimeField(auto_now_add=True, verbose_name="投稿日時")
    modified = models.DateTimeField(auto_now=True, verbose_name="更新日")

    def __str__(self):
        return self.title

    class Meta:

        verbose_name_plural = "記事"



class Comment(models.Model):

    article = models.ForeignKey(Article, on_delete=models.CASCADE, verbose_name="記事", related_name="comments")
    commenter = models.CharField(max_length=31, verbose_name="コメント者名")
    body = models.TextField(verbose_name="コメント文")
    created = models.DateTimeField(auto_now_add=True, verbose_name="コメント投稿日時")

    def __str__(self):
        return self.article.title + ":{}のコメント".format(self.commenter)

    class Meta:

        verbose_name_plural = "コメント"

urls.py

  • index (hogehoge.com) ダッシュボード
  • list (hogehoge.com/article/list) 記事一覧ページ
  • detail (hogehoge.com/article/<int:pk>) 記事詳細ページ
from django.urls import path
from . import views

app_name= 'article'
urlpatterns = [
    path('', views.index, name='index'),
    path('article/list', views.list, name='list'),
    path('article/<int:pk>', views.detail, name='detail'), 
]

DjangoのView

Djangoでは、views.py でDBとのやりとりから、フロントとのやりとりを行います。

views.pyで行うこと

views.py では、ページごとに処理を記述し、それをurls.pyに記述することでフロントエンドのHTMLファイルに投げられます。

ここでは、DBにアクセスしてデータを作成/取得/更新/削除(CRUD処理と呼ぶ)します。

また、DBにアクセスしなくても、現在時刻を取得したり、計算をしたり、Pythonのプログラミングで行えることがほぼ全てここで行えたりします。

外部APIを使用したり、その他様々な処理もこのviews.pyで行うことができます。

ユーザー目線で話を進めると、
ユーザーがページにアクセスするときに、chromeなどのブラウザがページを読み込んでぐるぐるしている時があります。

その間に、バックエンドのviewsで処理が走り、フロントでも処理が走り、ページが表示される仕組みになっています。

裏を返せば、ここの処理が長くなればなるほど、ページのロード時間が長くなり、ユーザビリティが落ちることになります。

また基本的にDBにアクセスするのには時間がかかります。できるだけDBへアクセスして、データを取り出すときは、回数を減らして処理を最適化することが重要になってきます。

それではまずDjangoのViewを作るための主要な2つのやり方を紹介します。

関数ベースとクラスベース

関数ベースとは主に def を用いて、ページの処理を書いていくスタイルのことです。(易しめ)

クラスベースは Class を用いてページの処理を書きます。(慣れるのに時間がかかる)

状況にもよりますが、ほとんどのケースでClassベースで書いていくことが推奨されているので、頑張ってClassベースで処理を書いていくことをお勧めします。

関数ベース

Djangoの学習を始めたばかりの場合、ほとんどの場合関数ベースで書くことがほとんどだと思います。

関数ベースは、def()でrequestを受け取り、return するまでの間に処理を書いていきます。

POSTやGETなどrequestの種類に応じて、if文で分岐などをしなければいけないので、コードがどうしても長くなり可読性が悪くなります。

from django.shortcuts import render
from .models import Article

def index(request):
    user = request.user # Userデータを取得
    post = request.POST # POSTデータを受け取り

    articles = Article.objects.all() # ArticleデータをDBから取得
    context = {'articles': articles}
    return render(request, 'index.html', context)

return render()の中には以下を入れます。

  • request
  • 対応するHTMLファイルのパス
  • 投げるデータを辞書型で

contextのキーにしている 'articles' を htmlファイル上で {{ articles }} とすることで、このArticleデータをフロントで表示することができます。

formからPOSTでリクエストする場合

例えば、formでPOSTリクエストを受け取る場合、if文で分岐する必要があります。

from django.shortcuts import render
from .models import Article

def index(request):
    if request.method == 'POST':
        title = request.POST['title']
        ...
        return ...

    articles = Article.objects.all() # ArticleデータをDBから取得
    context = {'articles': articles}
    return render(request, 'index.html', context)

HPなど処理が少ないプロダクトはまだいいのですが、規模の大きなプロダクトになればなるほど、この関数ベースの書き方は、コードの量と行が増えて、可読性がすごく悪くなります。
あくまで、処理が少ないもしくはシンプルな機能のViewに関数ベースを使いましょう。

クラスベース

クラスベースの書き方では、ある程度、定型化した書き方があるので、慣れることができればコードの可読性も量もかなりスッキリします。

また、Djangoのもつ様々なデフォルト機能を1行で使えたりするので、プロダクトが複雑になればなるほどその力を発揮します。

細かく話せばできることが無限にあるのですが、基本的な機能は、上記で述べたCRUD処理(作成/取得-一覧/更新/削除)に応じて、それぞれのデフォルト機能を使用できるということ

  • TemplateView 表示(表示のみ)
  • CreateView 作成
  • ListView 一覧(複数のデータ)
  • DetailView 詳細 (一つのデータ)
  • UpdateView 更新
  • DeleteView 削除

DeleteViewは使うケースは少ないので、今回の記事では、
TemplateViewを
次に ListVIew/DetailVIew その次に CreateView/UpdateViewを説明していきます。

では実際にTemplateViewを見ていきましょう。

TemplateView

TemplateViewでは、htmlファイルのパスを指定するだけで処理を完了させることができます。

views.py

from django.views.generic import TemplateView

class IndexView(TemplateView):

    template_name = 'index.html'

index = IndexView.as_view()

たったこれだけで、index.htmlが指定のURLで表示されます。

urls.pyでは、一番下で記述している変数名(index)を指定することで、その処理を受け取ることができます。

urls.py(一部)

path('', views.index, name='index'),

views.index で views.pyで指定した変数を受け取っていますね。

このように、既にデフォルトで提供されている TemplateViewの機能を使用することで、たった数行でページの処理を書くことが可能になります。

これだとページの数が増えても、classごとにページを切り分けられるので可読性も高くなりますし、コード量もスッキリします。

このTemplateView、少しいじれば、DBへアクセスすることもPOSTリクエストを受け取ることも可能です。

views.py

from django.views.generic import TemplateView
from .models import Article

class IndexView(TemplateView):

    template_name = 'index.html'

    # htmlファイルにデータを投げる
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        articles = Article.objects.filter(is_deleted=False)
        context['articles'] = articles
        return context

    # POSTリクエストを受け取る
    def post(self, request, *args, **kwargs):
        title = request.POST['title']
        ...
        return ...

    # ページ読み込み時に処理をしたい場合
    def get(self, request, *args, **kwargs):
        ...
        return super().get(request, **kwargs)

index = IndexView.as_view()

このようにただのTemplateViewでも無限にカスタマイズ可能で、しかも処理を関数ごとにわけることができるので、一度慣れれば、開発スピードも効率も格段に上がります。

TemplateViewについては以下の記事も参考になるので是非一読ください⭐️

Django でまず覚えたい TemplateView のパターン

便利機能一部紹介

  • LoginRequiredMixin
    • Classの引数に入れることで、ログインしていないユーザーを自動でリダイレクトしてくれる
  • UserPassesTextMixin
    • def test_func(self): で記述した処理が Trueを返さない場合、403の権限がありませんエラーを返してくれる

他にも自分で自分で作った Viewクラスを引数に指定することで継承できたりするので、同じ処理を書かなくて済むようになったりします。

また、def get_context_data()は、def get()の中で記述することも可能です。

では、一旦、IndexViewは、最新の3つの記事を表示するだけにしておきましょう。

views.py

from django.views.generic import TemplateView
from .models import Article

class IndexView(TemplateView):

    template_name = 'index.html'

    def get(self, request, *args, **kwargs):
        # getをオーバーライドして、デフォルトのコンテキストを取得
        get = super().get(request,**kwargs)
        context = get.context_data

        # 最新の記事を3つ取得
        latest_articles = Article.objects.filter(is_deleted=False).order_by('-created')[:3]
        context['articles'] = latest_articles
        return get

index = IndexView.as_view()

終わりに

views.pyに慣れていけば、Pythonを用いてできることが格段に増えて、開発できる内容が一気に広がります。

またクラスベースで書くことができれば、複数人での開発になっても、ページ数が多くなってきても、コードが汚くなりづらく、
またケースバイケースで処理を書いていかなければいけなくなるので、Web開発の仕組みが自然とイメージしやすくなってきます。

まずはその基本クラスでもある TemplateViewについて慣れておきましょう!