Pythonフレームワーク「Django」には、削除・更新機能を実装するときに便利な「DeleteView」「UpdateView」というクラスベースビューがある。
今回は、作成済の「Django」を利用したアプリケーションに、削除・更新機能を追加してみたので、そのサンプルプログラムを共有する。
なお、クラスベースビューについては、以下のサイトを参照のこと。
https://di-acc2.com/programming/python/5210/
前提条件
下記記事の実装が完了していること。
サンプルプログラムの作成
作成したサンプルプログラムの構成は、以下の通り。
なお、上記の赤枠は、前提条件のプログラムから追加・変更したプログラムである。
demoフォルダ下、templatesフォルダ下の一覧画面(list.html)の内容は以下の通りで、更新・削除を行うためのリンクを追加している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>一覧画面</title> </head> <body> ユーザーデータテーブル(user_data)の全データ<br/><br/> <table border="1" cellpadding="5"> <tr> <th>ID</th> <th>名前</th> <th>生年月日</th> <th>性別</th> <th></th> <th></th> </tr> <!-- context_object_nameで指定した一覧取得結果を画面に表示 --> {% for user in user_list %} <tr> <td>{{ user.id }}</td> <td>{{ user.name }}</td> <td>{{ user.birth_year }}年 {{ user.birth_month }}月 {{ user.birth_day }}日</td> <td> {% if user.sex|stringformat:"s" == "1" %} 男 {% elif user.sex|stringformat:"s" == "2" %} 女 {% endif %} </td> <td> <a href="{% url 'update' user.id %}">更新</a> </td> <td> <a href="{% url 'delete' user.id %}">削除</a> </td> </tr> {% endfor %} </table> <br/><br/> <!-- action属性のURLで、(demoフォルダ内)urls.pyの画面遷移先のname属性の値を指定している --> <form action="{% url 'input' %}" method="post"> <!-- 下記csrf_tokenは、CSRF対策を行うことでform送信時エラーを防ぐために設定 --> {% csrf_token %} <input type="submit" name="next" value="データ追加" /> </form> </body> </html> |
また、demoフォルダ下、urls.pyの内容は以下の通りで、更新・削除を行うためのURLを追加している。
1 2 3 4 5 6 7 8 9 10 11 12 | from django.urls import path from . import views urlpatterns = [ path('', views.UserListView.as_view(), name='index'), path('input', views.InputView.as_view(), name='input'), path('confirm', views.ConfirmView.as_view(), name='confirm'), path('regist', views.RegistView.as_view(), name='regist'), path('complete', views.CompleteView.as_view(), name='complete'), path('<int:pk>/update', views.UserUpdateView.as_view(), name='update'), path('<int:pk>/delete', views.UserDeleteView.as_view(), name='delete'), ] |
さらに、demoフォルダ下、views.pyの内容は以下の通りで、削除を行うためのDeleteViewクラスを継承したViewクラス、更新を行うためのUpdateViewクラスを継承したViewクラスを追加している。また、確認画面の「送信」ボタンが押下された場合に、user_dataテーブルのデータを(追加だけでなく)更新できるようにしている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | from django.shortcuts import render from .forms import InputModelForm from .models import UserData from django.db.models import Max from datetime import datetime as dt from django.views.generic import TemplateView from django.views.generic import FormView from django.urls import reverse_lazy from django.views.generic import ListView from django.views.generic.edit import DeleteView from django.views.generic.edit import UpdateView # クラス定数を定義 SEX_LABEL_IDX = 1 # 性別のラベルを取得するためのインデックス class UserListView(ListView): """ 一覧画面を表示するView """ template_name = 'demo/list.html' # 一覧画面のHTML model = UserData # UserDataモデル(user_dataテーブルと紐づける) context_object_name = 'user_list' # 一覧取得結果 class InputView(TemplateView): """ 入力画面を表示するView """ template_name = 'demo/input.html' def get_context_data(self): """ 入力画面に渡すフォーム値を初期化 """ context = super().get_context_data() context['form'] = InputModelForm() return context def post(self, request): """ 入力画面にpostリクエストで遷移 """ return render(request, self.template_name, self.get_context_data()) class ConfirmView(FormView): """ 入力画面で確認ボタンが押下された時の処理を定義するView """ template_name = 'demo/input.html' # form_invalidメソッド実行時に表示する画面 form_class = InputModelForm # 利用するFormクラス success_url = 'demo/confirm.html' # form_validメソッド実行時の画面遷移先 def get_context_data(self, form): """ 入力チェックエラー時に、入力画面にuser_idの値を渡す """ context = super().get_context_data() context['user_id'] = self.request.POST['user_id'] return context def form_valid(self, form): """ フォームの入力チェックを行い、エラーがない場合 """ # 確認画面で表示するための入力値(性別)の値を生成 lbl_sex = UserData.sex_data[int(form.cleaned_data['sex']) - 1][SEX_LABEL_IDX] # 確認画面に渡す各変数を定義し、確認画面に遷移 context = { 'input_form': form, 'lbl_sex': lbl_sex, 'lbl_checked': '確認済', 'user_id': self.request.POST['user_id'] } return render(self.request, self.success_url, context) def form_invalid(self, form): """ フォームの入力チェックを行い、エラーがある場合 """ # フォーム値はそのままで入力画面に戻る return super().form_invalid(form) class RegistView(FormView): """ 確認画面でボタンが押下された時の処理を定義するView """ template_name = 'demo/input.html' # 戻るボタン押下時に表示する画面 form_class = InputModelForm # 利用するFormクラス success_url = reverse_lazy('complete') # 送信ボタン押下後の画面遷移先 def get_context_data(self, form): """ 戻るボタン押下時に、入力画面にフォーム値を渡す """ context = super().get_context_data() context['input_form'] = form context['user_id'] = self.request.POST['user_id'] return context def form_valid(self, form): # 送信ボタンが押下された場合 if "send" in self.request.POST: # 入力データを生成 user_data = UserData() # user_idの値をリクエストから取得 user_id = self.request.POST['user_id'] # user_idが設定済(更新)の場合 if user_id: user_data.id = int(user_id) # user_idが未設定(追加)の場合 else: # USER_DATAテーブルに登録されているidの最大値を取得 max_id_dict = UserData.objects.all().aggregate(Max('id')) # USER_DATAテーブルに登録されているデータが無い場合、id=1とする user_data.id = (max_id_dict['id__max'] or 0) + 1 # 入力データを保存 input_form = InputModelForm(self.request.POST, instance=user_data) input_form.save() # 完了画面に遷移 return super().form_valid(form) # 戻るボタンが押下された場合 elif "back" in self.request.POST: # 確認画面でのフォーム値を入力画面に渡す return super().form_invalid(form) class CompleteView(TemplateView): """ 完了画面を表示するView """ template_name = 'demo/complete.html' class UserDeleteView(DeleteView): """ 一覧画面で削除リンクが押下された時の処理を定義するView """ template_name = 'demo/delete_confirm.html' # 削除確認画面を表示 model = UserData # UserDataモデル(user_dataテーブルと紐づける) success_url = reverse_lazy('index') # 削除ボタン押下後の画面遷移先 class UserUpdateView(UpdateView): """ データ更新時の入力画面を表示するView """ template_name = 'demo/input.html' model = UserData # UserDataモデル(user_dataテーブルと紐づける) form_class = InputModelForm # 利用するFormクラス |
また、demoフォルダ下、templatesフォルダ下の削除確認画面(delete_confirm.html)を追加している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>削除確認画面</title> </head> <body> <p>下記データを削除する場合は、「削除」ボタンを押下してください。</p><br/> <form method="post"> <!-- 下記csrf_tokenは、CSRF対策を行うことでform送信時エラーを防ぐために設定 --> {% csrf_token %} <table> <!-- 名前 --> <tr> <td align="left" valign="top">名前: </td> <td>{{ object.name }}</td> </tr> <!-- 生年月日 --> <tr> <td align="left" valign="top">生年月日: </td> <td>{{ object.birth_year }}年 {{ object.birth_month }}月 {{ object.birth_day }}日</td> </tr> <!-- 性別 --> <tr> <td align="left" valign="top">性別: </td> <td> {% if object.sex|stringformat:"s" == "1" %} 男 {% elif object.sex|stringformat:"s" == "2" %} 女 {% endif %} </td> </tr> <!-- メモ --> <tr> <td align="left" valign="top">メモ: </td> <!-- 「| default:""」で、Noneを空文字に置き換える --> <!-- テキストエリアの改行を有効にするため、「| linebreaksbr」を付与 --> <td>{{ object.memo | default:"" | linebreaksbr }}</td> </tr> </table> <br/><br/> <input type="submit" value="削除" /> <input type="button" name="back" value="戻る" onclick="location.href={% url 'index' %}" /> </form> </body> </html> |
さらに、demoフォルダ下、templatesフォルダ下の入力画面・確認画面に、ユーザーIDをhiddenで渡す処理を追加している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>入力画面</title> <!-- 独自CSSファイルの読み込み --> {% load static %} <link rel="stylesheet" href="{% static 'demo/css/demo.css' %}"> </head> <body> <p>下記必要事項を記載の上、「確認」ボタンを押下してください。</p><br/> <!-- action属性のURLで、(demoフォルダ内)urls.pyの画面遷移先のname属性の値を指定している --> <form action="{% url 'confirm' %}" method="post"> <!-- 下記csrf_tokenは、CSRF対策を行うことでform送信時エラーを防ぐために設定 --> {% csrf_token %} <table> <!-- 名前 --> <tr> <td align="left" valign="top">{{ form.name.label_tag }}</td> <td>{{ form.name }}</td> </tr> <!-- 生年月日 --> <tr> <td align="left" valign="top">{{ form.birth_day.label_tag }}</td> <td> {{ form.birth_year }}年 {{ form.birth_month }}月 {{ form.birth_day }}日 <!-- 生年月日の入力チェックエラーを表示 --> <span class="error_message">{{ form.non_field_errors.0 }}</span> </td> </tr> <!-- 性別 --> <tr> <td align="left" valign="top">{{ form.sex.label_tag }}</td> <td> {% for sex_choice in form.sex %} {{ sex_choice.choice_label }} {{ sex_choice.tag }} {% endfor %} </td> </tr> <!-- メモ --> <tr> <td align="left" valign="top">{{ form.memo.label_tag }}</td> <td>{{ form.memo }}</td> </tr> <!-- 入力確認 --> <tr> <td align="left" valign="top">{{ form.check.label_tag }}</td> <td>{{ form.check }}</td> </tr> <!-- ユーザーID --> <!-- 一覧画面で更新リンクを押下したため、object.idが設定されている場合 --> {% if object.id %} <input type="hidden" name="user_id" value="{{ object.id }}" /> <!-- 入力画面で入力チェックエラーが発生したり、確認画面で戻るボタンを 押下したため、user_idが設定されている場合 --> {% elif user_id %} <input type="hidden" name="user_id" value="{{ user_id }}" /> <!-- 一覧画面でデータ追加ボタンを押下したため、object.idもuser_idも 設定されていない場合 --> {% else %} <input type="hidden" name="user_id" value="" /> {% endif %} </table> <br/><br/> <input type="submit" name="confirm" value="確認" /> <input type="button" name="back" value="戻る" onclick="location.href={% url 'index' %}" /> </form> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>確認画面</title> </head> <body> <p>入力内容を確認し、問題なければ「送信」ボタンを押下してください。</p> <form action="{% url 'regist' %}" method="post"> {% csrf_token %} <table> <!-- 名前 --> <tr> <td align="left" valign="top">{{ input_form.name.label_tag }}</td> <td>{{ input_form.name.value }}</td> </tr> <!-- 生年月日 --> <tr> <td align="left" valign="top">{{ input_form.birth_day.label_tag }}</td> <td>{{ input_form.birth_year.value }}年 {{ input_form.birth_month.value }}月 {{ input_form.birth_day.value }}日</td> </tr> <!-- 性別 --> <tr> <td align="left" valign="top">{{ input_form.sex.label_tag }}</td> <td>{{ lbl_sex }}</td> </tr> <!-- メモ --> <tr> <td align="left" valign="top">{{ input_form.memo.label_tag }}</td> <!-- テキストエリアの改行を有効にするため、「| linebreaksbr」を付与 --> <td>{{ input_form.memo.value | linebreaksbr }}</td> </tr> <!-- 入力確認 --> <tr> <td align="left" valign="top">{{ input_form.check.label_tag }}</td> <td>{{ lbl_checked }}</td> </tr> <!-- ユーザーID --> <input type="hidden" name="user_id" value="{{ user_id }}" /> </table> <br/><br/> <!-- フォームの各値を次画面に渡すためhidden項目を設定 --> {% for field in input_form %} {{field.as_hidden}} {% endfor %} <input type="submit" name="send" value="送信" /> <input type="submit" name="back" value="戻る" /> </form> </body> </html> |
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/python/tree/master/django-delete-update/djangoApp
サンプルプログラムの実行
サンプルプログラムの実行結果は以下の通りで、user_dataテーブルの更新・削除ができることが確認できる。
1) 実行前のuser_dataテーブルの中身は、以下の通り。
1 | select * from user_data |
2) コマンドプロンプトでDjangoプロジェクト名のフォルダに移動し、コマンドプロンプトで「python manage.py runserver」コマンドを実行して、Webサーバーを起動する。
3) Webブラウザを起動し、「http://127.0.0.1:8000/demo/」にアクセスすると、以下のように、user_dataテーブルの中身が一覧画面(list.html)に表示されることが確認できる。ここでID=4の「削除」リンクを押下する。
4) 以下のように、削除確認画面が表示されID=4のデータが表示されることが確認できる。ここで「戻る」ボタンを押下する。
5) 以下のように、一覧画面が表示され、先ほど削除リンクを押下したID=4のデータが表示されることが確認できる。ここで再度「削除」リンクを押下する。
7) 以下のように、一覧画面に遷移し、ID=4のデータが削除されていることが確認できる。ここでID=3の「更新」リンクを押下する。
8) 以下のように、入力画面が表示されID=3のデータが表示されることが確認できる。ここで「戻る」ボタンを押下する。
9) 以下のように、一覧画面が表示され、先ほど更新リンクを押下したID=3のデータが表示されることが確認できる。ここで再度「更新」リンクを押下する。
10) 入力画面でデータを編集後、「確認」ボタンを押下する。
12) 以下のように、完了画面に遷移することが確認できる。ここで「一覧画面に戻る」ボタンを押下する。
13) 以下のように、一覧画面に遷移し、更新後のデータが表示されることが確認できる。
14) 実行後のuser_dataテーブルの中身は以下の通りで、削除・更新内容が反映されていることが確認できる。
1 | select * from user_data |
要点まとめ
- Pythonフレームワーク「Django」には、データベースの削除・更新をするのに便利な「DeleteView」「UpdateView」というクラスベースビューがある。