[Django] いいね機能の追加
前回までにブログの基本的な機能を作成しました。
今回は、いいね機能を実装してみたいと思います。
シリーズ一覧
データベースの確認
データベースの記事のテーブルに新しく「いいね」の数を保存するカラムが必要になりました。
なので、models.py
を編集して「いいね」数を保存できるように変更します。
まず、編集する前のモデル(データベース)を確認します。models.py
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=128)
text = models.TextField()
posted_at = models.DateTimeField(auto_now_add=True)
last_modify = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
おそらくこうなっていると思います。
これにlike = models.IntegerField(default=0)
を足して、いいねの数を保存できるようにします。models.py
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=128)
text = models.TextField()
posted_at = models.DateTimeField(auto_now_add=True)
last_modify = models.DateTimeField(auto_now=True)
like = models.IntegerField(default=0) # 追記
def __str__(self):
return self.title
models.IntegerField
は数値を保存します。default=XX
を指定するとデフォルト値を設定できます。他のフィールドでも同様に使えます。
変更が完了したら
python manage.py makemigrations
を実行してDjangoにモデルが変更されてことを教えてあげて、出力
Migrations for 'blog':
blog\migrations\0002_article_like.py
- Add field like to article
データベースを更新します。
python manage.py migrate
出力
Operations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
Applying blog.0002_article_like... OK
これで、データベースに新たにいいね数を保存するためのカラムが追加されました。
ちなみに、既存のモデル(テーブル)に新しく追加するフィールド(カラム)にdefault
を指定しない場合、
# default = XX をつけない場合
like = models.IntegerField()
Djangoにモデルが変更されてことを教える(makemigrations
)際に
「デフォルト値がないけど既に登録済みのデータには追加されたカラムに何セットするの?」
と聞かれます。
You are trying to add a non-nullable field 'like' to article without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option:
ここで「1」を選択すれば、コンソールにデフォルト値にしたいものを入力すればいいのですが、面倒なのであらかじめモデルにフィールドを足すときに、default
を設定しておきましょう。
URL
http://localhost:8000/article/数字/like
にアクセスするといいねの数が増えるようにします。urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
# 略
path("article/<int:pk>/delete/", views.delete, name="delete"),
path("article/<int:pk>/like/", views.like, name="like"), # 追記
]
view
今度は、views.py
にいいねボタンを押されたときの動作を記述します。
def like(request, pk):
try:
article = models.Article.objects.get(pk=pk)
except models.Article.DoesNotExist:
raise Http404
article.like += 1 # ここでいいねの数を増やす
article.save() # 保存をする
return redirect(view_article, pk)
何度も言いますが、save
メソッドを実行しないとデータが保存されないので注意
いいねボタンが押されたら詳細ページにリダイレクトするようにしています。
テンプレート
詳細ページのテンプレートにいいねボタンを追加します。view_article.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>{{ article.title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<p><a href="{% url 'index' %}">トップページへ</a></p>
<p><a href="{% url 'article_all' %}">一覧へ戻る</a></p>
<h3>{{ article.title }}</h3>
<small>投稿日時 : {{ article.posted_at }}</small>
<small>最終更新 : {{ article.last_modify }}</small>
<p>{{ article.text }}</p>
<p><a href="{% url 'like' article.pk %}"><span id="like">{{ article.like }}</span>いいね!</a></p>
<!-- いいねボタンを追記 -->
</body>
</html>
確認
さあ、これでいいね機能の追加ができたはずなので確認してみましょう。
Djangoサーバーを起動して
python manage.py runserver
http://localhost:8000/article/all/
にアクセス
詳細ページに移動して、いいねボタンをクリック!
クリックしたらいいねの数が増えることを確認しましょう
web API
いいね機能の追加は完了しました。
がしかし、
今の状態だといいねボタンをクリックするたびにページがロードされます。
これでは、表示に時間もかかるし、データ通信量も爆増です。
なので、いいねボタンを押しても、ページ全体をロードしなくてもいいように改造します。
ページの要素を部分的に書き換えるにはやっぱりjavascript
ですよね。
しかし、javascript
だけでは直接、データベースにアクセスできません。
なので、データーベースの「いいね」の数を増やすことができる機能をweb API
として、提供してあげます。
URLの設定
web API
を提供するにはアクセスさせるための入り口URL
をつくってあげる必要があります。http://localhost:8000/api/like/数字/
にアクセスするといいねの数が増えるようにします。
urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
# 略
path("article/<int:pk>/like/", views.like, name="like"),
path("api/like/<int:pk>/", views.api_like, name="api_like"),
]
viewの設定
web API
用のviewapi_like
を定義します。views.py
from django.shortcuts import render, redirect
from django.http import Http404
from django.http.response import JsonResponse # 追記
from . import models
# 略
def api_like(request, pk):
try:
article = models.Article.objects.get(pk=pk)
except models.Article.DoesNotExist:
raise Http404
article.like += 1 # ここでいいねの数を増やす
article.save() # 保存をする
# ここまでは like関数と同じ
return JsonResponse({"like":article.like}) # <- ここが特別
基本的にやっていることはlike
関数と同じですが最後にrender
関数ではないJsonResponse
という関数を使っています。
これは、HTMLではなくjson
形式のデータを送るための関数です。
引数には辞書型をとります。
json
json
はjavascriptで効率的にデータを表現できる汎用データフォーマットです。
様々なプログラミング言語でパーサが提供されているので、web APIでのデータ通信の標準フォーマットになりつつあります。
最後にjson形式のデータでarticle.like
を送信したのは、この値でウェブページ上の「いいね」の数を書き換えるためです。
テンプレート(javascript)
URLとviewを設定したのでいよいよjavascript
でいいね数を増やせるようにします。
javascript
でweb API
を実行するには、XMLHttpRequest
を利用します。
function api_like() {
var api_url = web APIのURL;
var btn_txt = document.getElementById("like");
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
if (request.readyState === 4 && request.status === 200) {
var received_data = JSON.parse(request.responseText);
btn_txt.innerText = received_data.like;
}
}
request.open("GET", api_url);
request.send();
}
request.onreadystatechange
で通信が完了した後に実行する動作を記述します。
通信が完了したらJSON.parse(request.responseText)
でAPIの実行結果をJson形式として受け取ります。btn_txt.innerText = received_data.like
でHTMLのいいね部分の数字を書き換えています。
これをテンプレート(HTML)のheadタグの中に加えます。view_article.html
<!-- 略 -->
<head>
<meta charset="utf-8" />
<title>{{ article.title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
function api_like() {
var api_url = "{% url 'api_like' article.pk %}";
var btn = document.getElementById("like");
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
if (request.readyState === 4 && request.status === 200) {
var received_data = JSON.parse(request.responseText);
btn.innerText = received_data.like;
}
}
request.open("GET", api_url);
request.send();
}
</script>
</head>
<!-- 略 -->
さらに、いいねボタン部分
<p><a href="{% url 'like' article.pk %}"><span id="like">{{ article.like }}<span>いいね!</a></p>
を
<p><a onclick="api_like()"><span id="like">{{ article.like }}</span>いいね!</a></p>
に書き換えます。
これで、設定が完了したので動作チェックしてみます。
詳細ページに移動して、いいねボタンをクリック
きちんと数は増えましたか?
まとめ
モデルを更新したら
python manage.py makemigrations
と
python manage.py migrate
を実行する。
JsonRespons(dictionary)
でjson
のレスポンスを返せる。
最後に
実は、記事の削除機能もいいね機能と同様web API
として機能を提供すれば、ページのリロードなしで削除できたりするんですが、それはまたの機会にでも
次回は、コメント機能を追加してみようと思います。
※2019/1/25一部コマンドが間違っていたので修正