【Django】DeleteViewの使い方(クラスベースビューでのレコード削除ページの実現)

DeleteViewでのクラスベースビューの実現方法解説ページアイキャッチ

このページでは、Django フレームワークで定義される DeleteView の使い方について説明していきます。

この DeleteViewView というクラスのサブクラスの1つであり、ビューをクラスベースで作成する際に利用するクラスとなります。この View のサブクラス を継承し、さらにクラス変数を定義したりメソッドをオーバーライドすることで、あなたが開発したいアプリに応じたビューを作成することが可能となります。

この辺りのクラスベースビューやクラスベースビューの作り方については下記ページで解説していますので、詳しくはこちらをご参照ください。

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

また、この DeleteView は、View のサブクラス である  DetailView と関連性が高いです。DetailView に関しては下記ページで解説していますので、事前に下記ページを読んでいただくことをオススメします。

DjangoのDetailViewの解説ページアイキャッチ【Django】DetailViewの使い方(クラスベースビューでの詳細ページの実現)

イメージとしては、DeleteView は DetailView に削除機能を追加したような View のサブクラス のサブクラスとなります。この2つのクラスの名前が多少似ているので、これらを混同しないように読み進めていただければと思います。

DeleteView

では、DeleteView について解説していきたいと思います。

DeleteView は、名前の通り、データベースに既に保存されているレコードの削除を行うページを実現する View のサブクラス となります。

DeleteViewdjango.views.generic から import して利用します。

DeleteViewのimport
from django.views.generic import DeleteView

例えば、会員退会ページ、投稿済みのコメントの削除ページといった、データベースに保存されているレコードを削除するためのページを実現するのが DeleteView となります。

DeleteViewで実現できるページの例

削除が行われるのは POST リクエスト時

この DeleteView では POST リクエストを受け取った際にデータベースからのインスタンス(レコード)の削除が行われるようになっています。

もう少し具体的に説明すると、DeleteView では、POST リクエストを受け取った際に URL で指定される「プライマリーキー」や「スラッグ:を持つインスタンスを1つデータベースから取得します。ここで取得されるのが削除対象のインスタンスとなります。そして、そのインスタンスに delete メソッドを実行させることで、そのインスタンスをデータベースから削除するようになっています。

また、図では省略していますが、インスタンスの削除に成功した際には他のページへのリダイレクトが行われることになります。

POSTリクエスト時にデータベースからのインスタンスの削除が行われる様子

この削除対象のインスタンスの取得は DeleteViewget_object というメソッドによって行われます。この get_object メソッドは DetailView も持っており、両方で全く同じメソッドとなっています。なので、DeleteView がリクエストを受け取った際にデータベースから1つのインスタンスの取得を行う仕組みに関しては DeleteView と同じで、その仕組みに関しては下記ページで解説していますので、詳細を知りたい方は下記ページを参照していただければと思います。

DjangoのDetailViewの解説ページアイキャッチ【Django】DetailViewの使い方(クラスベースビューでの詳細ページの実現)

ここで重要なのは、DeleteView では POST リクエストを受け取った際に目的のデータベースからのインスタンスの削除が行われるという点になります。

スポンサーリンク

GET リクエスト時には削除確認のページを表示

では、DeleteView が GET リクエストを受け取った際には何が行われるのでしょうか?

GET リクエストを受け取った際には、DeleteView では削除確認用のページの表示が行われるようになっています。あなたもウェブアプリを利用していて何かの削除や退会の申請をしようとした際に「本当に削除しても良いですか?」「本当に退会しますか?」のようなメッセージが表示された経験があるのではないかと思います。こういった削除確認用のページの表示を行うのが、GET リクエストを受け取った際の DeleteView の動作となります。

DeleteViewのGETリクエスト時の動作を説明する図

で、この時には削除対象となっているインスタンスの詳細情報を表示することが多いです。削除対象となっているインスタンスの情報を表示した上で、ユーザーに削除の確認を行います。

そのため、DeleteView では GET リクエストを受け取った際にも get_object メソッドによりデータベースから削除対象となるインスタンスを取得するようになっています。そして、そのインスタンスの詳細情報をページとして表示します。で、この時の動作は基本的には DetailView と同じです。なので、DeleteView を理解する上では DetailView についてまず理解しておいた方が良いですし、DetailView が使いこなせれば DeleteView も簡単に使いこなすことができます。

ただ、DetailView では単にデータベースから取得したインスタンスの情報を表示するだけでしたが、DetailView の場合は、データベースから取得したインスタンスの情報を表示するだけでなく、削除を実行するためのボタン等をページに表示する必要があります。

削除確認用ページにボタンが必要であることを示す図

そして、このボタンが押された際には DeleteView に対して POST リクエストが送信されるようにテンプレートファイルや urls.py を準備しておく必要があります。

こうしておけば、まず GET リクエストを受け取った DeleteView が削除対象のインスタンスの情報を表示するためのページの HTML をレスポンスとしてクライアントに返却し、さらにボタン等で削除の実行が行われた際には DeleteViewPOST リクエストを受け取って実際にデータベースからインスタンスの削除を行うような流れを実現することができるようになります。

GETリクエスト送信からPOSTリクエスト送信の流れ

