[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一部コマンドが間違っていたので修正
