【Django × Ajax】ページの更新せずに内容を変える

タイトル通りの内容を実装する方法を備忘録代わりにご紹介していきます。

もし改善点などありましたら是非ご教示いただけたらと思います!

Ajaxについてはこちらで分かりやすく解説されております。

目次

実装する内容

本記事で実装する内容はこんな感じです。

キーワードを入力し「送信」ボタンを押すと、内部でAPIを叩いてキーワードに関する書籍を取得し、テンプレートに描写しています。

実装

それでは実装していきます。

編集するファイルは下記の通りです。

ファイル名役割
urls.pyルーティング
forms.pyユーザーが入力するキーワードを定義
views.pyリクエスト処理を記述
book_title.htmlAjaxを実装するページ(任意)

※下記のプロジェクト・アプリケーションを作成したという前提で進めていきます。

プロジェクト名: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を行う方法についてまとめさせていただきました。誰かの助けになれば幸いです。

案件、ありますか?

「メインの仕事があるけれど、週1、2日だけできる仕事ないかな、、、」

「ある程度スキルが身に付いてはきたけど、そのスキルを活用できる場が欲しい」

なんて悩みが以前はありました。

自分で仕事を探しに行くのも大事ですが、蛇の道は蛇。その道の人に頼むことで、自分だけでは見つからないような案件に携わることができます。

IT PRO パートナーズでは、簡単に無料でアカウントを登録でき、さらにはエージェントさんに希望の働き方・案件の種類を提示することでお仕事を紹介してくれます!

登録自体も非常に簡単で、「エージェントさんとの面談を希望する」という欄にチェックをするだけで、エージェントさんから直接連絡をいただくことができます。

驚くほど簡単で正直拍子抜けしてしまいました笑

もしお仕事探しに困っておりましたら、一度登録し案件を眺めてみることをおすすめします!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

勤めていた設計会社を退社し、フリーランスとして活動しています
また、趣味で主にpyhonを用いて機械学習を行なっています!
現在競艇の予測モデルの開発中です。

コメント

コメントする

目次