【Django】ViewクラスでCRUD(Create・Update・Delete)実装|PythonでWebアプリ開発#16

こんにちは、Kosei(@kay_diacc2)です!

本記事はDjangoフレームワークの解説記事であり、「データベースと連携してCRUD操作を行う方法」に焦点を当てて解説します。
本記事読了後は基礎的なCRUD操作に加え、CRUD操作を実現するクラスベースビュー(CreateView, UpdateView, DeleteView, ListView, DetailView)の利用方法に至るまで理解ができるでしょう。

目次

CRUDとは

CRUDとはデータベースを利用するための基本機能を指し、頭文字をとって以下4つに分けられます。

Create テーブル上にレコードを新規作成
Read テーブルからレコードを参照
Update テーブルにある既存レコードの内容更新
Delete テーブルにある既存レコードを削除

上記4機能が実装できると、Webアプリ上からデータベースにアクセスし、自由にレコードが操作できるようになります。

CRUD操作を実現するクラスベースビュー(Create・Update・Delete View)

CRUD機能をWebアプリ上に実装する場合、上図Model、View、Templateをそれぞれ編集します。Modelにはデータベースに登録するテーブル情報を記述し、Viewにはテーブルのレコード操作に関する内容を記述します。最後に、Templateはクライアント側に表示する画面デザインを記述します。

ここでViewに記述するCRUD操作は、関数形式で記述していくとコードが複雑になりがちです。それを解消するためにDjangoにはCRUD操作をシンプルに実現するためのクラスベースビューが準備されています。

ビュークラス 概要 利用画面例
ListView 複数レコードの情報を一覧表示する際に利用 一覧画面
DetailView 単一レコードの詳細情報を表示する際に利用 詳細画面
CreateView 新たにレコード生成する際に利用 新規登録画面
UpdateView 既存レコードの中身を更新する際に利用 編集画面
DeleteView 既存レコード削除する際に利用 削除画面

本記事では上記のクラスベースビューを活用したCRUD機能を持つWebアプリの実装方法を解説します。ここで、クラスベースビューの基本概要を知りたい方はこちらの記事もご覧ください。

Webアプリ実装イメージ

サイトマップ

Webアプリ構築前に、アプリ実装後のイメージを掴みましょう。本記事では上図のようなサイトマップからなるWebアプリを実装します。

DB:テーブル情報

またWebアプリのデータベース内に実装するテーブルは上記を想定します。続いて個々の画面イメージを見ていきましょう。

一覧画面イメージ

一覧画面イメージ

トップページは会社一覧と題して上図のような画面を作成します。ポイントはListViewのクラスと連携し、Companyテーブルのレコードを一覧表示する点です。

詳細画面イメージ

詳細画面イメージ

詳細ページでは、上図のようにCompanyテーブル1レコードに紐づく従業員情報を表示します。ポイントは、DetailViewのクラスと連携し、画面実装する点です。

登録・更新画面イメージ

登録・更新画面イメージ

会社登録および更新画面は上記のようにフォーム形式で実装します。ここでは、CreateViewとUpdateViewと連携するのが特徴です。

削除画面イメージ

削除画面イメージ

最後に削除画面ページは上図イメージで実装します。ポイントはDeleteViewのクラスで実装することであり、上図「削除ボタン」を押すと対象のレコードが削除されるような仕様にします。

【実践】CRUD機能を実装したWebアプリの作成

お待たせしました!それでは実際にプログラミングしていきます。

ディレクトリ構成

ディレクトリ構成は上図に準拠しWebアプリを構築します。各ファイルの利用目的は下記です。

ファイル名 用途
models.py DB連携・テーブル作成。
urls.py 画面表示設定。
views.py 画面表示設定。
Company_list.html 会社一覧画面のデザイン。ListViewと連携。
Company_detail.html 会社詳細画面のデザイン。DetailViewと連携。
Company_form.html 登録・更新画面のデザイン。CreateViewおよびUpdateViewと連携。
Company_delete.html 削除画面のデザイン。DeleteViewと連携。
Header.html グローバルナビなど共通ヘッダー情報のデザイン。