もちろん、削除前のユーザーへの削除の確認が不要というのであれば、直接 DeleteView に POST リクエストを送信するようなアプリのページ構成にするようなことも可能です。例えば、インスタンスの一覧ページに削除ボタンを設け、このボタンが押された時に DeleteView に対して POST リクエストが送信されるようにしてやれば、ユーザーへの削除の確認を行わずに直でデータベースからのインスタンスの削除を行うことも可能です。

直接POSTリクエストが送信される様子

ただ、ユーザーが間違って削除してしまう可能性もあるので、削除確認用のページは表示した方が良いと思います。いずれにせよ、DeleteView では GET リクエストを受け取った際に削除確認用のページを表示し、POST リクエストを受け取った際にデータベースからのインスタンスの削除が行われるということは覚えておきましょう!

DeleteView でのクラスベースビューの作り方

では、次は DeleteView でクラスベースビューを作成する手順について説明していきます。

DeleteView でのクラスベースビューの作り方に関しても他の View のサブクラス とほぼと同様となります。

要は、views.py に DeleteView を継承するクラス(サブクラス)を定義し、views.py に定義したクラスにクラス変数やメソッドを定義することで DeleteView で定義されているクラス変数の上書きメソッドのオーバーライドを行なっていきます。これにより、DeleteView の特徴を活かしながら自身のウェブアプリに応じたビューにカスタマイズしていくことが可能となります。

クラスベースビューの作り方の説明図

また、DeleteView では GET リクエスト受けとった際に get メソッドが、POST リクエストを受け取った際に  post メソッドが実行されるようになっています。そして、前述のように、これらのメソッドが実行された際に、削除確認ページが表示されたりデータベースからのインスタンスの削除が行われるようになっています。

そして、DeleteView のクラス変数や他のメソッドは、これらの get メソッドおよび post メソッドから利用されるようになっています。したがって、DeleteView のサブクラス側でクラス変数やメソッドを上書き・オーバーライドしてやれば、この get メソッドと post メソッドの動作を変化させることができます。

DeleteViewのgetメソッドやpostメソッドからクラス変数や他のメソッドが利用される様子

削除対象のインスタンスの情報を表示する

また、前述の通り、GET リクエストを受け取った際に実行される get メソッドでは、ユーザーに「本当に削除を実行しても良いのか」を判断させるための情報を表示すること、および、削除を実行するためのボタン等を表示することが重要となります。そして、そのボタン等がクリックされた際には DeleteView のサブクラスに POST リクエストを送信することが必要です。

また、ユーザーに「本当に削除しても良いのか」を判断してもらうためには、削除対象のインスタンスの情報を表示することが必要です。これに関しては DetailView と同じ仕組みで表示することが可能で、DetailView に関しては下記ページで解説していますので詳細はこちらを参照していただければと思います。

DjangoのDetailViewの解説ページアイキャッチ【Django】DetailViewの使い方(クラスベースビューでの詳細ページの実現)

ポイントは、”削除対象のインスタンスを特定するための情報を指定できるように urls.py を作成” し、”その情報を受け取れるように DeleteView のサブクラスでクラス変数を定義” し、さらには “削除対象のインスタンスの情報を表示できるようにテンプレートファイルを作成” するという点になります。

要は、ビューとビュー以外の部分で話が合うように、それぞれのファイルを実装していく必要があります。この「ビューとビュー以外の部分で話が合うように実装する」という点はクラスベースでビューを作成していく上で重要なポイントになりますので、ここはしっかり意識しながら開発を進めるようにしていきましょう!

スポンサーリンク

削除実行ボタンを表示する

また、POST リクエストを送信するボタンの作り方に関しては下記のフォームの解説ページで詳細を説明しています。

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

要は、テンプレートファイルで <form></form> の間にボタン要素となるタグを記述し、form タグの action 属性に DeleteView のサブクラスに紐付けられた URL を指定し、さらに form タグの method 属性に post を指定してやれば良いだけです。例えば DeleteView のサブクラスから利用されるテンプレートファイルに下記のようなタグを記述しておけば、クリックすると DeleteView のサブクラスに POST リクエストを送信するボタンがページに表示されるようになります。

削除ボタン
<form action="{% url 'delete' object.id %}" method="post">
    {% csrf_token %}
    <p><input type="submit" value="削除"></p>
</form>

上記は url.pyDeleteView のサブクラスと 'delete' という名前が関連付けられていることを前提としたものとなっている点に注意してください。

また、 object は削除対象となるインスタンスを参照するための変数です。DeleteView が生成するコンテキストには 'object' キーにデータベースから取得したインスタンスがセットされるようになっています。この辺りに関しては後述の DeleteView が生成するコンテキスト で詳細を説明します。

こんな感じで、GET リクエストを受け取った際には削除対象のインスタンスを取得して表示することと、削除実行ボタンを表示することが重要になります。そして、前者に関しては DetailView と同様の手順で実現可能です。また、後者に関しては通常のフォームを扱う時と同様にテンプレートファイルを用意しておくことが必要となります。ただし、フォームに入力フィールドなどを用意することは必須ではありません。逆に言えば、入力フィールドを表示することも可能です。これに関しては後述の form_class で解説します。

リダイレクト先を設定する

