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