📚

# 4.5 メニュー制御の実装(業務単位での公開制御とUX設計)

に公開

前回の記事では、CustomUser / Dept / Role を使った独自の権限管理について解説した。
今回はその情報を活用し、実際に メニューを業務単位で制御する仕組み と、
Vue Router とF5リロードの違いを考慮した設計のポイント を紹介する。


1. メニュー制御の基本方針

  • メニューは業務単位で定義
    → 「受注管理」「出荷予定」「作業指示」「原価管理」といった業務ごとにメニューを用意する

  • 公開範囲は部署コードで制御
    → 受注管理は営業と生産管理に、作業指示は製造に、原価は経理に…と柔軟に設定可能

  • 段階的公開に対応
    → 本番切替時に「営業1課だけ先行公開、2課は後から追加」といった運用が可能

👉 この仕組みにより、不要なメニューを隠してUXを改善しつつ、運用の柔軟性も確保できる


2. モデル設計

メニューは「業務単位」で定義し、どの部署に公開するかを JSON で管理する。

# core/models/menu.py
from django.db import models

class Menu(models.Model):
    name = models.CharField(max_length=100)  # 業務名 (例: 受注管理)
    url = models.CharField(max_length=255, blank=True)
    parent = models.ForeignKey(
        'self', null=True, blank=True, related_name='children', on_delete=models.CASCADE
    )
    sort_order = models.PositiveIntegerField(default=0)
    icon = models.CharField(max_length=50, blank=True)

    # 公開対象の部署コード(例: ["ALL"], ["SALES", "PROD"], ["ACC"])
    allowed_dept_codes = models.JSONField(default=list)

    def __str__(self):
        return self.name
  • ["ALL"] → 全部署に公開
  • ["SALES", "PROD"] → 営業と生産管理にだけ公開
  • ["ACC"] → 経理にだけ公開

3. API 側での返却

ログイン後のメニューAPIで、ユーザーの所属部署コードと allowed_dept_codes を突き合わせ、
表示可能なメニューだけを返す。

返却例:

[
  { "name": "受注管理", "url": "/orders", "icon": "shopping-cart" },
  { "name": "出荷予定", "url": "/shipments", "icon": "truck" }
]

4. Vue 側での実装

Vue 側はシンプルに API が返したメニューを描画するだけ
これにより Django 側でメニュー公開設定を変えれば、そのままフロントに反映される。

<template>
  <nav>
    <ul>
      <li v-for="item in menuItems" :key="item.url">
        <RouterLink :to="item.url">
          <i :class="item.icon"></i> {{ item.name }}
        </RouterLink>
      </li>
    </ul>
  </nav>
</template>

<script setup>
import { ref, onMounted } from "vue";
import axios from "axios";

const menuItems = ref([]);

onMounted(async () => {
  const res = await axios.get("/api/menus/");
  menuItems.value = res.data;
});
</script>

5. Vue Router と F5 リロードの違い

ここが現場で一番ハマりやすいポイント。

  • メニュークリック時
    Vue Router が制御 → SPA として画面が切り替わる
  • F5リロード時
    ブラウザが直接 Django にリクエスト → Vue Router は関与しない

結果、以下のような現象が起きることがある:

  • クリック時は正常表示 → F5 すると 403 エラー
  • 内部リンクから遷移するとOK → ブックマークから開くとNG

👉 業務システムではこの差を考慮し、

  • API 側で認可を二重防御する
  • Vue Router のルート設計と Django 側のルーティングを整合させる

といった工夫が欠かせない。


6. 運用で得られた効果

  • UX改善
    → 不要なメニューが消え、ユーザーが迷わなくなった
  • 運用効率化
    → メニューの公開/非公開は Django 側のマスタ更新で即反映可能
  • 段階的リリースに対応
    → 部署ごとの本番切替を安全に進められる

7. AI活用のポイント

  • 雛形生成に有効
    Menu モデルのサンプルコードや Vue 側の描画処理は AI がすぐ出してくれるため、開発初期の効率は大きく上がった

  • 独自仕様はAI任せにできない
    AI は「業務単位でメニューを定義し、部署ごとに公開制御する」という実プロジェクト特有の設計までは前提にしてくれない
    → 生成コードはあくまで叩き台であり、実仕様に合わせた調整は人間が行う前提

  • F5問題の指摘は期待できない
    AI は「Vue RouterとF5リロードの挙動差による不具合」まで気づかないことが多い
    → 実際の利用動線を想定した検証は不可欠


まとめ

  • メニューは 業務単位で定義し、部署ごとに公開範囲を設定
  • 公開/非公開の切り替えは Django 側のマスタ更新だけで対応可能
  • Vue 側は API が返すメニューを描画するだけで改修コストゼロ
  • Vue Router とF5リロードの違いを意識し、ルーティングと認可は二重防御する
  • AI は雛形生成には便利だが、独自仕様の考慮や挙動検証は人間の仕事

次回は、認証・権限の延長として 「JWT とセッション管理の実践」 に進む予定。

Discussion