DjangoなAJAX

2019-08-01Python,TIPSAJAX,Django

DjangoでもAJAXを使用して非同期通信をさせたい場面は多くあります。Googleマップを組み込んだりページング処理などWEBアプリケーションをより動的にするための選択肢として非常に有効です。
今回はjQueryを使用し簡単に実装してみましょう。

Reactを使用したりJavaScriptのみでAJAXすることもできますが、基本的な考え方はさほど変わりません。

下準備

テンプレートにjQuiryを読み込ませます。
これだけで準備は完了しますが一つ注意が必要です。
それはjQueryとJavaScriptのリソース群はHTMLの最下部に設置することです。

・スクリプトが実行された時にDOMを正常に読み込むことを保証するため
・jQuiryを使用したインラインスクリプトを書かない

以上の2点を理由としてHTMLの最後に記述することを推奨します。
私が普段使用しているbase.htmlはこんな感じです。

{% load static %}<!DOCTYPE html>
<html>
  <head>
      {% block title_outer %}
          <title>{% block title %}{{ site.name }}{% endblock %}</title>
      {% endblock %}

      {% block meta %}
          <meta charset="utf-8">
      {% endblock %}

      {% block stylesheets %}

      {% endblock %}

      {% block extra_head %}
      {% endblock %}
  </head>
  <body>
    {% block body %}
    {% block content %}{% endblock %}
    {% endblock %}

    {% block extra_foot %}{% endblock %}

    <script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
    {% block javascript %}{% endblock %}
  </body>
</html>

ページ固有のJavaScriptなどは{% block javascript %}{% endblock %}のブロックに入れていく感じになります。

サンプル

今回はサンプルとしてユーザー作成画面を作ってみましょう。
アクターが希望するユーザ名やパスワードを入力した時にAJAXを使用し、入力された値に禁止文字が入っていないかなどを簡易的にチェックします。

views.py

from django.contrib.auth.forms import UserCreationForm
from django.views.generic.edit import CreateView

class SignUpView(CreateView):
    template_name = 'signup.html'
    form_class = UserCreationForm

urls.py

from django.contrib import admin
from django.urls import path

from ajax_signup import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('signup/', views.SignUpView.as_view(), name='signup'),
]

signup.html

{% extends 'base.html' %}

{% block content %}
  <h1>AJAXでの入力値確認</h1>
  <form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">登録</button>
  </form>
{% endblock %}

AJAXでの入力値検証

AJAXを使用して入力されたユーザー名が既に登録されているかどうかを検証してみましょう。

まずChromeなどのデベロッパツールを使用して {{form.as_p}} で生成されたHTMLを確認しましょう。

<input type="text" name="username" maxlength="150" autofocus="" required="" id="id_username">

必要なのは要素のidです。
“id_username" を使用しフィールド値が変更された時のイベントを作成します。

早速signup.htmlを編集しましょう。

signup.html

{% extends 'base.html' %}

{% block javascript %}
<script>
    $("#id_username").change(function () {
        console.log( $(this).val() );
    });
</script>
{% endblock %}

{% block content %}
<h1>AJAXでの入力値確認</h1>
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">登録</button>
</form>
{% endblock %}

最初にconsole.log()を使用し正しくイベントが発生していることを確認しましょう。もしここでchangeイベントが発生せずコンソールにログが吐き出されない場合は指定したid名が誤っていないか確認してください。

changeイベントが正常に発生していることを確認できたら入力されたユーザー名が既に使用されているかどうかを確認するviewを書きます。

views.py

from django.contrib.auth.models import User
from django.http import JsonResponse

def validate_username(request):
    username = request.GET.get('username', None)
    data = {
        'is_used': User.objects.filter(username__iexact=username).exists()
    }
    return JsonResponse(data)

ルートも追加しましょう。

urls.py

from django.contrib import admin
from django.urls import path

from ajax_signup import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('signup/', views.SignUpView.as_view(), name='signup'),
    path('ajax/validate_username/', views.validate_username, name='validate_username'),
]

早速jQuiryを使用して実装していきます。

signup.html

{% extends 'base.html' %}

{% block javascript %}
<script>
    $("#id_username").change(function () {
        var username = $(this).val();

        $.ajax({
            url: '/ajax/validate_username/',
            data: {
                'username': username
            },
            dataType: 'json',
            success: function (data) {
                if (data.is_used) {
                    alert("このユーザー名は既に使用されています");
                }
            }
        });

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

{% block content %}
<h1>AJAXでの入力値確認</h1>
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">登録</button>
</form>
{% endblock %}

viewsへユーザー名をJSONで送信しUserModelにデータが存在しているかを確認し結果をテンプレートへ返しています。
しかし、もう少し改善できる部分があります。

アラートメッセージをviews側に渡してみましょう。

views.py

from django.contrib.auth.models import User
from django.http import JsonResponse

def validate_username(request):
    username = request.GET.get('username', None)
    data = {
        'is_used': User.objects.filter(username__iexact=username).exists()
    }
    if data['is_used']:
        data['error_message'] = 'このユーザー名は既に使用されています'
    return JsonResponse(data)

signup.html

{% extends 'base.html' %}

{% block javascript %}
<script>
    $("#id_username").change(function () {
        var form = $(this).closest("form");
        $.ajax({
            url: form.attr("data-validate-username-url"),
            data: form.serialize(),
            dataType: 'json',
            success: function (data) {
                if (data.is_used) {
                    alert(data.error_message);
                }
            }
        });
    });
</script>
{% endblock %}

{% block content %}
<h1>AJAXでの入力値確認</h1>
<form method="post" data-validate-username-url="{% url 'validate_username' %}">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">登録</button>
</form>
{% endblock %}

これでPython側のコードとJavaScript側のコードを分離できました。
HTML側からは純粋にJSONを渡し表示するだけです。

今回のコードもGitHubへアップしています。
https://github.com/mila411/django-ajax

2019-08-01Python,TIPSAJAX,Django

Posted by Kenny