で、上記のようなボタンを用意しておけば、ユーザーがインスタンスを削除したいと判断した際にボタンがクリックされ、DeleteView のサブクラスに POST リクエストが送信されることになります。そして、これにより DeleteView の post メソッドが実行され、データベースからのインスタンスの削除が行われることになります。

データベースからのインスタンスの削除が行われた後には、CreateViewUpdateView と同様に、他の URL へのリダイレクトが行われることになります。そのため、DeleteView のサブクラスではリダイレクト先の URL を設定しておくことが必要になります。

このリダイレクト先の URL の設定に関しては下記の CreateView の解説ページで詳細を説明していますので、詳しくは下記ページをご参照いただければと思います。

CreateViewでのクラスベースビューの実現方法解説ページアイキャッチ【Django】CreateViewの使い方(クラスベースビューでの新規登録ページの実現)

DeleteView の場合は、クラス変数 success_url の定義によってリダイレクト先の URL を指定することが多いと思います。

DeleteView のクラス変数

では、次は DeleteView で定義されているクラス変数の紹介を行なっていきます。先ほど触れた success_url も DeleteView で定義されているクラス変数の1つで、これらのクラス変数を DeleteView を継承するクラスで定義し直して上書きすることでクラスの動作のカスタマイズを行うことが可能となります。

DeleteView の場合、GET メソッドのリクエストを受け取った際に get メソッドが実行され、GET メソッドのリクエストを受け取った際に post メソッドが実行されることになるため、これらの get メソッドや post メソッドの動作を変化させることを目的にクラス変数の定義を行なっていくことになります。

ただ、ここまでにも説明したように、DeleteView は削除対象となるインスタンスの表示や、削除対象となるインスタンスのデータベースからの取得は DetailView と同様の仕組みで実現されており、その中で DetailView でも定義されているクラス変数が利用されるようになっています。また、DeleteView ではフォームを扱うこともでき、このフォームを扱う仕組みは CreateView と同様です。そして、この際には CreateView でも同様に定義されているクラス変数が利用されます。 

で、DetailViewCreateView で定義されているクラス変数に関しては、これらの解説ページで既に説明済みです。したがって、DetailViewCreateView と全く同じ意味合いとなるクラス変数に関しては、下記ページにおける、それらのクラス変数の解説を行なっている節へのリンクのみを紹介するようにしたいと思います。この点はご了承ください。

DjangoのDetailViewの解説ページアイキャッチ【Django】DetailViewの使い方(クラスベースビューでの詳細ページの実現) CreateViewでのクラスベースビューの実現方法解説ページアイキャッチ【Django】CreateViewの使い方(クラスベースビューでの新規登録ページの実現)

スポンサーリンク

DeleteView のクラス変数の一覧

この DeleteView で定義されるクラス変数には下記のようなものが存在します。

各リンクをクリックすれば括弧内に示した View のサブクラス の解説ページにジャンプするようにしています。基本的に、フォームを扱うための設定を行うクラス変数に関しては CreateView の解説ページ、インスタンスを1つのみ取得して表示を行うための設定を行うクラス変数に関しては DetailView の解説ページ、その他のクラス変数に関しては ListView の解説ページにジャンプするようにしています。

ここでは、form_classtemplate_name_suffix のみの説明を行なっていきます。これらも CreateViewDetailView で定義されているクラス変数ではあるのですが、役割や初期値が異なるため、別途説明させていただくことにしています。

form_class

form_classDeleteView で扱うフォームクラスを指定するクラス変数となります。このクラス変数自体は CreateViewUpdateView にも存在するのですが、少し役割が異なります。

CreateViewUpdateView はデータベースにインスタンスを保存するための View のサブクラス であり、その保存対象となるインスタンスの情報の入力受付を行うためのフォームクラスを指定するために form_class を定義する必要があります。そして、それらのフィールドにユーザーが入力した情報に基づいてインスタンスがデータベースに保存されることになります。

CreateView等でフォームから送信されてきたデータに基づいたインスタンスがデータベースに保存される様子

ですが、DeleteView の場合、同様の目的で form_class の定義を行うことは無意味です。なぜなら、同様の目的で form_class の定義をしてデータベースにインスタンスを保存するための情報の入力受付を行なったとしても、その情報を DeleteView のサブクラスが受け取った際に、そのインスタンスがデータベースから削除されることになるためです。

DeleteView等でフォームからデータが送信されたとしても結局インスタンスがデータベースから削除されてしまう様子

 

なので、削除対象となるインスタンスの情報の入力を受け付けるのではなく、別の目的の入力受付を行うために form_class の定義を行うことになります。例えば私が思いつくのは「削除の理由の入力受付」です。

データベースからのインスタンスの削除を行う例の代表の1つはユーザーの退会になります。ボタンのクリック等で単に退会を受け付けるだけでも良いのですが、削除確認ページに入力フィールドを用意し、そこに退会理由などを記入してもらうようにして貰えばデータベースにユーザーの退会理由などを蓄積していくことができるようになります。そして、その理由を参考にしてアプリ等の運営の仕方の改善などを図ることができるようになります。

このように、DeleteView のサブクラスではフォームを扱うことができ、そのフォームは削除対象のインスタンスの情報を入力受付を行うのではなく、他の目的を実現するために扱うことは覚えておくと良いと思います。

