14.Djangoで投稿を編集・削除できるようにしてみよう

Djangoで作った1行掲示板に投稿を編集・削除できる機能を追加してみましょう。編集フォームと投稿編集用のWebページ、編集内容をデータベースに保存する関数、データベースから投稿を削除する関数など複数の機能が必要になります。

目次

必要な機能のリストアップ

まずはじめに必要になる機能とページを確認しておきましょう。

掲示板の投稿編集・削除には次の各機能が必要となります。

掲示板の投稿編集機能のフロー図

  • ルーティング(urls.pyに記述)
  • 投稿編集ページへ遷移するためのボタン(detail.htmlに配置)
  • 投稿を削除するためのボタン(detail.htmlに配置)
  • 投稿編集用のWebページ(edit.htmlを作成)
  • 編集内容をデータベースに保存するedit関数(views.pyに記述)
  • 投稿をデータベースから削除するdelete関数(views.pyに記述)

これらを1つずつ作っていきましょう。

ルーティング

ルーティングを記述します。

/bbs/urls.py に次のように記述します。

from django.urls import path
from . import views

app_name = 'bbs'

urlpatterns = [
    path('', views.index, name='index'),
    path('<int:id>', views.detail, name='detail'),
    path('new', views.new, name='new'),
    path('create', views.create, name='create'),
    path('<int:id>/edit', views.edit, name='edit'), # 投稿編集ページのURL
    path('<int:id>/update', views.update, name='update'), # 編集内容をデータベースに保存する関数のURL
    path('<int:id>/delete', views.delete, name='delete'), # 投稿を削除する関数のURL
]

ユーザーが /bbs/(id)/edit にアクセスすると views.py の edit関数を、/bbs/(id)/update/ にアクセスすると views.py の update関数を、/bbs/(id)/delete/ にアクセスすると views.py の delete関数をそれぞれ呼び出します。

また、それぞれname属性を付けておいたので、editやdeleteといった名前でこのURLをリンクで呼び出せます。

投稿編集ページへ遷移するためのボタンをdetail.htmlに配置

個別投稿ページに投稿編集ページへの遷移ボタンを配置しておきましょう。

{% extends "./base.html" %}

{% block content %}
    <h1>{{ article.id }}の個別投稿ページ</h1>
    <p>{{ article.content }}, {{article.user_name }}</p>
    <div>
      <button onclick='location.href="{% url "bbs:index" %}"'>一覧</button>
      <button onclick='location.href="{% url "bbs:edit" article.id %}"'>編集</button>
      <button onclick='location.href="{% url "bbs:delete" article.id %}"'>削除</button>
    </div>
{% endblock %}

先ほど urls.py でパスを edit や delete という名前で呼び出せるようにしておいたので、8,9行目のリンクでそれぞれ呼び出すことができます。

投稿編集ページを呼び出すedit関数を作成

投稿編集ページを呼び出すための関数を作成します。

フォームは前回の記事で新規投稿用に定義した ArticleForm を流用します。

views.py に次のように追記します。


# 前略

# 投稿編集ページを返すedit関数
def edit(request, id):
    article = get_object_or_404(Article, pk=id) # 指定した投稿をarticle変数に代入
    articleForm = ArticleForm(instance=article) # ArticleFormクラスからarticleFormオブジェクトを生成
    context = {
        'article': article,
        'articleForm': articleForm,
    }
    return render(request, 'bbs/edit.html', context) # edit.htmlというテンプレートを返す

特定の投稿の編集ページなので

  • 投稿idを受け取ってarticle変数に代入
  • それを元にarticleFormオブジェクトを用意してcontextに追加
  • テンプレート(edit.html)を呼び出す

これらの処理を記述しています。

投稿編集ページのテンプレート(edit.html)の作成

views.py で呼び出すように記述した edit.html というテンプレートファイルを作成します。

/templates/bbs/ に edit.html という名前でファイルを作成し、次のように記述します。

{% extends "./base.html" %}

