Django Grappelliにおけるカスタムダッシュボードモジュールの作成方法

Grappelliを活用した管理画面のカスタマイズ入門

Django Grappelliは、標準の管理画面(Admin)に洗練されたUIを提供するだけでなく、拡張性の高いダッシュボード機能も備えています。特にDashboardModuleクラスを継承することで、開発者は独自のモジュールを実装し、業務に特化した情報パネルを構築できます。本稿では、カスタムモジュールの設計から実装、最適化までの一連のプロセスを解説します。

基本構造:DashboardModuleの理解

すべてのモジュールはgrappelli.dashboard.modules.DashboardModuleを基底クラスとしています。主な属性とメソッドは以下の通りです。

  • title: モジュール見出し(多言語対応可)
  • collapsible: 折りたたみ可能かどうか
  • template: 使用するテンプレートファイルのパス
  • children: 表示内容のリストまたは辞書形式データ
  • init_with_context(): コンテキストを受け取り、動的データを初期化
  • is_empty(): モジュールが空であるか判定
from grappelli.dashboard import modules

class StatusSummaryModule(modules.DashboardModule):
    title = "システム状況"
    template = "admin/modules/status_summary.html"
    collapsible = True

    def init_with_context(self, context):
        request = context.get("request")
        # セッションやユーザー情報を元にデータ生成
        self.children = {
            "user": request.user.username,
            "last_login": request.user.last_login,
            "pending_tasks": self._get_pending_count(request.user)
        }

    def _get_pending_count(self, user):
        from myapp.models import Task
        return Task.objects.filter(
            assigned_to=user,
            status__in=["new", "in_progress"]
        ).count()

    def is_empty(self):
        return self._get_pending_count(self.context.get("request").user) == 0

テンプレートの実装

カスタムテンプレートはDjangoテンプレートエンジンを使用します。Grappelliの既存スタイルを継承することで、統一感のあるデザインを維持できます。

{% extends "grappelli/dashboard/module.html" %}
{% block module_content %}
  <div class="grp-status-panel">
    <div class="grp-stats-row">
      <span class="label">現在ログイン:</span>
      <strong>{{ children.user }}</strong>
    </div>
    <div class="grp-stats-row">
      <span class="label">未処理タスク:</span>
      <span class="count {% if children.pending_tasks %}warn{% endif %}">
        {{ children.pending_tasks }}
      </span>
    </div>
  </div>
{% endblock %}

ダッシュボードへの登録

カスタムダッシュボードクラスを作成し、モジュールを配置します。通常、dashboard.pyに定義します。

from django.contrib import admin
from grappelli.dashboard import Dashboard
from .modules import StatusSummaryModule

class MyAdminDashboard(Dashboard):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        # 第1カラムにステータス概要を追加
        self.children.append(StatusSummaryModule(column=1))

        # 第2カラムにリンク群をグループ化して追加
        from grappelli.dashboard.modules import LinkList, Group

        self.children.append(Group(
            column=2,
            children=[
                LinkList(
                    title="クイックアクション",
                    children=[
                        ("新規タスク作成", "/admin/myapp/task/add/"),
                        ("レポート出力", "/admin/reports/export/")
                    ]
                ),
                StatusSummaryModule(title="マイ進捗", collapsible=True)
            ]
        ))

# 管理サイトに適用
admin.site.index_template = "admin/custom_index.html"
admin.site.dashboard_class = MyAdminDashboard

高度な機能:非同期更新とキャッシュ制御

外部APIや重いクエリを扱う場合、フロントエンドでの非同期読み込みが有効です。

# views.py
from django.http import JsonResponse
from django.views.decorators.cache import cache_page

@cache_page(60 * 5)  # 5分キャッシュ
def api_system_status(request):
    from myapp.models import Task
    count = Task.objects.filter(status="new").count()
    return JsonResponse({"new_task_count": count})

JavaScript側で定期的に更新:

<script>
document.addEventListener("DOMContentLoaded", function () {
  const updateStatus = () => {
    fetch("/admin/api/system-status/")
      .then(response => response.json())
      .then(data => {
        const el = document.querySelector(".grp-new-task-count");
        if (el) el.textContent = data.new_task_count;
      });
  };

  updateStatus();
  setInterval(updateStatus, 30000); // 30秒ごとに更新
});
</script>

レスポンシブ対応と権限制御

小規模スクリーンでも見やすいようにCSSを調整します。

.grp-status-panel {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
  padding: 12px;
}

@media (min-width: 768px) {
  .grp-status-panel {
    grid-template-columns: auto 1fr;
    align-items: center;
  }
}

さらに、ユーザーのパーミッションに基づいてモジュールの表示を制御できます。

def is_empty(self):
    request = self.context.get("request")
    if not request:
        return True
    return not request.user.has_perm("myapp.view_sensitive_data")

トラブルシューティングとベストプラクティス

  • テンプレートが反映されない場合: TEMPLATE_DIRSにカスタムパスが含まれているか確認
  • データが表示されない: init_with_context内で例外が発生していないかログを確認
  • パフォーマンス低下: 外部リクエストには必ずキャッシュを導入
  • DB負荷軽減: select_related, only()などを積極的に使用

まとめ

Grappelliのモジュールシステムを使いこなせば、単なる管理画面ではなく、業務のハブとなるダッシュボードを構築できます。シンプルなリンク集からリアルタイム更新パネルまで、柔軟にカスタマイズ可能です。保守性を高めるためにも、共通モジュールは別アプリとして独立させることを推奨します。

タグ: Django grappelli dashboard admin-interface Python

7月13日 19:30 投稿