サイトアイコン 知的好奇心

Djangoで定期的なAjax処理(POST)を行う方法

Djangoで定期的なAjax処理(POST)を行う方法をご紹介します。

ここでは、フォームの入力値を指定した間隔で自動POSTを行います。

条件

models.py

models.pyに以下のようにPostクラスを追加します。

from django.db import models


class Post(models.Model):
    """投稿モデル"""
    class Meta:
        db_table = 'post'
    title = models.CharField(verbose_name='タイトル', max_length=10)
    comment = models.CharField(verbose_name='コメント', max_length=100)
    created_at = models.DateTimeField(verbose_name='登録日時', auto_now_add=True)
    updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True)
    def __str__(self):
        return self.title + ',' + self.comment

urls.py

トップ画面にフォーム画面を表示します。
また、Ajax処理用のURLを設定します。

from django.urls import path
from . import views

app_name = 'sampleApp'

urlpatterns = [
    # トップ画面
    path('', views.TestView.as_view(), name='index'),

    # Ajax処理
    path("exec/", views.exec_ajax, name='exec'),
]

forms.py

「forms.Form」のTestFormクラスを定義します。

from django.forms import forms, CharField
from sampleApp.models import Post


class TestForm(forms.Form):
    title = CharField(
        initial='',
        label='タイトル',
        max_length=10,
        required=True,  # 必須
    )
    comment = CharField(
        initial='',
        label='コメント',
        max_length=100,
        required=True,  # 必須
    )
    def save(self):
        # save data using the self.cleaned_data dictionary
        data = self.cleaned_data
        post = Post(title=data['title'], comment=data['comment'])
        post.save()

views.py

フォームの処理には「FormView」を用います。
また、Ajax用のメソッドを定義して、POSTで送られてきたパラメータを取得し、何らかの処理を記述します。

from django.contrib import messages
from django.http import HttpResponse, QueryDict
from django.views.generic.edit import FormView
from sampleApp.forms import TestForm


class TestView(FormView):
    template_name = 'sampleApp/index.html'
    form_class = TestForm
    success_url = '/'  # リダイレクト先URL

    def form_valid(self, form):
        form.save()  # 保存処理など
        messages.add_message(self.request, messages.SUCCESS, '登録しました!')  # メッセージ出力
        return super().form_valid(form)


def exec_ajax(request):
    if request.method == 'POST':  # POSTの処理
        # JSON文字列の取得
        dic = QueryDict(request.body, encoding='utf-8')
        title = dic.get('title')
        comment = dic.get('comment')

        # TODO:何らかの処理を実行する
        if not title and not comment: # 入力値が空の場合
            message = '何もしません。'
        else: # 何か入力されている場合
            # TODO:自動保存処理(DBの値と異なっていた場合、保存する。)
            message = '自動保存しました。' + 'タイトル:' + title + ', コメント:' + comment

        return HttpResponse(message)

テンプレート

index.htmlに、「メッセージ」と「フォーム」を表示するようにします。

また、jqueryを用いて定期的にAjaxのPOST処理が行われるようにします。
クロスサイトリクエストフォージェリ (CSRF) 対策のため、CSRFトークンの取得を行っています。

{% extends 'base.html' %}

{% block content %}
    <h2>フォームサンプル</h2>

    {% if messages %}
    <ul class="messages">
        {% for message in messages %}
        <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
        {% endfor %}
    </ul>
    {% endif %}

    <form method="post" id="form01" >{% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="送信">
    </form>

    <div id="resultPOST"></div>

    <script>
        // csrf_tokenの取得
        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;
        }

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

        $(function() {

            var POLLING_INTERVAL_TIME_IN_MILLIS = 10000; // 実行インターバル(ミリ秒)
            (function polling() {
                AutoSubmit();
                window.setTimeout(polling, POLLING_INTERVAL_TIME_IN_MILLIS);
            }());

            function AutoSubmit() {
                var val_title = $('#form01 [name=title]').val();
                var val_comment = $('#form01 [name=comment]').val();

                if(val_title && val_comment){ // フォームに入力されている場合

                    $.ajax({
                        type : "POST",
                        url : "{% url 'sampleApp:exec' %}",
                        data: {
                           "title": val_title,
                           "comment": val_comment,
                        },
                        contentType: "application/json",
                        timeout: 5000,
                        // AJAXリクエストにトークンをセット
                        beforeSend: function(xhr, settings) {
                            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                                xhr.setRequestHeader("X-CSRFToken", getCookie("csrftoken"));
                            }
                        },
                    }).done(function(data) {
                        // alert("success");
                        $('#resultPOST').text('自動POST処理成功:' + data);
                    }).fail(function(jqXHR, textStatus) {
                        // #alert("error");
                        $('#resultPOST').text('自動POST処理失敗:');
                    });
                }
            }
        });

    </script>
{% endblock %}

実行結果

フォームが未入力の場合

自動POST処理は行われません。

フォームに入力済の場合

自動POST処理が行われます。(送信ボタンを押さなくてもPOSTが行われます。)
index.htmlの「POLLING_INTERVAL_TIME_IN_MILLIS」で設定した間隔で、定期的に実行されます。

参考

Django:クロスサイトリクエストフォージェリ (CSRF) 対策

https://docs.djangoproject.com/ja/3.0/ref/csrf/

stackoverflow:How to process a form (via get or post) using class-based views?

https://stackoverflow.com/questions/8903601/how-to-process-a-form-via-get-or-post-using-class-based-views

jQueryでフォームの値を取得する方法

jQueryでフォームの値を取得する方法をまとめておくので、コピペでご利用ください。

モバイルバージョンを終了