{% block content %}
  <h1>Djangoで作る新規投稿フォーム</h1>

  <form action='{% url "bbs:update" articel.id %}' method="post">
    {% csrf_token %}<!-- セキュリティのための記述 -->
    {{ articleForm }}<!-- views.pyから受け取ったarticleFormを表示 -->
    <button type='submit'>保存</button>
    <a href='{% url "bbs:detail" article.id %}'>個別投稿ページへ戻る</a>
  </form>
{% endblock %}

new.html と同様、フォームはforms.py で定義した ArticleForm クラスを元に、views.py の edit関数の中でオブジェクトを作成してテンプレートに渡しているので、テンプレート側では articleForm という変数名を指定するだけで、必要な項目(今回はcontentとUser name)が用意されたフォームを作成してくれます。

また、6行目では「個別投稿ページへ戻る」ボタンをクリックすると update という名前のルートをPOSTメソッドで呼び出すよう記述しています。

※csrf_tokenはDjangoが用意してくれているセキュリティ機能です。フォームがデータを送信する時にトークン情報を一緒に送信し、リクエストを受け取ったViewが、トークンが正しいものがどうか検証し、正常なリクエストだと認められれば処理を実行してくれます。

編集内容をデータベースに保存するupdate関数を作成

次に、投稿編集Webページで「保存」ボタンを押した時に呼び出される update関数を作成しましょう。

views.py に次のように追記します。


# 前略

# 編集データを保存して個別投稿ページを返すupdate関数
def update(request, id):
    # リクエストのメソッドがPOSTなら
    if request.method == 'POST':
        article = get_object_or_404(Article, pk=id)
        articleForm = ArticleForm(request.POST, instance=article) # ArticleFormクラスからarticleFormオブジェクトを生成
        # 受け取ったデータが正常なら
        if articleForm.is_valid():
            articleForm.save() # データを保存
    context = {
        'article': article,
    }
    return render(request, 'bbs/detail.html', context) # detail.htmlを返す

この関数は投稿編集フォームからデータを受け取った時、POSTメソッドかどうか、受け取ったデータが正常かどうかをチェックし、問題なければデータを保存します。

データの保存後はまた個別投稿ページを呼び出すようにしています。

投稿内容をデータベースから削除するdelete関数を作成

次に、投稿編集Webページで「削除」ボタンを押した時に呼び出される delete関数を作成しましょう。

views.py に次のように追記します。


# 前略

# 投稿データを削除するdelete関数
def delete(request, id):
    article = get_object_or_404(Article, pk=id) # 指定した投稿をarticle変数に代入
    article.delete()

    articles = Article.objects.all()
    context = {
        'articles': articles,
    }
    return render(request, 'bbs/index.html', context) # index.htmlを返す

動作確認

それでは動作確認です。

Djangoコマンドを使ってサーバーを起動し、ブラウザで確認してみましょう。

http://127.0.0.1:8000/bbs にアクセスします。

トップページ

個別投稿ページへアクセスしましょう。

最後の投稿である「テスト」をクリックします。

個別投稿ページ

今回追加した「編集」「削除」ボタンが並んでいます。

「編集」から試してみましょう。

投稿編集ページ

投稿編集ページが表示できました。

実際にデータを編集して「保存」ボタンをクリックしてみましょう。

編集内容を保存

編集内容が保存されて個別投稿ページが再び表示されました。

一覧ページでも確認してみましょう。

投稿一覧ページでも確認

ちゃんと反映されていますね。

もう一度「ファイエル!」の投稿をクリックして個別投稿ページへアクセスしましょう。

個別投稿ページ

次は「削除」ボタンを試してみましょう。

投稿データを削除

投稿が削除されて、投稿一覧ページへと遷移しました。

投稿の編集・削除機能の完成です!

このエントリーをはてなブックマークに追加

コメントを残す

頂いたコメントは一読した後表示させて頂いております。
反映まで数日かかる場合もございますがご了承下さい。