『Django Ajax』で検索するといくつか記事がヒットするがほとんどが関数ベースビューによるものだったので、汎用ビューを使用したコードを解説。
汎用ビューよく分かってない方はまずはそちらに入門しましょう。
これは私見ですが、Djangoを採用する大きなメリットの1つがクラスベースである汎用ビューを利用することだと考えています。
たぶんこれがベストプラクティスだと思います。
完成品イメージ
GitHub - skokado/django_ajax: Django + Ajax サンプル
入力した文字列を画面上に表示するというもの。
簡単に仕様を決めておく。
- ユーザは
/greet
にブラウザでアクセスする - フォームに入力した値(
name
)はPOSTメソッドで送信する- ※POSTメソッドの送信先パスは画面の表示と同じ
/greet
- ※POSTメソッドの送信先パスは画面の表示と同じ
-
views.py
ではAjaxリクエスト(XMLHttpRequest)を識別してそれ以外の処理と切り離す - Ajaxリクエストに対して
こんにちは、[name]さん!
というメッセージを<p>
タグで画面内に表示する
コード解説
コンポーネントは大きく以下。
-
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
のみ。
from django.urls import path
from . import views
urlpatterns = [
path('greet/', views.GreetView.as_view(), name='greet'),
]
forms.py
name
を入力するだけ
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
を継承したGreetView
1つを定義する
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_WITH
にXMLHttpRequest
がセットされる場合にのみ使える。
jQueryはこれをちゃんとセットしてくれるっぽい。
※jQueryを使わない(素のJavaScript)で少し試したところ上手くいきませんでした。
- 検証コード
views.py
のGreetView
を以下のように書き換えてリクエストヘッダを覗いてみる。
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
と組み合わせるとどんなことができるか試してみたい