【django】Ajaxによる非同期通信:動的にページ更新する方法

2021.03.18 /

【django】Ajaxによる非同期通信:動的にページ更新する方法

本記事ではDjangoでAjax非同期通信を使用して、動的にページ更新する方法を解説しています。

Ajaxを使うことで、ページ遷移(ページの読み込み)を行うことなく、ページの一部のみを更新することができます。1からページを読み込むことがないため、様々なメリットが得られます。

初心者にも理解しやすいように、Ajaxについて詳しく解説しています。

Django環境の構築から、Ajaxを用いたサンプルプログラムまで解説していますので、コピペするだけでもAjaxを使ったページを作成できるようになっています。
ぜひご自分のパソコンで試してみてください。

本記事を通して以下の知識を学べます。

学べる知識
  • Ajaxについて
  • Ajaxのデータ形式(JSON)
  • DjangoでのAjaxの実装方法

Ajax

Ajaxとは

Ajaxとは「Asynchronous JavaScript + XML」の略で、JavaScriptとXMLを使用してWebサーバと非同期通信を行う技術(手法)です。

具体的には、JavaScriptのHTTP通信機能を用いてWebサーバとの非同期通信を実現し、WebサーバとXMLデータをやりとりします。

IT用語の確認

非同期通信とは、コンピュータ間でデータの送信・受信のタイミングを合わさずに通信を行える通信方式。要求を出したコンピュータは、応答を待つことなく別の処理を行えます

通常、ブラウザでWebサーバと通信を行うと、ページの読み込みを1から行います。そのためユーザーはページ読み込みが終わるまで次の操作ができません。

Ajaxではページはそのままの状態でWebサーバと通信を行い、動的にページを変更します。
ページ全体の更新を行わずページの一部のみを更新できるので、スピードが速く、サーバとの通信が終わることを待たずにページ操作が可能です。
代表的なサイトとして、Googleマップがあります。Googleマップの登場により、Ajaxは世間に知られるようになりました。

Ajaxのメリット

以下にAjaxを使うことのメリットを記します。

  • ページ全体の更新を行わず(ページ遷移がない)に、ページの一部のみを更新できる
  • Webサーバと通信中にページ操作が可能
  • ページ読み込み(ページ遷移)がないため、画面が真っ白になることがない

データ形式

もともとはXMLによるデータのやりとりを行っていました。

ですが現在では、通信時のデータ量の削減が可能なことからJSONが使用されることが一般的です。

本記事で説明するAjaxも、JSONによる非同期通信を行っています。

JSONとは、JavaScript Object Notationの略で、読み書きが簡単、軽量という特徴を持っています。

JSONのデータは以下のように記述します。

{ "name": "Office", "age": 54 }

JSONについてより詳しく学びたい方は、以下記事をご参照ください。

Ajaxの準備

Python環境の構築(Anacondaのインストール)

仮想環境の作成

Djangoのインストールおよび基本操作

サンプルプログラム

次のようなページをAjaxで作成してみます。

作成サンプルプログラム:Ajaxページ

数値を入力して計算ボタンを押すと、入力した数値の足し算・引き算の結果が表示されます。

作成サンプルプログラム:Ajaxページの動作

結果の表示はページが再読み込みされることなく、非同期通信により表示されます。

HTML + Ajax

<!doctype html>
<html lang="ja">
  <head>
    <title>Ajax</title>
  </head>
  <body>
      <div class="container">
          <h2>数値の入力</h2>
          <form id="ajax-number" action="{% url 'blog:ajax_number' %}" method="POST">
              {% csrf_token %}
              <input type="number" id="number1" required>
              <input type="number" id="number2" required>
              <button type="submit" >計算</button>
          </form>

          <h2>数値の計算</h2>
          <div class="result">
          </div>
      </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <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);
                }
            }
        });

        $('#ajax-number').on('submit', function(e) {
            e.preventDefault();

            $.ajax({
                'url': '{% url "blog:ajax_number" %}',
                'type': 'POST',
                'data': {
                    'number1': $('#number1').val(),
                    'number2': $('#number2').val(),
                },
                'dataType': 'json'
            })
            .done(function(response){
                $('.result').prepend('<p>引き算結果:' + response.minus + '</p>');
                $('.result').prepend('<p>足し算結果:' + response.plus + '</p>');
            });
        });
    </script>
  </body>
