【Django】非同期処理でプルダウンメニューを使ったリスト表示方法【Fetch API】

アイキャッチ画像
  • Djangoで非同期処理を実装するには?
  • プルダウンメニューで特定のリストを表示するには?
  • Fetch APIの使い方とは?

本記事ではこのような疑問を解決します。


Djangoにおいて、非同期処理でプルダウンメニューを使ったリスト表示をしたい場面ってあると思います。

例えば、プルダウンメニューの商品カテゴリーで「ドリンク」を選択したら、ソフトドリンクやアルコールメニューがパッと表示されるようなシーンです。

このような場合、JavaScriptを使った、Djangoとテンプレート間のデータの受け渡し処理が必要になります。


そこで今回はDjangoにおける非同期処理でプルダウンメニューを使ったリスト表示方法を解説していきます。

前提条件

今回は飲食店での以下のようなメニュー設定を想定します。

○商品カテゴリー
・ドリンク(ソフトドリンク、アルコール)
・パスタ(トマト系ソース、クリーム系ソース)

※・”大”カテゴリー名(”小”カテゴリー名)

○商品
・オレンジジュース(ソフトドリンク)
・ウーロン茶(ソフトドリンク)
・ハイボール(アルコール)
・レモンサワー(アルコール)
・アマトリチャーナ(トマト系ソース)
・ペスカトーレ(トマト系ソース)
・カルボナーラ(クリーム系ソース)
・ポルチーニクリームパスタ(クリーム系ソース)

※・商品名(”小”カテゴリー名)

なお、機能としてはプルダウンメニューで「カテゴリー(大)」を選択すると、選択された「カテゴリー(大)」の中の「カテゴリー(小)」に紐づいた商品(商品名・金額)が一覧で表示されるというものです。


上記のメニュー設定から、models.pyにて以下のモデルを定義します。

from django.db import models


class BigCategory(models.Model):
    class Meta:
        verbose_name = "カテゴリー(大)"
        verbose_name_plural = verbose_name
    
    big_category_id = models.IntegerField(primary_key=True, unique=True)
    title = models.CharField(max_length=50)

    def __str__(self):
        return self.title


class SmallCategory(models.Model):
    class Meta:
        verbose_name = "カテゴリー(小)"
        verbose_name_plural = verbose_name
    
    small_category_id = models.IntegerField(primary_key=True, unique=True)
    title = models.CharField(max_length=50)
    big_category = models.ForeignKey(BigCategory, on_delete=models.CASCADE)

    def __str__(self):
        return self.title


class Item(models.Model):
    class Meta:
        verbose_name = "商品"
        verbose_name_plural = verbose_name
    
    item_id = models.IntegerField(primary_key=True, unique=True)
    title = models.CharField(max_length=50)
    price = models.IntegerField()
    small_category = models.ForeignKey(SmallCategory, on_delete=models.CASCADE)

    def __str__(self):
        return self.title

なお、データベースでのレコードは以下の通りです。

○カテゴリー(大)テーブル

big_category_idtitle
1“ドリンク”
2“パスタ”

○カテゴリー(小)テーブル

small_category_idtitlebig_category
1“ソフトドリンク”1(”ドリンク”)
2“アルコール”1(”ドリンク”)
3“トマト系ソース”2(”パスタ”)
4“クリーム系ソース”2(”パスタ”)

○商品テーブル

item_idtitlepricesmall_category
1“オレンジジュース”4001(”ソフトドリンク”)
2“ウーロン茶”4001(”ソフトドリンク”)
3“ハイボール”5002(”アルコール”)
4“レモンサワー”6002(”アルコール”)
5“アマトリチャーナ”12003(”トマト系ソース”)
6“ペスカトーレ”13003(”トマト系ソース”)
7“カルボナーラ”11004(”クリーム系ソース”)
8“ポルチーニクリームパスタ”14004(”クリーム系ソース”)

バックエンド側の手順

まずはDjangoでの記述をしていきます。

①URLを設定する

urls.pyにてパスを以下のように記述します。

from django.urls import path
from . import views
from django.conf import settings
from django.conf.urls.static import static

app_name = 'app'
urlpatterns = [
    path('menu_list', views.menu_list, name='menu_list'),
    path('menu_list_info', views.menu_list_info, name='menu_list_info'),
]+ static(settings.STATIC_URL, document_root=settings.STATICFILES_DIRS)

②処理を記述する

views.pyにて、処理を以下のように記述します。

