プログラミング学習

DjangoでAjaxを使っていいねボタンを作る

Django ロゴプログラミング学習

こんにちは。リューさん(@Ryu_programs)です。今回の記事ではjQueryを使ってDjangoでAjaxを行っていきたいと思います。

この記事ではDjangoについては詳細な説明をしていないので、Djangoの実装方法などについては以下の記事を読んでみてください。

Djangoで簡単な「文字カウントメモ」を作ってみる
この記事では、「Djangoを勉強したい!」「Pythonで何か作ってみたい」という人に向けて、Djangoを用いた簡単なWebアプリ作成の方法を実際に一緒に作りながら紹介していきます。プログラミング学習の助けになれば幸いです。

はじめに – Ajaxとは?

Ajax(Asynchronous JavaScript and XML)は、それ自体が技術というわけではなく、一種のアプローチです。このAjaxを利用ことで、表示されているページをすべて更新することなく、一部のみを更新することができ、Webアプリケーションの高速化を行うことができます。

よくGoogle MapsがAjaxを利用している例として挙げられます。Google Mapsはドラッグに合わせて、ページをリロードすることなく表示される範囲が滑らかに移動しますが、それはAjaxを利用しているためです。

また、Asynchronous JavaScript and XMLにはXMLの文字が入っていますが、JSONの方が軽く、JavaScriptで扱いやすいなどの理由から最近はXMLよりもJavaScriptの方がよく使われているそうです。今回もJSONを使って実装していきたいと思います。

実装

テンプレートファイルは以下の通りです。今回はjQueryの$.ajaxメソッドを利用しているので、base.htmlでjQueryを読み込んでいます。

getCookieから$.ajaxSetupまでは、csrftokenをいい感じにするために記述しています。送信フォームを使う場合は{% csrf_token %}をつけておけばDjangoが良い感じにしてくれるのですが、ajaxでPOSTする場合はこのようにcookieから拾ってきたcsrftokenの内容をX-CSRFTokenという名前でヘッダに設定する必要があります。

{% extends 'article/base.html' %}

{% block title %}
    My test page
{% endblock %}

{% block content %}
    <ul>
        {% for article in article_list %}
            <li>
                {{ article.article_text }}
                <span class="add_good" id={{ article.id }}>&hearts;</span>
                <span id="good-count-{{article.id}}">{{ article.good_count }}</span>
            </li>
        {% endfor %}
    </ul>
    <button onclick="location.href='{% url 'article:article_create' %}'">
        追加
    </button>
{% endblock %}

{% block extrajs %}
    <script>
        function getCookie(name) {
            var cookieValue = null;
            if (document.cookie && document.cookie !== '') {
                var cookies = document.cookie.split(';');
                for (var i = 0; i < cookies.length; i++) {
                    var cookie = jQuery.trim(cookies[i]);
                    // Does this cookie string begin with the name we want?
                    if (cookie.substring(0, name.length + 1) === (name + '=')) {
                        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                        break;
                    }
                }
            }
            return cookieValue;
        }

        var csrftoken = getCookie('csrftoken');

        function csrfSafeMethod(method) {
            // these HTTP methods do not require CSRF protection
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        }

        $.ajaxSetup({
            beforeSend: function (xhr, settings) {
                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                }
            }
        });

        $('.add_good').on('click', function(e) {
            article_id = $(this).attr("id");
            $.ajax({
                'url': '{% url "article:change_count" %}',
                'type': 'POST',
                'data': {
                    'id': article_id,
                },
                'dataType': 'json'
            }).done( response => {
                const response_id = response.id;
                const good_count = response.good_count;
                const element = '#good-count-' + response_id;
                $(element).text(good_count);
            });

        });
    </script>
{% endblock %}

モデルはこんな感じです。good_countがいいねの数に対応しています。ajaxでArticleのidをもらい、viewで対応するArticleのgood_countを増やしてからjsonで追加後のいいね数を返しています。

from django.db import models

# Create your models here.
class Article(models.Model):
    article_text = models.CharField(max_length=100)
    good_count = models.IntegerField(default=0)
    
    def __str__(self):
        return self.article_text

viewは以下の通りです。add_good_count_for_articleが主にajaxの処理を行う関数です。return JsonResponseでJsonを返却しています。

from django.shortcuts import render
from .models import Article
from django.views.generic import ListView
from django.views.generic.edit import CreateView
from django.urls import reverse
from django.http import JsonResponse

# Create your views here.
class ArticleListView(ListView):
    template_name = 'article_list.html'
    model = Article
    

class ArticleCreateView(CreateView):
    model = Article
    fields = ('article_text', )
    
    def get_success_url(self):
        return reverse('article:article_list')
        

def add_good_count_for_article(request):
    article_id = request.POST.get('id')
    article = Article.objects.get(id=article_id)
    article.good_count += 1
    article.save()
    data = {
        'id': article_id,
        'good_count': article.good_count,
    }
    return JsonResponse(data)

urlは以下のように設定しています。

from django.urls import path
from . import views

app_name = 'article'
urlpatterns = [
    path('', views.ArticleListView.as_view(), name='article_list'),
    path('create/', views.ArticleCreateView.as_view(), name='article_create'),
    path('change/', views.add_good_count_for_article, name='change_count')
]

トリミングと拡大をしているのでだいぶ荒いですが、このようにいいねを押すとリロードを挟まずにいいね数が増えていきます。変更はデータベースにも反映されているので、リロードを行ってもいいね数は変更された値のままです。(ListViewを使っているので、順序は変わるかもしれませんが)

おわりに

今回はDjangoとajaxを利用していいね!を行うことができる簡単なWebアプリを実装しました。このアプリではいいねを何度も押すことができましたが、ユーザやIPアドレスなどで判別して一度しか押せないようにしたり、いいね!したらcssなどで色を変えたりすればより実用的になると思います。

タイトルとURLをコピーしました