models.py:テーブル作成

CompanyテーブルおよびEmployeeテーブルを作成するために、models.pyにモデルクラスを記述します。

from django.db import models

#会社情報を格納するテーブル
class Company(models.Model):
    name      = models.CharField(max_length=256)
    industory = models.CharField(max_length=256)
    location  = models.CharField(max_length=256)

    def __str__(self):
        return self.name

#会社に紐づく従業員情報を格納するテーブル
class Employee(models.Model):
    name    = models.CharField(max_length=256)
    age     = models.PositiveIntegerField()
    company = models.ForeignKey(Company,related_name='Employees',on_delete=models.CASCADE)

    def __str__(self):
        return self.name

モデルクラス作成後はマイグレーションを行い、データベースに適用しましょう。マイグレーション方法は下記の記事で紹介しています。適時ご参照ください。

urls.py:表示設定

本記事ではurls.pyを下記の仕様で設定します。

  • アプリケーションフォルダ内にurls.pyを新規作成
  • urls.py(プロジェクト)をurls.py(アプリケーション)にinclude関数で紐付け
  • urls.py(アプリケーション)をメインの編集ファイルとして利用

以下実際に記述してみましょう。

プロジェクトフォルダ内のurls.py

from django.contrib import admin
from django.urls import path
from django.urls import include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('App_Folder.urls')),   #(App_Folder)はご自身で作成したアプリケーションフォルダがあればその名前を記載
]

アプリケーションフォルダ内のurls.py

from django.urls import path
from . import views

app_name = "App"

urlpatterns = [
    path('', views.CompanyList.as_view(), name='list'),                         #一覧画面
    path('detail/<int:pk>/',views.CompanyDetail.as_view(),name='detail'),       #詳細画面
    path('create/',views.CompanyCreateView.as_view(),name='create'),            #新規登録画面(会社)
    path('create2/',views.CompanyCreateView2.as_view(),name='create2'),         #新規登録画面(従業員)
    path('update/<int:pk>/',views.CompanyUpdateView.as_view(),name='update'),   #更新画面(会社)
    path('update2/<int:pk>/',views.CompanyUpdateView2.as_view(),name='update2'),#更新画面(従業員)
    path('delete/<int:pk>/',views.CompanyDeleteView.as_view(),name='delete'),   #削除画面(会社)
]

URLパターンの設定方法として留意すべきポイントは下記になります。

  • 今回クラスベースビューを用いるためクラス名の後に.as_view()を記述すること
  • 詳細画面および更新画面は1レコードを参照するため、pathの第一引数はレコードID(PK)と紐付けること

views.py:画面表示

views.pyのコード全量を以下に示します。

from django.shortcuts import render
from django.urls import reverse_lazy
from django.urls import reverse
from django.http import HttpResponse
from django.views.generic import (ListView,
                                  DetailView,
                                  CreateView,
                                  DeleteView,
                                  UpdateView)
from . import models

#一覧画面
class CompanyList(ListView):
    #Companyテーブル連携
    model = models.Company
    #レコード情報をテンプレートに渡すオブジェクト
    context_object_name = "company_list"
    #テンプレートファイル連携
    template_name = "Company_list.html"

#詳細画面
class CompanyDetail(DetailView):
    #Companyテーブル連携
    model = models.Company
    #レコード情報をテンプレートに渡すオブジェクト
    context_object_name = "company_detail"
    #テンプレートファイル連携
    template_name = "Company_detail.html"

#Create(会社)画面
class CompanyCreateView(CreateView):
    #Companyテーブル連携
    model = models.Company
    #入力項目定義
    fields = ("name","industory","location")
    #テンプレートファイル連携
    template_name = "Company_form.html"
    #更新後のリダイレクト先
    def get_success_url(self):
        return reverse('App:detail', kwargs={'pk': self.object.pk})

