Progateで作ったWebアプリをDjangoで作ってみる！ Part7 -Delete編-

ProgateのNode.jsコースではお買い物リストアプリを作るのですが、これと全く同じものをDjangoで作ってみます。

Djangoでのアプリ開発の一連の流れを整理するために記していきます。

**目標物**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/471633/16123e6f-f050-226e-a369-8383267de571.png)


HTMLとCSS、PNG形式画像は使い回しで、一部をDjango用のタグに変えます。

#前回のおさらい

[Part6](https://qiita.com/insilicomab/items/3d58e504e5d8f8e732c4)

Part6では、リストのアイテムをブラウザから編集・更新できるページを作成しました。

今回はリストを削除するための機能を実装していきます。

#Delete機能の実装

Deleteの実装方法は、削除するメモを指定して、deleteメソッドを使うだけです。なのでページを表示する必要はないのでテンプレートを用意する必要はありません。

Djangoは削除用のビューとして**DeleteView**を準備してくれていますが、今回はそれを使いません。Listページの「削除」をクリックするだけでアイテムを消せるようにします。

☆1のように``href="{% url 'list:edit' object.pk %}``とすることで、``http://localhost:8000/delete/（数字）``にアクセスしたとき、primary keyが（数字）のものが削除されるようにします。削除用のdelete関数を**views.py**で定義していきます。

```html:index.html
{% load static %}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>LIST APP</title>
    <link rel="stylesheet" href="{% static 'list/style.css' %}">
  </head>
  <body>
    <header>
      <a href="{% url 'list:top' %}" class="header-logo">LIST APP</a>
    </header>
    <div class="container">
      <div class="container-header">　
        <h1>買い物リスト</h1>
        <a href="{% url 'list:new' %}" class="new-button">+ 新規登録</a>
      </div>
      <div class="index-table-wrapper">
        <div class="table-head">
          <span class="id-column">ID</span>
          <span>買うもの</span>
        </div>
        <ul class="table-body">
          {% for object in object_list %}
            <li>
              <div class="item-data">
                <span class="id-column">{{ forloop.counter }}</span>
                <span class="name-column">{{ object.item }}</span>
              </div>
              <div class="item-menu">
                <form action="{% url 'list:delete' object.pk %}" method="post">{% csrf_token %}
                  <input type="submit" value="削除">
                </form>
                <a href="{% url 'list:edit' object.pk %}">編集</a> <!--☆1-->
              </div>
            </li>
          {% endfor %}
        </ul>
      </div>
    </div>
  </body>
</html>
```

delete関数の実装をやっていきます。

関数名は**deletefunc**とします（☆2）。引数は``request``と``pk``（primary key）とします。

**get_object_or_404()**関数と**redirect()**関数を使うため、あらかじめimportしておきます（☆3）。

**deletefunc()**関数内で、はじめに**get_object_or_404()**関数を使って、データベース（ListModel）の``id``が``pk``のアイテムをgetします。そして、それを変数``item``に代入します（☆4）。

その``item``を**delete()**メソッドで削除します（☆5）。

削除したあとは、Listページにリダイレクトするようにします（☆6）。

``@require_POST``というものがimportされ、関数の頭に追加されています（☆7）。これによって、postの時のみこの関数が実行され、getのときは実行されなくなります（☆7）。

```python:listapp/list/views.py
from django.shortcuts import render, redirect, get_object_or_404 # ☆3
from django.views.generic import TemplateView, ListView, CreateView, UpdateView
from .models import ListModel 
from django.urls import reverse_lazy
from django.views.decorators.http import require_POST # ☆7

class ListTop(TemplateView):
    # top.htmlをレンダリング
    template_name = 'list/top.html'

class ListIndex(ListView):
    # index.htmlをレンダリング
    template_name = 'list/index.html'
    model = ListModel

class ListNew(CreateView):
    # new.htmlをレンダリング
    template_name = 'list/new.html'
    model = ListModel
    fields = ['item']
    success_url = reverse_lazy('list:index')

class ListEdit(UpdateView):
    # edit.htmlをレンダリング
    template_name = 'list/edit.html'
    model = ListModel
    fields = ['item']
    success_url = reverse_lazy('list:index')

@require_POST # ☆7
def deletefunc(request, pk): # ☆2
    item = get_object_or_404(ListModel, id=pk) # ☆4
    item.delete() # ☆5
    return redirect('list:index') # ☆6
```

最後に、**urls.py**ファイルにコードを追加して、**views.py**の**deletefunc**関数を読み込むようにします。

```python:listapp/list/urls.py
from django.urls import path
from .views import ListTop, ListIndex, ListNew, ListEdit, deletefunc # ☆8

# URLパターンを逆引きできるように名前をつける
app_name = 'list'

urlpatterns = [

    # リクエストされたパス部分が''に合致した場合、views.pyのListTopクラスをインスタンス化する
    path('', ListTop.as_view(), name='top'),

    # リクエストされたパス部分が'index'に合致した場合、views.pyのListIndexクラスをインスタンス化する
    path('index/', ListIndex.as_view(), name='index'),

    # リクエストされたパス部分が'new'に合致した場合、views.pyのListNewクラスをインスタンス化する
    path('new/', ListNew.as_view(), name='new'),

    # リクエストされたパス部分が'edit/pk'に合致した場合、views.pyのListEditクラスをインスタンス化する
    path('edit/<int:pk>', ListEdit.as_view(), name='edit'),

    # リクエストされたパス部分が'delete/pk'に合致した場合、views.pyのdeletefuncを呼び出す
    path('delete/<int:pk>', deletefunc, name='delete'), # ☆8
]
```

実際に「削除」ボタンで削除されるか確かめてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/471633/dc44943a-947d-a2ed-29d5-2b8b886965ae.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/471633/1110d6c2-615f-5f3d-adfe-ea7ebd114cf5.png)

ちゃんと「削除」ボタンで削除されました！

これで全て完成です！