WebアプリフレームワークDjangoの書籍1を勉強中です。 書籍の1章で説明されているコードSnippetの共有アプリをベースに機能を追加して勉強を継続しています。
今回、DBに登録されたSnippetを削除する機能を追加したので、その内容をメモしています。
DB削除機能の実装例
機能追加前
Snippet削除機能は、Snippet詳細画面に追加しました。 青色の編集ボタンの右にボタンを追加します。
機能追加後
Snippet詳細画面に、赤色の削除ボタンを追加した結果です。
削除ボタンを押すと、確認画面に遷移するように実装しました。
ユーザが異なる場合の挙動
Snippetを登録したユーザ以外は、編集も削除もできないようにしています。 ユーザadminでログインしたとき、ユーザtestuserが登録したSnippetは編集・削除ボタンを表示させません。
また、悪意あるユーザがURLを直接入力して編集・削除画面に遷移できないようにしています。
URLが正しくとも、ログインユーザのIDをチェックして編集・削除権限が無ければ404エラーを出すようにしました。
ファイル構成
migrationディレクトリなど、機能追加に関係のないディレクトリやファイルは省略しています。
$ tree . ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── models.py ├── static │ └── snippets │ └── css │ └── style.css ├── templates │ └── snippets │ ├── snippet_delete.html # ファイル追加 │ ├── snippet_detail.html │ ├── snippet_edit.html # ファイル変更 │ ├── snippet_new.html │ └── top.html ├── urls.py # ファイル変更 └── views.py # ファイル変更
ソースコード
Djangoのクラスベースview機能で提供されているDeleteViewクラスを利用して実装しました。
views.py
DeleteView
に加えて、ログインしていないユーザをログイン画面に遷移させるため、LoginRequiredMixin
も継承しています。
get_queryset
メソッドをオーバーライドすることで、作成したユーザのIDとログインユーザIDが一致する場合のみ編集・削除ボタンを表示するためのデータを取得するようにしています。
クラス変数success_url
は正常に処理が行われた場合の遷移先を表し、ここではtopページに遷移させています
(reverse_lazy
はきちんと理解できていないので、後日redirectやreverseとの違いを追記します)。
pk_url_kwarg
はurl.pyで使用するURLのPrimary Keyを、snippet_idという表記で上書きしています(デフォルトはpk)2。
from django.views.generic import DeleteView from django.urls import reverse_lazy from django.contrib.auth.mixins import LoginRequiredMixin classSnippetDeleteView(LoginRequiredMixin, DeleteView): template_name = "snippets/snippet_delete.html" model = Snippet success_url = reverse_lazy("top") pk_url_kwarg = "snippet_id"defget_queryset(self): queryset = super().get_queryset() filtered_queryset = queryset.filter(created_by_id=self.request.user.id) return filtered_queryset
urls.py
urlpatterns
に以下の削除用ページ情報を追加します。
int:snippet_id
となっているのは、SnippetDeleteView
のクラス変数pk_url_kwarg
を上書きしたためです。
urlpatterns = [ ... path('<int:snippet_id>/delete/', views.SnippetDeleteView.as_view(), name="snippet_delete"), ... ]
HTML template
CSSは、bootstrap(チートシートサイト)を利用しています。
snippet_detail.html
編集ボタンの次に、削除ボタンを追加します。 必要な情報を削除するリスクがあるので、注意喚起のため赤色ボタンにしました。
<divclass="snippet-date">投稿日:{{ snippet.created_at|date:"DATETIME_FORMAT" }} {% if user.is_authenticated and snippet.created_by_id == user.id %} <aclass="btn btn-primary"href="{% url 'snippet_edit' snippet.id %}">編集</a><!-- 以下の行を追加 --><aclass="btn btn-danger"href="{% url 'snippet_delete' snippet.id %}">削除</a> {% endif %} </div>
snippet_delete.html (追加ファイル)
urls.pyに追加した遷移先を記述するため、新たに追加したファイルです。 最低限の動作としては、確定ボタンだけでも良いのですが、 ユーザが再確認できるよう、Snippetの詳細ページを踏襲した情報も載せています。
{% extends "base.html" %} {% load pygmentize %} {% load django_bootstrap5 %} {% block extraheader %} <style>{% pygments_css %}</style> {% endblock %} {% block main %} <formmethod="POST"> {% csrf_token %} <h3>"{{ snippet.title }}"を本当に削除しますか?</h3><divclass="source-code"> {{ snippet.code|pygmentize:"python3" }} </div><p>{{ snippet.description | urlize }}</p><buttontype="submit"class="btn btn-danger">確定</button></form> {% endblock %}
書籍
まず実際にWebアプリを作り、その後詳細な機能を解説する形式で、Djangoを学ぶことができます。
- 芝田 将 "実践DjangoPythonによる本格Webアプリケーション開発"↩
- Classy Class-Based Views: DeleteView↩