実装例に関しては、DeleteView の利用例 で紹介したいと思います。

template_name_suffix

template_name_suffix に関してはクラス変数の意味合いとしては他のクラス同様です。クラス変数 template_name を定義しなかったときに View のサブクラス が利用するテンプレートファイルのデフォルトのパスにおけるサフィックス(末尾側の文字列)を指定するクラス変数となります。

DeleteView においては、この template_name_suffix のデフォルトは 'confirm_delete' となります。そして、template_name が定義されていない場合のテンプレートファイルのパスはクラス変数 model に指定したモデルクラスの モデルクラス名 (小文字) とを組み合わせて下記のように決まります。そして、このテンプレートファイルが利用されて GET リクエスト受信時には削除確認用のページ表示が行われることになります。

モデルクラス名_confirm_delete.html

このテンプレートファイルのパスからも、DeleteView  では GET リクエストを受け取った際には削除確認用のページが表示される作りになっていることが確認できると思います。

スポンサーリンク

DeleteView のメソッド

次に DeleteView の持つメソッドを紹介していきます。DeleteView を継承したクラスを定義し、そのクラスで DeleteView の持つメソッドをオーバーライドしてやることで DeleteView とは異なる動作のクラスを実現することができるようになります。

DeleteView のメソッド一覧

DeleteView の持つメソッドの一覧は下記のようになります。あくまでもクラスのカスタマイズ目的でオーバーライドを行う可能性のあるものを挙げており、as_view などのカスタマイズは行わないであろうメソッドは省略しています。

また、DeleteViewget メソッドと post メソッドを持っており、メソッドが GET のリクエストを受け取った場合は get が実行され、メソッドが POST のリクエストを受け取った場合は post が実行されようになっています。そして、これらの getpost の中から各種メソッドが実行されるようになっています。

  • get:リクエストのメソッドが GET の場合の処理を実行する
  • post:リクエストのメソッドが POST の場合の処理を実行する
  • get_context_data:テンプレートに渡すコンテキストを生成する
  • get_form:フォームクラスのインスタンスを取得する
  • get_form_class:フォームクラスを取得する
  • get_form_kwargs:フォームクラスのコンストラクタに指定するパラメータを取得する
  • get_initialinitial への指定値を取得する
  • get_prefixprefix への指定値を取得する
  • form_valid:データベースに保存してあるレコード・インスタンスを削除し、特定の URL へのリダイレクトレスポンスを返却する(妥当性の検証が OK の時に実行される)
  • form_invalid:再度フォームを表示する(妥当性の検証が NG の時に実行される)
  • get_success_url:リダイレクト先の URL を取得する
  • get_template_names:テンプレートファイルの名前を取得する
  • render_to_response:レスポンスを返却する
  • get_context_object_namecontext_object_name への指定値を取得する
  • get_queryset:インスタンスの集合(クエリセット)を取得する
  • get_object:インスタンスを取得する
  • get_slug_fieldslug_field を取得する

オーバーライドする機会の多いメソッドは form_valid になると思います。CreateViewUpdateView の場合は get_success_url もオーバーライドする機会が多いですが、これは新規登録したインスタンスや更新したインスタンスに応じてリダイレクト先の URL を動的に変化させたいケースが多いためです。DeleteView の場合、削除したインスタンスの情報を表示するようなことは少ないため、動的にリダイレクト先の URL を変化させる機会は少ないかもしれません。それでも get_success_url をオーバーライドしたい場合は、CreateView の解説ページで get _success_url のオーバーライド例を紹介していますので、下記ページを参照していただければと思います。

CreateViewでのクラスベースビューの実現方法解説ページアイキャッチ【Django】CreateViewの使い方(クラスベースビューでの新規登録ページの実現)

form_valid

ここでは form_valid のオーバーライドについてのみ説明を行いたいと思います。

form_valid は先程触れた get_success_url 同様に CreateViewUpdateView でも定義されているクラス変数になります。そして、これも CreateViewUpdateView 同様に、フォームから送信されてきたデータの妥当性の検証結果が OK の場合に実行されるメソッドになります。

MEMO

CreateViewUpdateView の場合は、これらのビューの中で利用されるフォームクラスを指定するクラス変数(form_class or model + fields)の定義が必須ですが、DeleteView の場合は必須ではなく定義は任意となります

これらのクラス変数が定義されていない場合、DeleteView では利用されるフォームクラスが Form というクラスに自動的に設定されるようになっています

そして、この場合は必ず妥当性の検証結果が OK となります

ただし、メソッド内部で行われる処理が若干異なるので注意が必要です。

DeleteViewform_valid で行われるのは下記の3つの処理のみとなります。2つ目の処理によって目的のデータベースからのインスタンスの削除が行われることになります。

  • リダイレクト先の URL の取得
  • データベースからのインスタンスの削除
  • 取得した URL へのリダイレクトレスポンスの返却

インスタンスの削除を行うだけでれば上記の処理のみで問題ないです。ですが、form_class で説明したように DeleteView でもフォームを扱うことができ、フォームから送信されてきたデータをデータベースにインスタンス(レコード)として保存したいような場合は、上記の処理しか行われない form_valid では不十分ということになります。こういった、妥当性の検証結果が OK の場合に実行したい処理が DeleteViewform_valid では不十分である場合は、DeleteView のサブクラス側で form_valid を定義してオーバーライドする必要があります。