#Create(従業員)画面
class CompanyCreateView2(CreateView):
    #Companyテーブル連携
    model = models.Employee
    #入力項目定義
    fields = ("name","age","company")
    #テンプレートファイル連携
    template_name = "Company_form.html"
    #作成後のリダイレクト先
    success_url = reverse_lazy("App:list")

#Upadate画面(会社情報)
class CompanyUpdateView(UpdateView):
    #入力項目定義
    fields = ("name","industory","location")
    #Companyテーブル連携
    model = models.Company
    #テンプレートファイル連携
    template_name = "Company_form.html"
    #更新後のリダイレクト先
    def get_success_url(self):
        return reverse('App:detail', kwargs={'pk': self.object.pk})

#更新画面(従業員情報)
class CompanyUpdateView2(UpdateView):
    #入力項目定義
    fields = ("name","age")
    #Employeeテーブル連携
    model = models.Employee
    #テンプレートファイル連携
    template_name = "Company_form.html"
    #更新後のリダイレクト先
    success_url = reverse_lazy("App:list")

#削除画面
class CompanyDeleteView(DeleteView):
    #Companyテーブル連携
    model = models.Company
    #テンプレートファイル連携
    template_name = "Company_delete.html"
    #削除後のリダイレクト先
    success_url = reverse_lazy("App:list")

まず一番のポイントは、クラスベースビューを利用している点です。ListView、DetailView、CreateView、DeleteView、UpadateViewクラスを用いるために、下記コードを記述して親クラスを呼び出しています。

from django.views.generic import (ListView,
                                  DetailView,
                                  CreateView,
                                  DeleteView,
                                  UpdateView)

続いて、各クラスベースビューを見ると、それぞれ似たオブジェクトを活用しているのがわかります。各オブジェクトの役割は下記であり、使いこなせると非常にシンプルなコードで機能実装できるため有用です。

model モデルクラスを指定すると対象のデータベースのテーブルと連携する
context_object_name 指定したオブジェクト名をテンプレートに渡す
template_name 指定したテンプレートファイルをレンダリングする
fields テンプレート側にフォームを設けた際、表示するフィールドを指定
success_url POST処理後のリダイレクト先を指定
get_success_url() POST処理後のリダイレクト先を指定

最後に、CreateView、UpdateView、DeleteViewの特徴を紹介します。後に記述するテンプレート上でPOST処理が実行された場合、各クラスビューは下記の役割を遂行します。

ビュークラス POST処理時の挙動
CreateView 新規レコードを作成する
UpdateView 既存レコードを更新する
DeleteView 既存レコードを削除する

テンプレートファイル作成

最後にテンプレートファイルを作成します。ファイルは下記5つを作成します。今回CSSのデザインはbootstrap5を用いており、内容は割愛します。

ファイル名 用途
Header.html グローバルナビなど共通ヘッダー情報をデザイン
Company_list.html ListViewと連携し、会社一覧画面をデザイン
Company_detail.html DetailViewと連携し、会社詳細画面をデザイン
Company_form.html CreateViewおよびUpdateViewと連携し、登録・更新画面をデザイン
Company_delete.html DeleteViewと連携し、削除画面をデザイン

共通ナビ(Header.html)

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
    <title>CRUD入門</title>

</head>
  <body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
      <div class="navbar-nav">
        <!-- urls.pyで定義したアプリ名 + urlpatternのpathで定めたnameを指定し、リンクで遷移できるようにする -->
        <a class="navbar-brand" href="{% url 'App:list' %}">CompanyList</a>
      </div>
    </nav>

    <!-- Company_list.htmlのHTMLコンテンツを呼び出し -->
    <div class="container">
      {% block content_block %}
      {% endblock %}
    </div>

  </body>
</html>

上記は各サイト共通して用いるヘッダー(主にナビメニュー)に該当するテンプレートです。

{% url ‘App:list’ %}というテンプレートタグは、urls.py(アプリケーション)で記述したapp_name=”App”とpathメソッドの引数name=”list”を指定しています。これでリンクから会社一覧画面に遷移できるようになります。

会社一覧画面(Company_list.html)

{% extends "Header.html" %}

