Django - ジェネリックビューで楽してみた(DeleteView編)
数回前の記事で、CreateViewとUpdateViewについて書きました。今回は更新ジェネリックビューの残り、DeleteViewについて書きます。CRUDのDに相当します。
バージョン等
前回と同様です。
| ツール | バージョン |
|---|---|
| Python | 3.7.2 |
| Django | 2.1.7 |
| bootstrap | 4.1.3 |
DeleteViewについて
前提:Model層
- models.py
from django.db import models class Table(models.Model): """テーブル一覧""" name = models.CharField(max_length=100) class Column(models.Model): """カラム定義(物理名格納)""" table = models.ForeignKey(Table, on_delete=models.CASCADE) name = models.CharField(max_length=100)
前回と同様ですが、上記二つのModelを使います。
DeleteViewとは
DeleteViewは、名前の通り要素を削除するビューです。対象が存在したら削除します。が、削除は対象が無くても結果的には成功と言えるので…
DeleteViewでは、ビューで指定したモデルの1行を削除することを想定しています。
DeleteViewの定義
DeleteViewは、CreateView、UpdateViewと異なり、入力する必要が無いです。そのため、考え方か異なります。
説明のために、URLとビューを示します。
- urls.py
from django.urls import path from .views import table app_name = 'inputs' urlpatterns = [ path('table', table.TableListView.as_view(), name='tables'), path('table/<int:pk>', table.TableDetailView.as_view(), name='table'), path('table/<int:pk>/delete', table.TableDeleteView.as_view(), name='table_del'), ]
UpdateViewの場合と同様、URLに主キーを含めます。
- views/table.py
class TableDeleteView(DeleteView): """テーブル削除""" model = Table success_url = reverse_lazy('inputs:tables') template_name = 'inputs/delete.html'
単純な場合、上記の定義のみで良いです。
テンプレートについて
CreateView等のテンプレートは、入力項目をformとして配置し、そこにsubmit用のボタン等を置く、という形で作成します。登録するデータを入力するためのテンプレートを渡すことになります。
一方、DeleteViewが要求するテンプレートは、削除確認用途として使われます。ドキュメントにも
確認ページを表示して、現存するオブジェクトを削除するビューです。
という記述の通りです。共通のような形で作ると、以下のようになります。
- templates/delete.html
<!-- body部のみ --> <form method="post"> {% csrf_token %} <p>削除します。よろしいですか?</p> <input class="btn btn-danger" type="submit" value="削除する"> </form>
削除確認のみ行います。
URLに対する役割
DeleteViewのURLに対してリクエストを送った場合、以下の挙動となります。
| メソッド | 挙動 |
|---|---|
| GET | 確認ページを返す |
| POST | 指定された要素を削除し、指定したページにリダイレクト |
POSTするには、フォームにsubmitのボタンを置けばいいです。
雑な図ですが、イメージはこんな感じです。

削除前に情報を表示する
削除する前に、削除されようとしている項目の情報を取得したいとします。
テンプレートにはobjectという名前で削除しようとしているモデルが格納されるので、そちらを使用できます。
<form method="post"> {% csrf_token %} <p>このテーブルを削除しますか?</p> <table class="table table-sm table-bordered table-responsive"> <tbody> <tr> <th>ID</th> <td>{{ object.id }}</td> </tr> <tr> <th>テーブル名</th> <td>{{ object.name }}</td> </tr> </tbody> </table> <input class="btn btn-danger" type="submit" value="削除する"> </form>
また、ここでは載せませんが、get_querysetやget_context_dataをオーバーライドして、追加データを取得することもできます。
削除時の子要素
削除する際は、その要素の主キーを指定します。
今回、ColumnはTableを外部キー要素として指定しています。もし、Tableを削除する場合、そのTableを参照しているColumnがあると、通常は先にColumnを消す必要があります。
今回は、親が消された場合、子も一緒に消えるように、
class Column(models.Model): """カラム定義(物理名格納)""" table = models.ForeignKey(Table, on_delete=models.CASCADE) name = models.CharField(max_length=100)
ForeignKeyを作成する際にon_delete=models.CASCADEを指定しています。なので気にせず消せます*1。
on_deleteに関しては、こちらに記載があります。
参考
次回
ジェネリックビューを使わない場合について、書く予定です。今回書く予定でしたが、分量が多かったため、改めて書きます。
おわりに
Djangoは、単純なCRUDだけならとても簡単に作成できます。問題はそこから逸脱し始めるときです。そのあたりを次回書いてみます。
*1:場合によっては多くの行が消えるため、一応パフォーマンス上の考慮は必要