from django.shortcuts import render
from .models import BigCategory, SmallCategory, Item
from django.http import JsonResponse

def menu_list(request):
    big_category = BigCategory.objects.all()
    item_all = Item.objects.all()
    template_name = 'menu_list.html'
    context = {
        "big_category": big_category,
        "item_all": item_all,
    }
    return render(request, template_name, context) 


def menu_list_info(request):
    selected_big_category = request.POST.get('big_category')
    if selected_big_category == 'all_menu':
        item_list = Item.objects.all()
        item_list = list(item_list.values())
    else:
        big_category = BigCategory.objects.get(big_category_id=selected_big_category)
        small_category = SmallCategory.objects.filter(big_category=big_category)
        item_list = Item.objects.filter(small_category__in=small_category)
        item_list = list(item_list.values())
    data = {
        "item_list": item_list,
    }
    return JsonResponse(data)

フロントエンド側の手順

こちらではHTMLとJavaScriptの記述を行います。

今回は機能の解説に焦点を当てているため、CSSの記述は省略します。

①テンプレートを作成する

表示するテンプレート(HTML)を以下のように記述します。

<!DOCTYPE html>
{% load static %}
<html lang="ja">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>メニューリスト</title>
        <meta name="description" content="">
    </head>

    <body>

        <main>
            <section>
                <h1>商品一覧</h1>
                <select id="category_select" required>
                    <option value="すべて" id="all_menu">すべて</option>
                    {% for b in big_category %}
                    <option value="{{ b.title }}" id="{{ b.big_category_id }}">{{ b.title }}</option>
                    {% endfor %}
                </select>
                <div class="list-container" id="list-container">
                {% for item in item_all %}
                <div class="product-box">
                    <div class="product-txt-box">
                        {{ item.title }}<br>{{ item.price }}円(税込)
                    </div>
                </div>
                {% endfor %}
                </div>
            </section>
        </main>

        <script src="{% static 'js/menu_info.js' %}"></script>

    </body>

</html>

②JavaScriptを記述する

①で作成したHTMLファイルの中で以下のJavaScriptファイルを読み込みます。

// ①Django側にPOST送信する際に記述する"お決まりのコード"
const getCookie = (name) => {
    if (document.cookie && document.cookie !== '') {
        for (const cookie of document.cookie.split(';')) {
            const [key, value] = cookie.trim().split('=');
            if (key === name) {
                return decodeURIComponent(value);
            }
        }
    }
};
const csrftoken = getCookie('csrftoken');


// ②選択されたセレクトメニュー情報をDjango側にPOST送信してデータを取得する
// セレクトメニュー内の要素を取得する
const categoryList = document.getElementById('category_select');
// セレクトメニューの値が変更された時に実行される処理
categoryList.addEventListener('change', (event) => {
    // セレクトメニュー内で選択された値の順番を取得する
    const selectedCategoryId = categoryList.selectedIndex;
    // セレクトメニュー内で選択された値のidを取得する
    const selectedCategory = categoryList[selectedCategoryId].id;
    // 非同期処理を記述する
    async function menu_list() {
        const url = '/menu_list_info';
        let res = await fetch(url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
                    'X-CSRFToken': csrftoken,
                },
                body: `big_category=${selectedCategory}`
            });
            let json = await res.json();
            let item_list = json.item_list;
            let list_container = document.getElementById('list-container');
            list_container.innerHTML = '';
            for (let item of item_list) {
                let product_box = document.createElement('div');
                let product_txt_box = document.createElement('div');
                product_box.className = "product-box";
                product_txt_box.className = "product-txt-box";
                product_txt_box.innerHTML = `${item.title}<br>${item.price}円(税込)`;
                product_box.appendChild(product_txt_box);
                list_container.appendChild(product_box);
            }
        }
    // 定義した関数を実行する
    menu_list();
});

完成画面を確認

まとめ

以上がDjangoにおける非同期処理でプルダウンメニューを使ったリスト表示方法になります。

イマドキのWebアプリでは動的なWebページが求められており、非同期処理は必須の機能と言えるでしょう。

本記事はDjango×JavaScriptでの非同期処理の基本であるため、様々な開発に応用が可能です。

ぜひあなたのWebアプリ開発に活かしてみてください!

関連記事

Djangoが学べるプログラミングスクールとは?おすすめのDjangoスクールとは? 本記事ではこのような疑問を解決します。DjangoはPythonで作られた中で1番メジャーなWebフレームワークです。みんなが普段利用するYouT[…]

アイキャッチ画像