</html>

urls.py

from django.urls import path
from . import views

app_name = 'blog'
urlpatterns = [
    path('', views.index, name='index'),
    path('ajax-number/', views.ajax_number, name='ajax_number'),
]

views.py

from django.shortcuts import render
from django.conf import settings
from django.http import JsonResponse

def index(request):
    return render(request, 'index.html', {})

def ajax_number(request):
    number1 = int(request.POST.get('number1'))
    number2 = int(request.POST.get('number2'))
    plus = number1 + number2
    minus = number1 - number2
    d = {
        'plus': plus,
        'minus': minus,
    }
    return JsonResponse(d)

Ajaxでの非同期通信方法

jQueryの読み込み

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Ajax前のおまじない

Ajaxによる処理を記入する前に、以下のおまじないを記入します。

  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);
          }
      }
  });

これはCSRFトークンの処理で、Ajax通信でデータをPOSTする場合は必ず記述しておいてください。

submitイベントを検知する

htmlファイルにAjax(jQuery)を記述していきます。

まずsubmitが押下されたら、処理が走るように次のように記述をします。

$('#ajax-number').on('submit', function(e) {
	ここに処理を書く
});

form送信を防止する

フォームでsubmitが押されたときに、フォーム送信の通信を止めるためにpreventDefault()を使用します。

event.preventDefault();

Ajaxの構造

Ajaxは以下のような3つの構造(ブロック)に分かれています。

  • $.ajax:サーバに送信するデータの設定
  • .done:通信成功時の処理
  • .fail:通信失敗時の処理
$.ajax({
    サーバに送信するリクエストの設定
)}
.done(function(){
    通信成功時の処理
})
.fail(function(){
    通信失敗時の処理
})

サーバに送信するリクエストの設定

$.ajaxに記載する各引数について説明します。$.ajaxには次のように引数を指定します。

$.ajax({
    'url': '{% url "blog:ajax_number" %}',
    'type': 'POST',
    'data': {
            'number1': $('#number1').val(),
            'number2': $('#number2').val(),
    },
    'dataType': 'json'
})

$.ajax関数の引数には、上記からわかるように連想配列を指定します。キーと値の組み合わせでリクエストの設定を行います。

以下に示すようなキーと値の組み合わせを指定します。

url:リクエストを送信するURLを指定

type:HTTPメソッドのGET通信かPOST通信を指定

data:サーバに送信するデータの指定

dataType:データ形式(ここではjson)を指定します。

通信成功時の処理

次のようにして、views.pyから受け取ったJSONデータをページに表示しています。

.done(function(response){
    $('.result').prepend('<p>引き算結果:' + response.minus + '</p>');
    $('.result').prepend('<p>足し算結果:' + response.plus + '</p>');
});

views.pyでのAjax受信データの処理

サンプルプログラムでは次のようにviews.pyで処理を行っています。

def ajax_number(request):
    number1 = int(request.POST.get('number1'))
    number2 = int(request.POST.get('number2'))
    plus = number1 + number2
    minus = number1 - number2
    d = {
        'plus': plus,
        'minus': minus,
    }
    return JsonResponse(d)

Ajaxによる非同期通信で送信されたデータは、request.POST.get()を使用して取得します。

返したいデータを辞書にまとめたら、JsonResponseを使用して返します。

クライアント側でJsonResponseによるデータを受信できたら、Ajaxの構造「通信成功時の処理」が走る、という流れです。

まとめ

本記事「【Django】Ajaxによる非同期通信:動的にページ更新する方法」はいかがでしたか。
Ajaxについて知らない方にも理解しやすかったと思います。

Ajaxが使えるようになれば、作成できるWebアプリケーションの幅が非常に広がります。

ぜひ本記事を参考にして、Ajaxを使用したWebアプリケーションを作成してみてください。