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 DeleteViewfrom django.urls import reverse_lazyfrom django.contrib.auth.mixins import LoginRequiredMixinclass SnippetDeleteView(LoginRequiredMixin, DeleteView):template_name = "snippets/snippet_delete.html"model = Snippetsuccess_url = reverse_lazy("top")pk_url_kwarg = "snippet_id"def get_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
編集ボタンの次に、削除ボタンを追加します。 必要な情報を削除するリスクがあるので、注意喚起のため赤色ボタンにしました。
<div class="snippet-date">投稿日:{{ snippet.created_at|date:"DATETIME_FORMAT" }}{% if user.is_authenticated and snippet.created_by_id == user.id %}<a class="btn btn-primary" href="{% url 'snippet_edit' snippet.id %}">編集</a><!-- 以下の行を追加 --><a class="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 %}<form method="POST">{% csrf_token %}<h3>"{{ snippet.title }}"を本当に削除しますか?</h3><div class="source-code">{{ snippet.code|pygmentize:"python3" }}</div><p>{{ snippet.description | urlize }}</p><button type="submit" class="btn btn-danger">確定</button></form>{% endblock %}
書籍
まず実際にWebアプリを作り、その後詳細な機能を解説する形式で、Djangoを学ぶことができます。
- 芝田 将 "実践Django Pythonによる本格Webアプリケーション開発"↩
- Classy Class-Based Views: DeleteView↩