タイトル通りの内容を実装する方法を備忘録代わりにご紹介していきます。
もし改善点などありましたら是非ご教示いただけたらと思います!
Ajaxについてはこちらで分かりやすく解説されております。
実装する内容
本記事で実装する内容はこんな感じです。
キーワードを入力し「送信」ボタンを押すと、内部でAPIを叩いてキーワードに関する書籍を取得し、テンプレートに描写しています。
実装
それでは実装していきます。
編集するファイルは下記の通りです。
ファイル名 | 役割 |
---|---|
urls.py | ルーティング |
forms.py | ユーザーが入力するキーワードを定義 |
views.py | リクエスト処理を記述 |
book_title.html | Ajaxを実装するページ(任意) |
※下記のプロジェクト・アプリケーションを作成したという前提で進めていきます。
プロジェクト名:django_project
アプリケーション名:book_app
バージョン:4.1.5
ルーティング
「django_project/」直下の urls.py を下記のように編集します。
from django.urls import path
from . import views
urlpatterns = [
# ... 略 ...
path('book-title/', views.BookTitleView.as_view(), name="book_title"),
]
フォームの定義
ユーザーが入力する値を、アプリケーション内の forms.py に定義します。
from django import forms
class BookTitleForm(forms.Form):
keyword = forms.CharField(label='キーワードを入力してください')
書籍を検索する時の「キーワード」のみ定義します。
テンプレート
「キーワード」を入力するフォームや、取得した書籍の情報を表示させる処理などを記述していきます。
「book_app/templates/」ディレクトリに book_title.html を作成し、下記のように修正します。
機能を果たすための最小限の情報のみ記述しています。
フォーム部分
<h2>書籍を調べよう</h2>
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
<div class="formContent">
<p>{{ field.label_tag }}</p>
{{ field }}
{{ field.errors }}
</div>
{% endfor %}
<button type="submit">送 信</button>
</form>
<div id="result">
<!-- 取得した書籍情報をここに追加していく -->
</div>
<div id=”result”>の子要素として、検索した書籍の情報が格納される予定です。
スクリプト部分
jQueryを用いて、「送信」ボタンを押したときにAjax処理を行うように記述します。
<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) {
// submitイベントの発生元のフォームが持つデフォルトの動作をキャンセルできる。
// これを記述することで、送信ボタンを押した際にリロードされてしまうのを妨げる(prevent)ことができる。
event.preventDefault();
var form = $(this);
$.ajax({
url: form.prop('action'),
method: form.prop('method'),
data: form.serialize(),
timeout: 10000,
dataType: 'json',
})
.done(function(data) {
$.each(data.title, function(index, value) {
$('#result').append('<p>' + value + '</p>');
});
$('#result').append('<p><br><br></p>');
});
});
</script>
$.ajax() 内に、Ajaxリクエストを送信するにあたってのオプションを設定しています。例えば「dataType: ‘json’」については、リクエストをして取得予定のデータの型を記述します。
オプションとキーの種類についてはこちらを参照ください。
また取得したデータを data に格納して、.done(function(data) { ~ }) 内でページにレンダリングする処理を記述しています。
フォーム、スクリプト部分を合併したコード
<h2>書籍を調べよう</h2>
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
<div class="formContent">
<p>{{ field.label_tag }}</p>
{{ field }}
{{ field.errors }}
</div>
{% endfor %}
<button type="submit">送 信</button>
</form>
<div id="result">
<!-- 取得した書籍情報をここに追加していく -->
</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) {
// submitイベントの発生元のフォームが持つデフォルトの動作をキャンセルできる。
// これを記述することで、送信ボタンを押した際にリロードされてしまうのを妨げる(prevent)ことができる。
event.preventDefault();
var form = $(this);
$.ajax({
url: form.prop('action'),
method: form.prop('method'),
data: form.serialize(),
timeout: 10000,
dataType: 'json',
})
.done(function(data) {
$.each(data.title, function(index, value) {
$('#result').append('<p>' + value + '</p>');
});
$('#result').append('<p><br><br></p>');
});
});
</script>
ビューの定義
先ほど作成した BookTitleForm を継承した BookTitleView を作成します。
import json
import requests
from django.urls import reverse_lazy
from django.views import generic
from django.http import HttpResponse
from .forms import BookTitleForm
class BookTitleView(generic.FormView):
template_name = 'book_title.html'
form_class = BookTitleForm
success_url = reverse_lazy('cafe_app:ajax')
このビューに3つのメソッドを追加していきます。
API叩いて書籍情報を取得するメソッド
「https://www.googleapis.com/books/v1/volumes?q=<任意のキーワード>」を叩くことで、キーワードに関する情報を取得できます。
この中からタイトルだけを取得し、配列として返す get_title()メソッドを下記のように定義します。
def get_title(self, keyword):
""" APIを叩いて書籍情報を取得する """
# エンドポイントURL
url = "https://www.googleapis.com/books/v1/volumes?q={}"
# データ取得
res = requests.get(url.format(keyword))
res_json = json.loads(res.content)
# 取得したデータからタイトルのみをリスト化
title_list = [item['volumeInfo']['title'] for item in res_json['items']]
return title_list
POST送信された時のメソッド
def post(self, request, *args, **kwargs):
form = self.get_form(self.form_class)
if form.is_valid():
if request.headers.get('x-requested-with') == 'XMLHttpRequest':
""" Ajax処理を別メソッドに切り離す """
return self.ajax_response(form)
# Ajax以外のPOSTメソッドの処理
return super().form_valid(form)
else:
# フォームバリデーション通過できなかった場合
return super().form_invalid(form)
POST通信リクエストが Ajax通信かどうかを判断し、Trueの場合は ajax_response()メソッド(まだ記述していません)に処理を移すようにしています。
上記の判別を「if request.headers.get(‘x-requested-with’) == ‘XMLHttpRequest’」としていますが、Django3.2系では「if request.is_ajax()」で判別できるそうです。
is_ajax() はDjango4.0で削除されました。
jQueryにレスポンスを返すメソッド
def ajax_response(self, form):
# フォーム入力した「キーワード」を取得する
keyword = form.cleaned_data['keyword']
# 「キーワード」をもとに、書籍情報を取得する
title_list = self.get_title(keyword)
# JSON形式にして渡す
return HttpResponse(json.dumps({
"title": title_list
}))
取得した書籍データをjQuery(テンプレート側)に渡す処理を記述しています。
ビューのコード全体
import json
import requests
from django.urls import reverse_lazy
from django.views import generic
from django.http import HttpResponse
from .forms import BookTitleForm
class BookTitleView(generic.FormView):
template_name = 'book_title.html'
form_class = BookTitleForm
success_url = reverse_lazy('cafe_app:ajax')
def post(self, request, *args, **kwargs):
form = self.get_form(self.form_class)
if form.is_valid():
if request.headers.get('x-requested-with') == 'XMLHttpRequest':
""" Ajax処理を別メソッドに切り離す """
return self.ajax_response(form)
# Ajax以外のPOSTメソッドの処理
return super().form_valid(form)
else:
# フォームバリデーション通過できなかった場合
return super().form_invalid(form)
def get_title(self, keyword):
""" APIを叩いて書籍情報を取得する """
# エンドポイントURL
url = "https://www.googleapis.com/books/v1/volumes?q={}"
# データ取得
res = requests.get(url.format(keyword))
res_json = json.loads(res.content)
# 取得したデータからタイトルのみをリスト化
title_list = [item['volumeInfo']['title'] for item in res_json['items']]
return title_list
def ajax_response(self, form):
# フォーム入力した「キーワード」を取得する
keyword = form.cleaned_data['keyword']
# 「キーワード」をもとに、書籍情報を取得する
title_list = self.get_title(keyword)
# JSON形式にして渡す
return HttpResponse(json.dumps({
"title": title_list
}))
実装内容は以上です!
配列を渡そうとして苦労した話
最初「タイトルを複数格納した配列」をわざわざJSON化せずに、直接渡そうとしていました。
# タイトルの配列
title_list = ['さるかに合戦', '桃太郎', '浦島太郎']
# こうではなくて
return HttpResponse(json.dumps({
"title": title_list
}))
# こうしてた
return HttpResponse(title_list)
このように直接渡すと、jQuery側では ‘さるかに合戦桃太郎浦島太郎’ といった感じで文字列として受け取ってしまいました。
内容を表示するだけなら良いのですが、実際にデータを扱う際は $.each() で各値を取り出してこねくり回したいと思います。
なので色々試行錯誤した結果、上記のようなJSON形式で渡すことで配列としてデータを扱うことがでいました…。
良い案があれば是非ご教示ください…
参考記事
今回参考にさせていただいた記事です。ありがとうございました。
まとめ
DjangoでAjaxを行う方法についてまとめさせていただきました。誰かの助けになれば幸いです。
コメント