オーバーライドのやり方自体は下記ページでも解説していますので、ここでの詳細な説明は省略しますが、DeleteView の利用例 ではフォームから送信してきたデータをデータベースにインスタンスとして保存する例を示したいと思います。

CreateViewでのクラスベースビューの実現方法解説ページアイキャッチ【Django】CreateViewの使い方(クラスベースビューでの新規登録ページの実現)

DeleteView が生成するコンテキスト

続いて DeleteView が生成するコンテキストについて説明しておきます。

DeleteView の場合、コンテキストは GET リクエストを受け取った際に削除確認用のページを表示するための HTML を生成する際と、POST リクエストを受け取ったものの、送信されてきたデータの妥当性の検証結果が NG の場合に、再度削除確認用ページを表示するための HTML を生成する際に利用されることになります。

そして、DeleteView が生成するコンテキストには下記の要素が含まれることになります。上側の2つに関しては DetailView が生成するコンテキストにも含まれるキーとなり、それに 'form' が追加された形のコンテキストが生成されることになります。

  • 'object' : データベースから取得されたインスタンス
  • context_object_name : データベースから取得されたインスタンス
  • 'form' : フォームクラスのインスタンス

また、context_object_name に関しては DetailView の解説ページの context_object_name で説明したようにクラス変数の定義で変更することも可能ですし、デフォルトは モデルクラス名 (小文字) となっています。モデルクラス名が Comment であれば 'comment' となります。

コンテキストには 'form' が含まれることになりますが、基本的にテンプレートファイルから form を参照するのは、DeleteView のサブクラスでクラス変数 form_class を定義している場合のみとなります。form_class を定義しない場合は Django フレームワークで定義される Formというクラスのインスタンスがコンテキストの 'form' キーにセットされることになりますが、このクラスではフィールドが定義されていないため、テンプレートファイルで form を出力してもページとしては何も表示されません。

上記の3つ以外のデータをコンテキストにセットしてテンプレートファイルから参照できるようにしたい場合は、ListView の解説ページの extra_context で説明しているクラス変数 extra_context を定義したり、get_context_data のオーバーライドを行なったりしてコンテキストの要素を追加する必要があります。

スポンサーリンク

DeleteView の利用例

最後に、ここまでの説明のまとめとして、DeleteView を継承するクラスでのビューの作成例を示していきたいと思います。

プロジェクトとアプリの作成の手順に関しては下記ページの ListView の利用例 と同じとなりますので、ここでは説明を省略させていただきます。ListView の解説ページの利用例を読んでいない方は、下記ページの プロジェクトとアプリの作成 を読んで手順を進めていただければと思います。

DjangoのListViewの解説ページアイキャッチ【Django】ListViewの使い方(クラスベースビューでの一覧リストページの実現)

モデルの作成とマイグレーション

プロジェクトとアプリの作成の次は、models.py を変更してモデルクラスの定義を行なっていきます。

今回は、下記のように forum フォルダ内の models.py を変更してモデルクラスの定義を行いたいと思います。

models.py
from django.db import models

class Comment(models.Model):
    text = models.CharField(max_length=256)

class Reason(models.Model):
    text = models.CharField(max_length=256)

Comment は掲示板アプリ等に投稿されたコメントを管理するテーブル、Reason はコメントの削除理由を管理するテーブルとして扱っていきます。このアプリでの削除対象は Comment のインスタンスとなります。

models.py の変更後は、models.py に定義したモデルクラスに対応するテーブルをデータベースに作成します。テーブルは下記の2つのコマンドでマイグレーションを実施することで作成することができます。

% python manage.py makemigrations
% python manage.py migrate

フォームの定義

続いてフォームクラスの定義を行なっていきます。

まず、forum フォルダの下に forms.py というファイルを新規作成し、その forms.py の中身を下記のように変更して保存してください。

forms.py
from django import forms
from .models import Comment, Reason

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['text', ]

class ReasonForm(forms.ModelForm):
    class Meta:
        model = Reason
        fields = ['text', ]

今回は2つのフォームクラスの定義を行なっています。1つ目が CommentForm で、このフォームは DeleteView ではなく CreateView のサブクラスで利用するフォームであり、コメントの新規投稿フォームの役割を果たすフォームとなります。

2つ目が ReasonForm で、DeleteView のサブクラスで本フォームを利用し、コメント削除時に削除理由を入力できるようにするためのフォームとなります。

スポンサーリンク

クラスベースビューの作成

続いて、このページの主題ともなっているクラスベースビューを作成していきます。今回は、ここまでの説明の通り DeleteView を継承するクラスを定義し、そのクラスをクラス変数の定義とメソッドのオーバーライドによってカスタマイズしていきます。

また、DeleteView はデータベースからのインスタンスを削除するビューとなりますので、事前に削除対象となるインスタンスをデータベースに新規登録するために CreateView のサブクラスの定義も行います。さらに、CommentReason のインスタンスの一覧を表示できるよう、ListView のサブクラスの定義も行っていきたいと思います。

