Quantcast
Channel: プログラミング
Viewing all articles
Browse latest Browse all 8335

DjangoでのDB削除機能の追加 - Engineer's Memorandum

$
0
0

WebアプリフレームワークDjangoの書籍1を勉強中です。 書籍の1章で説明されているコードSnippetの共有アプリをベースに機能を追加して勉強を継続しています。

今回、DBに登録されたSnippetを削除する機能を追加したので、その内容をメモしています。

DB削除機能の実装例

機能追加前

Snippet削除機能は、Snippet詳細画面に追加しました。 青色の編集ボタンの右にボタンを追加します。

delete機能追加前のSnippet詳細画面

機能追加後

Snippet詳細画面に、赤色の削除ボタンを追加した結果です。

delete機能追加後のSnippet詳細画面

削除ボタンを押すと、確認画面に遷移するように実装しました。

Snippet削除の確認画面

ユーザが異なる場合の挙動

Snippetを登録したユーザ以外は、編集も削除もできないようにしています。 ユーザadminでログインしたとき、ユーザtestuserが登録したSnippetは編集・削除ボタンを表示させません。

編集・削除権限のないユーザのSnippet詳細画面

また、悪意あるユーザがURLを直接入力して編集・削除画面に遷移できないようにしています。 URLが正しくとも、ログインユーザのIDをチェックして編集・削除権限が無ければ404エラーを出すようにしました。

編集・削除権限のないユーザが編集・削除用URLを入力したときのエラー

ファイル構成

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を学ぶことができます。


  1. 芝田 将 "実践DjangoPythonによる本格Webアプリケーション開発"
  2. Classy Class-Based Views: DeleteView

Viewing all articles
Browse latest Browse all 8335

Trending Articles