{% block content_block %}
<h2>会社一覧</h2>
<table class="table">
  <thead>
    <tr>
      <th>会社名</th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    {% for company in company_list %}
    <tr>
      <td>{{ company.name }}</td>
      <td><a href="detail/{{ company.id }}">詳細</a></td>
    </tr>
    {% endfor %}
  </tbody>
</table>

<div class="container">
  <p><a class='btn btn-primary' href="{% url 'App:create' %}">新規登録</a></p>
  <br>
</div>
{% endblock %}

上記は会社一覧画面を表すテンプレートです。views.pyにて記述したcontext_object_name(company_list)をテンプレートに渡すことで、Companyテーブルのレコードを一覧表示します。

詳細画面(Company_Detail.html)

{% extends "Header.html" %}

{% block content_block %}
<div class="jumbotron">
  <h2>会社詳細</h2>
  <table class="table">
    <thead>
      <tr>
        <th scope="col">会社</th>
        <th scope="col">業界</th>
        <th scope="col">地域</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>{{ company_detail.name }}</td>
        <td>{{ company_detail.industory }}</td>
        <td>{{ company_detail.location }}</td>
      </tr>
    <tbody>
  </table>
  <div class="container">
    <p><a class='btn btn-primary' href="{% url 'App:update' pk=company_detail.pk %}">更新</a></p>
    <br>
  </div>

  <h2>従業員</h2>
  <table class="table">
    <thead>
      <tr>
        <th scope="col">名前</th>
        <th scope="col">年齢</th>
        <th scope="col">詳細</th>
      </tr>
    </thead>
    <tbody>
      {% for employee in company_detail.Employees.all %}
      <tr>
        <td>{{ employee.name }}</td>
        <td>{{ employee.age }}</td>
        <td><a href="{% url 'App:update2' pk=employee.pk %}">詳細</a></td>
      </tr>
      {% endfor %}
     <tbody>
  </table>
  <div class="container">
    <p><a class='btn btn-primary' href="{% url 'App:create2' %}">新規登録</a></p>
    <br>
  </div>
  <div><a href="{% url 'App:delete' pk=company_detail.pk %}">>>>会社情報を削除する</a></div>
</div>
{% endblock %}

上記は会社詳細画面を表すテンプレートです。記述方法は会社一覧画面と同様にオブジェクトを指定してテーブルレコードを一覧表示しています。

新規登録・更新画面(Company_form.html)

{% extends "Header.html" %}

{% block content_block %}
<h1>
  {% if not form.instance.pk %}
  会社を登録する
  {% else %}
  会社・従業員情報を更新する
  {% endif %}
</h1>
  <form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" class='btn btn-primary' value="登録">
  </form>
{% endblock %}

上記は新規登録画面および更新画面を表すテンプレートです。新規登録画面と更新画面は同一のテンプレートで実装しています。Webアプリを利用するユーザーにとって新規登録画面か更新画面か見分けられるように、下記で対応しているのがポイントです。

  {% if not form.instance.pk %}
  会社を登録する
  {% else %}
  会社・従業員情報を更新する
  {% endif %}

削除画面(Company_delete.html)

{% extends "Header.html" %}

{% block content_block %}
<h1>本当に削除しますか?</h1>
<form method="post">
  {% csrf_token %}
  <input type="submit" class="btn btn-danger" value="削除">
  <a href="{% url 'App:detail' pk=company.pk %} ">キャンセル</a>
</form>
{% endblock %}

最後に削除画面を表すテンプレートを示します。

これでWebアプリは完成です!最後にローカルサーバーを開き、画面が表示できるか確認してみましょう。

Djangoまとめ

最後までご覧いただきありがとうございました。当サイトではDjangoフレームワークを用いた用途別解説記事を多数取り扱っております。是非合わせてご覧ください。

Djangoを学習したい方向けにおすすめできる教材も紹介しています。是非ご覧下さい。

最後に

この記事が気に入ったら
フォローしてね!

本記事をシェア!
URLをコピーする
URLをコピーしました!
目次
閉じる