これらを定義した views.py の例は下記のようになります。

views.py
from django.views.generic import  ListView, CreateView, DeleteView
from .models import Comment, Reason
from .forms import CommentForm, ReasonForm
from django.urls import reverse_lazy

class CommentDelete(DeleteView):
    model = Comment
    form_class = ReasonForm
    pk_url_kwarg = 'comment_id'
    success_url = reverse_lazy('comments')

    def form_valid(self, form):
        form.save()
        return super().form_valid(form)

class CommentCreate(CreateView):
    form_class = CommentForm
    model = Comment
    success_url = reverse_lazy('comments')

class CommentList(ListView):
    model = Comment

class ReasonList(ListView):
    model = Reason

この views.py においては4つのクラスを定義しています。

まず、CommentListReasonList はそれぞれ CommentReason のインスタンス一覧の表示を実現する ListViewのサブクラスとなります。ListView に関しては下記ページで解説していますので、詳細に関しては下記ページを参照してください。

DjangoのListViewの解説ページアイキャッチ【Django】ListViewの使い方(クラスベースビューでの一覧リストページの実現)

また、 CommentCreate はコメントの新規投稿(Comment のインスタンスのデータベースへの新規登録)を実現する CreateView のサブクラスとなります。CreateView に関しては下記ページで解説していますので、詳細に関しては下記ページを参照してください。

CreateViewでのクラスベースビューの実現方法解説ページアイキャッチ【Django】CreateViewの使い方(クラスベースビューでの新規登録ページの実現)

そして、CommentDelete はデータベースからのインスタンスの削除を実現するクラスとなります。この CommentDelete こそが、このページの主題ともなっている DeleteView のサブクラスとなります。

クラス変数 modelComment を指定しているため、このクラスで削除するインスタンスは Comment クラスのインスタンスとなります。また、削除確認ページに詳細を表示するインスタンスも Comment クラスのインスタンスとなります。

pk_url_kwarg = 'comment_id' を定義しているため、この CommentDelete は、削除対象のインスタンスのプライマリーキーが URL で指定され、そのプライマリーキーを  'comment_id' という変数名の変数で受け取ることになります。そして、GET リクエストや POST リクエストを受け取った際には、comment_id で指定されたプライマリキーを持つ Comment のインスタンスの取得が行われることになります。この辺りは下記ページの pk_url_kwarg の節で解説していますので、詳しくは pk_url_kwarg  を参照していただければと思います。

DjangoのDetailViewの解説ページアイキャッチ【Django】DetailViewの使い方(クラスベースビューでの詳細ページの実現)

また、クラス変数 form_class を定義して ReasonForm を指定しているため、コメントの削除理由を入力するフィールドを持つ ReasonForm のインスタンスがテンプレートファイルから参照可能となっています。後述で説明するテンプレートファイルでは、このフォームを表示してユーザーからのコメントの削除理由の入力受付を行うようにしていきます。

さらに、CommentDelete では form_valid メソッドのオーバーライドを行なっており、この form_valid によって、削除確認用ページから送信されてきたデータの妥当性の検証結果が OK の場合に送信されてきたデータを ReasonForm のベースとなっているモデルクラスのインスタンス、つまり Reason のインスタンスがレコードとしてデータベースに保存されることになります。

このように、DeleteView のサブクラスでは単に削除確認用のページを表示するだけでなく、form_class を定義することでユーザーからの入力受付を行うことも可能となります。そして、form_valid のオーバーライドによって、それをデータベースに保存したり、さらには削除時にメールを送信するようなことも可能となります。

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

次は views.py で定義したビューと URL とのマッピングを行なっていきます。

まず、classviewproject フォルダの下にある urls.py を下記のように変更してください。

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

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

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

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

urlpatterns = [
    path('delete/<int:comment_id>/', views.CommentDelete.as_view(), name='delete'),
    path('create/', views.CommentCreate.as_view(), name='create'),
    path('comments/', views.CommentList.as_view(), name='comments'),
    path('reasons/', views.ReasonList.as_view()),
]

ポイントは urlpatterns の第1要素になります。

上記のように要素を指定しておくことで、ルートパス指定で /forum/delete/<int:comment_id>/ へのリクエストがあった際に CommentDelete が動作するようになります。この <int:comment_id> 部分には、削除対象となるインスタンスに割り当てられたプライマリーキーを指定する必要があります。そして、CommentDelete では、ここで指定されたプライマリーキーが割り当てられているインスタンスがデータベースから取得され、そのインスタンスの表示や削除が行われることになります。

テンプレートの作成

次はテンプレートファイルを作成していきます。ソースコードの変更としては、これが最後となります。

今回 views.py で定義した各クラスではクラス変数 template_name を定義していないため、各クラスのデフォルト設定のパスに存在するテンプレートファイルが各クラスで利用されることになります。

具体的に言うと、各クラスで利用されるテンプレートファイルは下記のパスのものとなります。なので、下記のパスにテンプレートファイルを用意していくことになります。

  • CommentDelete'forum/comment_confirm_delete.html'
  • CommentCreate'forum/comment_form.html'
  • CommentList'forum/comment_list.html'
  • ReasonList'forum/reason_list.html'

