[Django] 記事の編集ページと記事の削除機能の追加
前回、基本的な機能は実装できたので今回は、記事の編集機能と削除機能を実装します。
シリーズ一覧
編集ページの追加
すでに投稿してある記事を編集する機能を実装します。
URLの設定
urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("new/", views.new, name="new"),
path("article/all/", views.article_all, name="article_all"),
path("article/<int:pk>/", views.view_article, name="view_article"),
path("article/<int:pk>/edit/", views.edit, name="edit"), # 追記
]
編集用のページのURLを設定。
viewの設定
編集用のページも投稿ページ同様POSTされたときとGETした時で動作を変える必要があります。
if request.method == "POST":
記事のデータをアップデート
GETされたときは、データベースから投稿済みのデータを引っ張り出して表示させる必要があります。
なので取り合えずデータベースから目的のデータをテンプレートに渡しておきます。
views.py
from django.shortcuts import render, redirect # redirectを追記
from django.http import Http404
from . import models
# 略
def edit(request, pk):
template_name = "blog/edit.html"
try:
article = models.Article.objects.get(pk=pk)
except models.Article.DoesNotExist:
raise Http404
if request.method == "POST":
article.title = request.POST["title"]
article.text = request.POST["text"]
article.save()
return redirect(view_article, pk)
context = {"article": article}
return render(request, template_name, context)
記事の閲覧ページ同様、article = models.Article.objects.get(pk=pk)
でデータベースからデータを取り出します。
もし記事がかなった場合は例外処理で404エラー
を出すようにしています。
if文
の中がデータベースの更新処理です。
取り出したデータに対して更新したいフィールドに投稿されたデータを代入しています。
article.title = request.POST["title"] # 新しいタイトル
article.text = request.POST["text"] # 新しい本文
article.save() # saveメソッドで変更を更新
代入した後にsave()
メソッドを実行するとデータベースに変更を反映できます。
saveし忘れるとデータの更新が行われないのでご注意を
リダイレクト
記事を修正したとき(POSTメソッドのとき)にreturn redirect(view_article, pk)
という見慣れないことをやっていますね。
リダイレクトと聞けばわかる人も多いとは思いますが、これで別のページに遷移するようにしています。
redirect関数
の第一引数にはリダイレクトさせたいページのview関数をとります。
# 引数を一つだけ取るview関数
def single_arg_view(request):
return render(request, template_name)
# 引数を一つだけ取るview関数にリダイレクトする場合
def redirect_to_single_arg_view(request):
# 第一引数にリダイレクト先のview関数
return redirect(single_arg_view)
リダイレクトさせたいページのview関数が二つ以上の引数をとる場合、リダイレクトさせたいページに渡す用の引数をredirect関数
の第二引数 以降に渡します。
# 引数を二つ取るview関数
def double_arg_view(request, pk):
return render(request, template_name)
# 引数を二つ取るview関数にリダイレクトする場合
def redirect_to_double_arg_view(request):
# 第一引数にリダイレクト先のview関数
# 第二引数にはリダイレクト先のview関数に渡す第二引数
pk = 1
return redirect(double_arg_view, pk)
ついでに
今、新しくredirect
をやったので新しく記事を投稿した際も自動的にその記事のページにリダイレクトするように変更しましょう。
views.py
# new関数を書き換えます
def new(request):
template_name = "blog/new.html"
if request.method == "POST":
# articleに新しく作成したデータを代入
article = models.Article.objects.create(title=request.POST["title"], text=request.POST["text"])
# リダイレクトする処理を追記
# 第二引数には新しく作成した記事の主キーを渡す
return redirect(view_article, article.pk)
return render(request, template_name)
これで、記事を投稿した際にそのページに移動するようになったので、記事が投稿できたことがわかり易くなりました。
テンプレートの編集
新しくtemplates/blog/edit.html
(blogはアプリケーション名)を作成します。
基本的にはnew.html
と同じですが、以前に投稿した内容を事前にテキストエリアに表示させておかねばなりません。
フォームに事前に値を入れておくには?
フォームに事前に値を入れておく方法のご紹介。
inputの場合
input
タグにあらかじめ値を入れておきたい場合は、value属性
をつけてあげます。
<input type="text" value="あらかじめ入れておきたい値" />
textareaの場合
先ほどinput
タグではvalue属性
を付与して事前に値を入れておくようにしましたが、textarea
の場合はもっと簡単。
<textarea>あらかじめ入れておきたい文字列</textarea>
事前に入れておきたい値をtextarea
タグで囲うだけ。
それを踏まえてedit.html
の中身を以下のようにします。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>my-blog-edit</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<a href="{% url 'index' %}">トップページへ</a>
<a href="{% url 'view_article' article.pk%}">編集の取りやめ</a>
<h3>まい ぶろぐ 記事の編集ページ</h3>
<form action="" method="post">
{% csrf_token %}
<p><label for="title_input">記事のタイトル</label></p>
<p><input type="text" name="title" id="title_input" value="{{ article.title }}"></p>
<p><label for="text_area">記事の本文</label></p>
<p><textarea name="text" id="text_area" cols="30" rows="10">{{ article.text }}</textarea></p>
<p><button type="submit">更新</button></p>
</form>
</body>
</html>
input
タグのvalue属性
とtextarea
タグの中にそれぞれ、もともとの記事のタイトル{{ article.title }}
ともともとの記事の本文{{ article.text }}
を展開しています。
削除機能の追加
続いて投稿した記事を削除できる機能を追加します。
URL
urls.py
urlpatterns = [
path("", views.index, name="index"),
path("article/<int:pk>/edit/" iews.edit, name="edit"),
path("article/<int:pk>/delete/", views.delete, name="delete"), # 追記
]
viewの設定
削除用の関数delete
を定義します。
views.py
def delete(request, pk):
try:
article = models.Article.objects.get(pk=pk)
except models.Article.DoesNotExist:
raise Http404
article.delete()
return redirect(article_all)
登録してあるデータをデータを削除するには
article = models.Article.objects.get(pk=pk)
で削除対象のデータを取得した後に
article.delete()
とします。
削除が終わった後は一覧ページにリダイレクトさせます。
テンプレートの編集
今回は削除用に新しくページを作ることはしません。
代わりに記事の一覧ページに先ほど作った記事の編集ページへのリンクと削除用のリンクを設置します。article_all.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>article all</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<a href="{% url 'index' %}">トップページへ</a>
<h3>投稿された記事一覧</h3>
{% for article in articles %}
<p>
<a href="{% url 'view_article' article.pk %}">{{ article.title }} - {{ article.posted_at }}</a>
<a href="{% url 'edit' article.pk %}">編集</a>
<a href="{% url 'delete' article.pk %}">削除</a>
</p>
{% endfor %}
</body>
</html>
まとめ
redirect(リダイレクト先のview関数)
でそのページにリダイレクト出来る。
リダイレクト先のview関数が二つ以上の引数を取る場合は、redirect(リダイレクト先の関数, リダイレクト先の関数が受け取る第二引数, 第三引数, ...)
にする。
データーベースからのデータの削除は
Model.objects.get(フィールド名=値).delete()
# 対象のデータを取得して -> 削除
最後に
これで十分ブログの機能が完成しました。
でもせっかくなら、皆から「いいね」とか「コメント」とかもらいたいですよね。
なので、次回はいいね機能を追加してみましょう!
一応、次々回にコメント機能を実装して、その次はbootstrapを使ってデザインを整えて最終回ということにしようと思います。
次回