Django API でデータが取得できても表示されない問題と解決方法

# はじめに
DjangoでAPIを利用してデータを取得するページを作った際に、「JSONデータがそのままブラウザに表示される」 という問題に遭遇しました。

# エラー内容
今回、DjangoのAPI を利用してデータを取得するページを作ったところ、「JSONデータがそのままブラウザに表示される」 という問題に遭遇しました。

理想としては、フォームと取得データを組み合わせたページ表示を期待値としていました。
「なぜ？」という視点でデバッグを進めた結果、API のデータをフロントに適切に渡すための重要なポイントが見えてきた。



# 原因の特定

画面に表示されたJSON情報
```
  "obtained_batches": [
    {
      "id": 3,
      "name": "バッジA",
      "description": "3本の映画を視聴",
      "icon": "badges/tree-movie_YI1I6m6.jpeg"
    },
    {
      "id": 4,
      "name": "バッジB",
      "description": "1日3本の映画を視聴",
      "icon": "badges/Triple_Movie_Streak.jpg"
    }
  ],
  "unobtained_batches": []
}
```

問題の原因を特定するため、以下の3つの仮説を立てました。

🔹仮説1：ビューが JSON 形式でデータを返している
　　→ Djangoのビューで JsonResponse を使っているため
  　　　HTML ではなく JSONデータのみをレスポンスとして
    　　　返している 可能性。

🔹仮説2：フロントエンド側の処理が不足している
　　→ API からデータを取得した後、それを HTML に
  　　　適用する処理が不足している 可能性。

🔹 仮説3：APIのエンドポイント設計の問題
　　→ もともとこのエンドポイントは「データのみを返す」
  　　　仕様だった？
     
　　→ ページで表示するには、別途 HTML を適用する
  　　ビュー必要だったのでは？



# 修正対応
Django 側の修正（views.py）

問題は 「データ取得後のフロント側の処理が抜けていた」 ことだったため、以下のように JSON を取得後、JavaScript 側で描画処理を行うよう修正 しました。

```views.py
from django.http import JsonResponse
from django.views import View
from .models import UserBatch, Batch

class BatchListView(View):
    def get(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return JsonResponse({"error": "Unauthorized"}, status=401)

        user = request.user
        obtained_batches = UserBatch.objects.filter(user=user).values_list('batch', flat=True)
        obtained_batches_list = Batch.objects.filter(id__in=obtained_batches).values("id", "name", "description", "icon")
        unobtained_batches_list = Batch.objects.exclude(id__in=obtained_batches).values("id", "name", "description", "icon")

        return JsonResponse({
            "obtained_batches": list(obtained_batches_list),
            "unobtained_batches": list(unobtained_batches_list),
        })

```

フロントエンドの修正（JavaScript）
```batch_list.js
// UI を更新する関数（取得済み・未取得のバッジを表示）
window.updateUI = function () {
    if (!window.apiData || typeof window.apiData !== "object") return;

    const obtainedBadgesElement = document.getElementById("obtained-badges");
    const unobtainedBadgesElement = document.getElementById("unobtained-badges");

    if (!obtainedBadgesElement || !unobtainedBadgesElement) return;

    // 取得済みバッジのリストを更新
    obtainedBadgesElement.innerHTML =
        window.apiData.obtained_batches.length > 0
            ? window.apiData.obtained_batches.map(badge => `
                <div class="badge-item">
                    <img src="${badge.icon || '/static/images/default_badge.png'}" class="badge-img" alt="${badge.name}">
                    <p class="badge-title">${badge.name}</p>
                </div>
            `).join("")
            : "<p>まだ取得済みのバッジはありません。</p>";

    // 未取得バッジのリストを更新
    unobtainedBadgesElement.innerHTML =
        window.apiData.unobtained_batches.length > 0
            ? window.apiData.unobtained_batches.map(badge => `
                <div class="badge-item badge-unobtained">
                    <img src="${badge.icon || '/static/images/default_badge.png'}" class="badge-img" alt="${badge.name}">
                    <p class="badge-title">${badge.name}</p>
                </div>
            `).join("")
            : "<p>未取得のバッジはありません。</p>";
};

// バッジデータ取得に失敗した場合、エラーメッセージを表示
function showError(message) {
    document.getElementById("obtained-badges").innerHTML = `<p class='text-danger'>${message}</p>`;
    document.getElementById("unobtained-badges").innerHTML = `<p class='text-danger'>${message}</p>`;
}

// APIからバッジデータを取得する関数
async function loadBatches() {
    try {
        const response = await fetch("/missions/user_batches", {
            method: "GET",
            credentials: "include",
            headers: {
                "Content-Type": "application/json",
                "Accept": "application/json"
            }
        });

        if (!response.ok) throw new Error(`API エラー: ${response.status} ${response.statusText}`);

        window.apiData = await response.json();
        updateUI();

    } catch (error) {
        showError("バッジデータの取得に失敗しました。");
    }
}

// ページロード時にバッジデータを取得
document.addEventListener("DOMContentLoaded", loadBatches);

```

# 修正結果
修正前
JSON がそのまま表示される
フロントエンドでデータを受け取れていない

修正後
updateUI() 関数を実装し、データを HTML に適用
バッジのリストが正常にレンダリングされるようになった 


# 今回の学び
✅ APIを利用すると、データの流れが変わる
Djangoのテンプレートエンジンでは、バックエンドでデータを直接 HTML に埋め込むが、API ではデータ取得と描画を別々に考える必要がある。


✅ APIのレスポンスが正しくても、データをフロントで適切に描画しなければ意味がない
「データはあるのに表示されない」 という現象は、フロント側の処理不足が原因であることが多い。

✅ フロントエンドとバックエンドの「データの受け渡し」を意識することが大事