これらのパスは、アプリフォルダの templates フォルダから見た相対パスとなりますので、まずはアプリフォルダ(forum フォルダ)の中に templates フォルダを作成し、さらにその templates フォルダの中に forum フォルダを作成しておきましょう。この最後に作成した forum フォルダの中に、各クラスが利用するテンプレートファイルを1つ1つ作成していきます。

comment_confirm_delete.html

まずは、CommentDelete から利用される comment_confirm_delete.html を作成します。

先程作成した forum フォルダの中に comment_confirm_delete.html を新規作成し、中身を下記のように変更して保存してください。

comment_confirm_delete.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>Example of DeleteView</title>
</head>
<body>
    <main>
        <h2>コメントの削除</h2>
        <p>下記のコメントを削除してもよろしいですか?</p>
        <table>
            <tbody>
                <tr><td>ID   :</td><td>{{ comment.id }}</td></tr>
                <tr><td>TEXT :</td><td>{{ comment.text }}</td></tr>
            </tbody>
        </table>
        <p>削除理由を教えてください</p>
        <form action="{% url 'delete' object.id %}" method="post">
            {% csrf_token %}
            <table>{{ form.as_table }}</table>
            <p><input type="submit" value="削除"></p>
        </form>
    </main>
</body>
</html>

このテンプレートファイルは、CommentDeleteGET リクエストを受け取った際 or POST リクエストを受け取ったものの送信されてきたデータの妥当性の検証結果が NG の際に利用されるテンプレートファイルとなります。

このテンプレートファイルは削除対象のインスタンスの情報を表示するとともに、ユーザーからの削除実行を受け付けるための HTML を生成するために利用されます。そして、これらが実現できるように、このテンプレートファイルではコンテキストとして受け取った comment の情報の表示(データ属性 idtext の出力)とボタン要素の表示を行うようにしています。

また、CommentDelete では class_form に  ReasonForm を指定しているため、テンプレートファイルからは変数名 form で ReasonForm のインスタンスを参照することができ、これを {{ form.as_table }} で出力することで ReasonForm の持つフィールド、すなわち削除理由の入力フィールドをページに表示しています。

このように、ユーザーに削除対象のインスタンスの情報示すだけでなく、削除を実行するためのボタン、さらには必要に応じてフォームの表示を行うようにテンプレートファイルを用意しておくと言う点が、DeleteView のサブクラスを使いこなす上でのポイントの1つとなります。

comment_form.html

続いて他のテンプレートファイルを作成していきます。

CommentCreate から利用される comment_form.html には下記のようなものを用意したいと思います。単純にフォームを表示するだけのテンプレートファイルであり、このテンプレートファイルから参照する formCommentForm のインスタンスとなり、comment_confirm_delete.html から参照する form とは異なるものであることに注意してください。

comment_form.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>Example of CreateView</title>
</head>
<body>
    <main>
        <h2>コメントの投稿</h2>
        <form action="{% url 'create' %}" method="post">
            {% csrf_token %}
            <table>{{ form.as_table }}</table>
            <p><input type="submit" value="送信"></p>
        </form>
    </main>
</body>
</html>

comment_list.html

また、CommentList から利用される comment_list.html には下記のようなものを用意したいと思います。基本的には CommentList のクラス変数 model で指定されるモデルクラスのインスタンスの一覧を表示するためのテンプレートファイルとなりますが、各インスタンスの右側には 削除 リンクを表示するようにしています。そして、この 削除 リンクをユーザーがクリックすれば、{% url 'delete' object.id %} によって解決される URL、すなわち、そのインスタンスの削除確認用のページが表示されるようにしています(Comment クラスにおいて、object.id はインスタンスのプライマリーキーとなります)。

comment_list.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>Example of ListView</title>
</head>
<body>
    <main>
        <h2>コメント一覧</h2>
        <table>
            <thead>
                <tr>
                    <th>ID</th><th>text</th>
                </tr>
            </thead>
            <tbody>
                {% for object in object_list %}
                <tr>
                    <td>{{ object.id }}</td>
                    <td>{{ object.text }}</td>
                    <td><a href="{% url 'delete' object.id %}">削除</a></td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </main>
</body>
</html>

reason_list.html

最後に、ReasonList から利用される reason_list.html を用意していきます。この reason_list.html は下記のようなものにしたいと思います。単純に、ReasonList のクラス変数 model で指定されるモデルクラスのインスタンスの一覧を表示するだけのテンプレートファイルになっています。。

reason_list.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>Example of ListView</title>
</head>
<body>
    <main>
        <h2>削除理由一覧</h2>
        <table>
            <thead>
                <tr>
                    <th>ID</th><th>text</th>
                </tr>
            </thead>
            <tbody>
                {% for object in object_list %}
                <tr>
                    <td>{{ object.id }}</td>
                    <td>{{ object.text }}</td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </main>
</body>
</html>

スポンサーリンク

動作確認

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

今回は Comment のインスタンスの削除が正常に行えることを確認することが動作確認の目的となりますが、まずはその前にインスタンスのデータベースへの新規登録を行う必要があります。そのため、インスタンスのデータベースへの新規登録を行なったとに削除の動作確認を行なっていきます。

最初に、manage.py が存在するフォルダで下記コマンドを実行します。

% python manage.py runserver

