「ボタンひとつで、スムーズいいね!— Django × 非同期処理で快適なユーザー体験を実現」
こんにちは、タケトロだよ。寿司界のとろけるアイドル、トロさ! 今日は僕、タケトロが、Djangoのいいねボタンを非同期で実装する話をお届けするよ。
まずは準備、Djangoの厨房へ!
僕らの寿司屋では、記事という名の寿司ネタがずらっと並んでるんだ。今回はその中でも特製の記事に「いいね」をつけてもらうためのボタンを作るよ。
views.py — いいねの裏舞台
寿司ネタに「いいね」を追加するために、like_articleビューを用意したよ。
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from .models import Article
@require_POST
def like_article(request, article_id):
# 寿司ネタの取得
article = get_object_or_404(Article, id=article_id)
# トッピング(いいね)を追加
article.count += 1
article.save()
# 新鮮な状態でお返し
return JsonResponse({"message": "success", "new_count": article.count})
urls.py — すし職人への注文
すし職人たちがこのビューにアクセスできるようにURLを整備したよ。
from django.urls import path
from . import views
urlpatterns = [
path('blog/<int:article_id>/like', views.like_article, name='like_article'),
]
models.py — 寿司ネタの仕込み
寿司ネタに「いいね」をカウントするフィールドを用意。
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
count = models.PositiveIntegerField(default=0) # いいねの数
テンプレート、見た目も大事
さて、寿司屋のショーケースを彩るHTMLを用意するよ。
<div class="d-inline my-1">
<button class="btn btn-sm btn-outline-warning" id="like_btn" type="button" aria-label="Like this article">
<svg class="bi bi-lightning-fill" fill="currentColor" height="16" viewBox="0 0 16 16" width="16"
xmlns="http://www.w3.org/2000/svg">
<path d="M11.251.068a.5.5 0 0 1 .227.58L9.677 6.5H13a.5.5 0 0 1 .364.843l-8 8.5a.5.5 0 0 1-.842-.49L6.323 9.5H3a.5.5 0 0 1-.364-.843l8-8.5a.5.5 0 0 1 .615-.09z"
fill-rule="evenodd"></path>
</svg>
<span id="like_count">{{ article.count }}</span> good
</button>
</div>
JavaScriptで非同期な魔法を
ここからは僕、タケトロの出番さ!いいねボタンをクリックしたとき、ビューに指示を出して、返ってきた結果を受け取るよ。
イベント登録とデータ送信
このコードの冒頭では、DOMContentLoadedイベントが発火したタイミングで初期化が行われる仕組みを作っているよ。特に重要なのは、いいねボタンをクリックしたときにどのようにデータをビューに送信するかという部分。
document.addEventListener("DOMContentLoaded", () => {
const likeBtn = document.getElementById("like_btn");
likeBtn.addEventListener('click', () => {
const url = `/blog/{{ article.id }}/like`;
const csrfToken = getCookie("csrftoken");
fetch(url, {
method: "POST",
credentials: "same-origin",
headers: {
"Content-Type": "application/json",
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": csrfToken,
},
})
.then(response => response.json())
.then(data => {
if (data.message === "success") {
const likeCount = document.getElementById("like_count");
likeCount.innerText = data.new_count;
}
})
.catch(error => console.error("Error:", error));
});
});
function getCookie(name) {
const cookies = document.cookie.split(';');
for (const cookie of cookies) {
const [key, value] = cookie.trim().split('=');
if (key === name) {
return decodeURIComponent(value);
}
}
return null;
}
fetchメソッド: この部分ではfetchを使ってサーバーに非同期リクエストを送信しているよ。リクエストには適切なHTTPヘッダーと、サーバーサイドで必要なCSRFトークンを含めている。
CSRFトークンの取得: セキュリティのために、getCookie関数でCSRFトークンをクッキーから取得してる。
レスポンス処理: サーバーからのレスポンスを受け取り、成功した場合にいいねのカウントを更新。
エラー処理も忘れずに
catchブロックではエラーが発生した場合の対応をしているよ。例えば、ネットワーク障害があった場合でもエラーメッセージを適切に出力できるようにしている。
まとめ
さあ、これでお客様は好きな寿司ネタに「いいね」をつけることができるようになったよ! 僕の寿司屋では、こうして寿司ネタをいつもフレッシュに保っているんだ。
次回はどんな寿司ネタにタケトロが挑戦するか、お楽しみに!