21
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DjangoでAjax(汎用クラスビューを使用)

Last updated at Posted at 2020-06-02

『Django Ajax』で検索するといくつか記事がヒットするがほとんどが関数ベースビューによるものだったので、汎用ビューを使用したコードを解説。

汎用ビューよく分かってない方はまずはそちらに入門しましょう。

これは私見ですが、Djangoを採用する大きなメリットの1つがクラスベースである汎用ビューを利用することだと考えています。
たぶんこれがベストプラクティスだと思います

完成品イメージ

GitHub - skokado/django_ajax: Django + Ajax サンプル

入力した文字列を画面上に表示するというもの。

簡単に仕様を決めておく。

  • ユーザは/greetにブラウザでアクセスする
  • フォームに入力した値(name)はPOSTメソッドで送信する
    • ※POSTメソッドの送信先パスは画面の表示と同じ/greet
  • views.pyではAjaxリクエスト(XMLHttpRequest)を識別してそれ以外の処理と切り離す
  • Ajaxリクエストに対してこんにちは、[name]さん!というメッセージを<p>タグで画面内に表示する

1.gif

コード解説

コンポーネントは大きく以下。

  • urls.py: ルーティング(URLバイディング)
  • forms.py: ユーザがデータを入力するためのフォームを定義
  • views.py: リクエスト処理を記述。
    • 汎用ビューのFormViewを使う
    • Request.is_ajax()を使用してAjaxリクエストを識別する
  • テンプレート(index.html): 表示するHTML。本記事ではjQueryも同梱する。

準備

DjangoプロジェクトとDjangoアプリケーションの作成を適当に済ませておく。
※プロジェクト名: django_ajax, アプリケーション名: greeting とする。

urls.pyにルーティングルールを記載。

urls.py

使用するパスは/greetのみ。

django_ajax/urls.py
from django.urls import path

from . import views


urlpatterns = [
    path('greet/', views.GreetView.as_view(), name='greet'),
]

forms.py

nameを入力するだけ

greeting/forms.py
from django import forms


class GreetForm(forms.Form):
    name = forms.CharField(label='あなたの名前は?')

index.html

  • フォーム部分

<div id="result">の要素配下にこんにちは、[name]さん!という文言が追加されていく。
(と、いう処理をjQueryに記述する)

<h1>Let's Greeting!</h1>
<form action="" method="post">{% csrf_token %}
    {{ form }}
    <button type="submit">送信</button>
</form>

<div id="result">
    <!-- ここにあいさつ文の <p> タグ要素が追加されていく -->
</div>
  • スクリプト部分

jQuery部分についても上述の仕様通りになっていることが何となく読み取れると思う。
また、(slimでない)jQueryが必要なのでロードする。

<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script>
// 送信ボタンにイベントリスナーを設定。内部に Ajax 処理を記述
$("form").submit(function(event) {
    event.preventDefault();
    var form = $(this);
    $.ajax({
      url: form.prop("action"),
      method: form.prop("method"),
      data: form.serialize(),
      timeout: 10000,
      dataType: "text",
    })
    .done(function(data) {
        $("#result").append("<p>" + data + "</p>");
    })
});
</script>
上記をがっちゃんこした`index.html`
<h1>Let's Greeting!</h1>
<form action="" method="post">{% csrf_token %}
    {{ form }}
    <button type="submit">送信</button>
</form>

<div id="result">
    <!-- Will be replaced with inputed text by Ajax -->
</div>

<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script>
// 送信ボタンにイベントリスナーを設定。内部に Ajax 処理を記述
$("form").submit(function(event) {
    event.preventDefault();
    var form = $(this);
    $.ajax({
      url: form.prop("action"),
      method: form.prop("method"),
      data: form.serialize(),
      timeout: 10000,
      dataType: "text",
    })
    .done(function(data) {
        $("#result").append("<p>" + data + "</p>");
    })
});
</script>

views.py

FormViewを継承したGreetView1つを定義する

greeting/views.py
from django.http import HttpResponse
from django.views.generic import FormView

from . import forms

# Create your views here.
class GreetView(FormView):
    template_name = 'index.html'  # テンプレート名(htmlファイル名)
    form_class = forms.GreetForm
    success_url = '/greet'

    def post(self, request, *args, **kwargs):
        form = self.get_form(self.form_class)
        if form.is_valid():
            if request.is_ajax():
                """Ajax 処理を別メソッドに切り離す"""
                print('### Ajax request')
                return self.ajax_response(form)
            # Ajax 以外のPOSTメソッドの処理
            return super().form_valid(form)
        # フォームデータが正しくない場合の処理
        return super().form_invalid(form)

    def ajax_response(self, form):
        """jQuery に対してレスポンスを返すメソッド"""
        name = form.cleaned_data.get('name')
        return HttpResponse(f'こんにちは、{name}さん!')

ちなみに

views.py内のif request.is_ajax():について、公式ドキュメントには以下のようにある。

参照 - リクエストとレスポンスのオブジェクト | Django ドキュメント | Django

(はんなり和訳)

HttpRequest.is_ajax()

HTTP_X_REQUESTED_WITHヘッダの'XMLHttpRequest'文字列で
リクエストがXMLHttpRequest経由によるものかどうかをチェックし、Trueを返します。
多くのモダンなJavaScriptライブラリはこのヘッダを送信します。

つまり、このメソッドはリクエストヘッダのHTTP_X_REQUESTED_WITHXMLHttpRequestがセットされる場合にのみ使える。
jQueryはこれをちゃんとセットしてくれるっぽい。
jQueryを使わない(素のJavaScript)で少し試したところ上手くいきませんでした

  • 検証コード

views.pyGreetViewを以下のように書き換えてリクエストヘッダを覗いてみる。

class GreetView(FormView):
    template_name = 'greet.html'
    form_class = forms.GreetForm
    success_url = '/greet'

    def post(self, request, *args, **kwargs):
        # ★追加
        for k, v in request.META.items():
            print(k, v)
        # ...(以下同様)
  • 結果

※デバッグモードだと大量のリクエストヘッダが全て出力されるので抜粋

...
HTTP_ACCEPT text/plain, */*; q=0.01
HTTP_X_REQUESTED_WITH XMLHttpRequest  # <= これ
HTTP_USER_AGENT Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36
...

まとめ

以上、汎用ビューによる Django のAjax サンプルを記載した。
他の記事よりも簡潔に実装できていると思う。
他にも、StreamingHttpResponseと組み合わせるとどんなことができるか試してみたい

21
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?