これにより、開発用ウェブサーバーが起動しますので、ウェブブラウザから作成したアプリのページを表示することが可能となります。

次に下記 URL をウェブブラウザで表示してみましょう!

http://localhost:8000/forum/create/

この URL をウェブブラウザで表示すれば下図のような text の入力フィールドを持つフォームが表示されるはずです。これは CommentCreate によって表示されるコメントの投稿フォームとなります。

CommentCreateによって表示されるページ

表示されたフォームで適当な半角英数字の文字列を text に入力して 送信 ボタンを押せば、これらのフィールドに入力されたデータが対応するフィールドにセットされ、さらに id(プライマリーキー) が自動的にセットされた Comment のインスタンスがデータベースに新規登録されることになります。

さらにデータベースへの新規登録後に urls.py によって名前が 'comments' である URL にリダイレクトが行われ、今度は CommentList が動作してコメントの一覧が表示されるはずです。といっても表示されるコメントは先程新規登録したコメント1つのみになると思います。先程のコメントの新規投稿フォームを再度表示してコメントの投稿を新たに行うことで、コメント一覧に表示されるコメントの数が増えていくことも確認することができるはずです。

CommentListによって表示されるページ

次は、コメント一覧から削除したコメントの右側にある 削除 リンクをクリックしてみましょう!これにより、GET リクエストを DeleteView が受け取り、下の図のようなコメントの削除確認用のページが表示されるはずです。

CommentDeleteによって表示されるページ

このページでは ReasonForm のインスタンスの出力によって ReasonForm のインスタンスの持つフィールド(text)が表示されていることが確認できると思います。そこに適当な理由を入力して 削除 ボタンをクリックしてみましょう!

このボタンのクリックによって今度は POST リクエストを DeleteView が受け取り、まずはフォームから送信されたデータが Reason のインスタンスとしてデータベースに新規登録されることになります。続いて先程削除確認用のページに表示されたインスタンスがデータベースから削除され、さらにリダイレクトによって再びコメント一覧ページが表示されることになります。

そして、そのコメント一覧ページからは先程削除したインスタンスの表示がなくなっていることが確認できると思います。これにより、今回定義した CommentDelete によってデータベースからのインスタンスの削除が実現できていることが確認できたことになります!

コメント一覧からコメントが削除された様子

さらに、今度は下記 URL をウェブブラウザで表示してみましょう。

http://localhost:8000/forum/reasons/

これにより、今度はインスタンスの削除理由の一覧ページが表示されることになります。これは ReasonList によって表示されるページであり、一覧に表示されるインスタンスは先程 DeleteView によってデータベースに新規登録された Reason のインスタンスとなります。先程削除確認用のページで入力した削除理由が表示されていることが確認できるはずです。

ReasonListによって表示されるページ

今回は削除したインスタンスがコメントに対応するものでしたが、例えばユーザーのインスタンスの削除によってウェブアプリの会員の退会を実現した場合、ユーザーから退会理由を集計することができ、それによって今後のウェブアプリの運営の仕方を改善し、よりよりウェブアプリに仕立てていくこともできると思います1

まとめ

このページでは、DeleteView および DeleteView を継承したクラスベースビューの作り方について解説しました!

DeleteView はデータベースに既に保存されているインスタンス(レコード)を削除するための View のサブクラス であり、このクラスを継承することで、ユーザーのアプリや SNS 等からの退会や投稿済みのコメントの削除等を行うページを簡単に実現できます。

解説の中でも説明しましたが、DeleteView では POST リクエストを受け取った際にデータベースからのインスタンスの削除が行われます。GET リクエスト時にはインスタンスの削除が行われないと言う点に注意してください。GET リクエスト時には削除確認用のページの表示が行われます。削除するだけなのでフォームからのデータの送信は不要であり、GET リクエスト時に削除が実行されると考えても不自然ではないのですが、実際には POST リクエスト時にデータベースからのインスタンスの削除が行われるようになっています。

また、GET リクエストや POST リクエストを受け取った際には特定の1つのインスタンスのみをデータベースから取得することになり、この際の処理は DetailView と同様になります。なので、DetailView を使いこなせるようになっておけば、DeleteView も簡単に使いこなすことができると思います。

ウェブアプリを運営していく上では、データベースからのインスタンス(レコード)の削除も重要な機能となりますので、是非 DeleteView についても使いこなせるようになっておきましょう!

また、このサイトでは他の View のサブクラス についても説明していますので、他のページも是非読んでみてください!

DjangoのListViewの解説ページアイキャッチ【Django】ListViewの使い方(クラスベースビューでの一覧リストページの実現) DjangoのDetailViewの解説ページアイキャッチ【Django】DetailViewの使い方(クラスベースビューでの詳細ページの実現) CreateViewでのクラスベースビューの実現方法解説ページアイキャッチ【Django】CreateViewの使い方(クラスベースビューでの新規登録ページの実現) UpdateViewでのクラスベースビューの実現方法解説ページアイキャッチ【Django】UpdateViewの使い方(クラスベースビューでのレコード更新ページの実現) FormViewでのクラスベースビューの実現方法解説ページアイキャッチ【Django】FormViewの使い方(クラスベースビューで汎用的なフォームを扱う)

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