Skip to content

Dashboards

This guide explains how dashboards work in django-admin-deux and how they display your registered ModelAdmins.

Overview

django-admin-deux provides two types of dashboards:

  1. Project Dashboard - Shows all apps and their models
  2. App Dashboard - Shows all models within a specific app

Both dashboards serve as navigation hubs, displaying available ModelAdmins and their actions.

Project Dashboard

Accessing

Navigate to /djadmin/ (or your configured admin path) to see the project dashboard.

What It Shows

The project dashboard displays: - All Django apps that have registered ModelAdmins - All ModelAdmins within each app - General actions for each ModelAdmin (typically a ListView link)

Example Layout

┌─────────────────────────────────────────┐
│  Dashboard                              │
├─────────────────────────────────────────┤
│                                         │
│  Webshop                                │
│  ┌─────────────────────────────────────┐│
│  │ Categories      [View List] [Add]   ││
│  │ Products        [View List] [Add]   ││
│  │ Orders          [View List] [Add]   ││
│  └─────────────────────────────────────┘│
│                                         │
│  Blog                                   │
│  ┌─────────────────────────────────────┐│
│  │ Posts           [View List] [Add]   ││
│  │ Comments        [View List] [Add]   ││
│  └─────────────────────────────────────┘│
└─────────────────────────────────────────┘

URL Patterns

# Project dashboard
reverse('djadmin:index')  # /djadmin/

# With namespace
reverse('djadmin:index', current_app='djadmin')

Template

The project dashboard uses: - djadmin/project_dashboard.html (from theme plugin) - Context includes app_list with all apps and their ModelAdmins

App Dashboard

Accessing

Navigate to /djadmin/<app_label>/ to see a specific app's dashboard.

What It Shows

The app dashboard displays: - The app name - All ModelAdmins registered for that app - General actions for each ModelAdmin

Example Layout

┌─────────────────────────────────────────┐
│  Webshop                                │
├─────────────────────────────────────────┤
│  ┌─────────────────────────────────────┐│
│  │ Categories      [View List] [Add]   ││
│  │ Products        [View List] [Add]   ││
│  │ Customers       [View List] [Add]   ││
│  │ Orders          [View List] [Add]   ││
│  │ Reviews         [View List] [Add]   ││
│  │ Tags            [View List] [Add]   ││
│  └─────────────────────────────────────┘│
└─────────────────────────────────────────┘

URL Patterns

# App dashboard
reverse('djadmin:app_index', kwargs={'app_label': 'webshop'})  # /djadmin/webshop/

# With namespace
reverse('djadmin:app_index', kwargs={'app_label': 'webshop'}, current_app='djadmin')

Template

The app dashboard uses: - djadmin/app_dashboard.html (from theme plugin) - Context includes app_label, model_admins, and app_config

How ModelAdmins Are Displayed

Display Name

Each ModelAdmin is displayed with a name derived from the model:

# Model's verbose_name_plural is used
class Product(models.Model):
    class Meta:
        verbose_name_plural = "Products"  # Shows as "Products" in dashboard

Multiple ModelAdmins for One Model

When you register multiple ModelAdmin classes for the same model, each appears as a separate entry:

@register(Product)
class SimpleProductAdmin(ModelAdmin):
    list_display = ['name', 'price']

@register(Product)
class DetailedProductAdmin(ModelAdmin):
    list_display = ['name', 'sku', 'price', 'stock_quantity', 'status']

# Dashboard shows:
# Product (SimpleProductAdmin)    [View List] [Add]
# Product (DetailedProductAdmin)  [View List] [Add]

Each gets its own URL: - /djadmin/webshop/product/0/ → SimpleProductAdmin ListView - /djadmin/webshop/product/1/ → DetailedProductAdmin ListView

Actions in Dashboards

General Actions

General actions are the primary actions displayed in dashboards. They represent main entry points to your ModelAdmin.

from djadmin.actions.list_view import ListAction
from djadmin.actions.dashboard import DashboardAction

@register(Product)
class ProductAdmin(ModelAdmin):
    # Default: only ListAction
    general_actions = [ListAction]

The default ListAction creates a "View List" link to the ListView.

List Actions in Dashboards

List actions may also appear in some dashboard layouts, typically as secondary buttons:

from djadmin.plugins.core.actions import AddAction

@register(Product)
class ProductAdmin(ModelAdmin):
    # AddAction appears next to "View List" in dashboard
    list_actions = [AddAction]

What Doesn't Appear

  • Record actions - These appear in ListViews, not dashboards
  • Bulk actions - These appear in ListViews when records are selected

Example Configuration

from djadmin import ModelAdmin, register
from djadmin.actions.list_view import ListAction
from djadmin.plugins.core.actions import AddAction

@register(Product)
class ProductAdmin(ModelAdmin):
    # These appear in dashboard
    general_actions = [ListAction]
    list_actions = [AddAction]

    # These don't appear in dashboard
    record_actions = [EditAction, DeleteAction]
    bulk_actions = [DeleteBulkAction]

Dashboard shows:

Products  [View List] [Add]
          ^          ^
          |          |
          general    list action
          action

Customizing Dashboard Display

Hiding from Dashboard

Remove general actions to hide a ModelAdmin from dashboards:

@register(Product)
class HiddenProductAdmin(ModelAdmin):
    general_actions = []  # Won't appear in dashboard
    list_actions = [AddAction]

Note: The ModelAdmin still exists and is accessible via direct URL, but won't be listed in dashboards.

Custom Dashboard Actions

Create custom general actions for specialized entry points:

from djadmin.actions import BaseAction, GeneralActionMixin

class AnalyticsDashboardAction(GeneralActionMixin, BaseAction):
    label = 'Analytics Dashboard'
    icon = 'chart-bar'
    css_class = 'secondary'

    def get_url_pattern(self):
        return f'{self.model._meta.app_label}/{self.model._meta.model_name}/analytics/'

    def execute(self, request, **kwargs):
        # Show analytics dashboard
        context = {
            'total_products': self.model.objects.count(),
            'active_products': self.model.objects.filter(status='active').count(),
            # ... more analytics
        }
        return render(request, 'myapp/analytics_dashboard.html', context)

@register(Product)
class ProductAdmin(ModelAdmin):
    general_actions = [ListAction, AnalyticsDashboardAction]

Dashboard shows:

Products  [View List] [Analytics Dashboard]

Read-Only Dashboard Entry

Create a dashboard entry without modification actions:

@register(Product)
class ReadOnlyProductAdmin(ModelAdmin):
    general_actions = [ListAction]  # Only view list
    list_actions = []        # No add
    record_actions = []      # No edit/delete
    bulk_actions = []        # No bulk operations

Dashboard shows:

Products  [View List]

Dashboard Context Data

Project Dashboard Context

{
    'app_list': [
        {
            'name': 'Webshop',
            'app_label': 'webshop',
            'verbose_name': 'Webshop',
            'url': '/djadmin/webshop/',
            'model_admins': [
                {
                    'name': 'Product',
                    'display_name': 'Products',
                    'object_name': 'Product',
                    'general_actions': [...],  # Action instances with URLs
                    'list_actions': [...],     # Action instances with URLs
                },
                # ... more ModelAdmins
            ],
        },
        # ... more apps
    ],
    'site_title': 'Django Admin Deux',
    'site_header': 'Administration',
}

App Dashboard Context

{
    'app_label': 'webshop',
    'app_config': <AppConfig for webshop>,
    'model_admins': [
        {
            'name': 'Product',
            'display_name': 'Products',
            'object_name': 'Product',
            'general_actions': [...],
            'list_actions': [...],
        },
        # ... more ModelAdmins
    ],
    'site_title': 'Django Admin Deux',
    'site_header': 'Administration',
}

Customizing Dashboard Templates

Override Project Dashboard

Create djadmin/project_dashboard.html in your template directory:

{% extends "djadmin/admin_base.html" %}

{% block title %}Dashboard{% endblock %}

{% block content %}
    <h1>My Custom Dashboard</h1>

    {% for app in app_list %}
        <section class="app-section">
            <h2>{{ app.verbose_name }}</h2>

            {% for model_admin in app.model_admins %}
                <div class="model-card">
                    <h3>{{ model_admin.display_name }}</h3>

                    <div class="actions">
                        {% for action in model_admin.general_actions %}
                            <a href="{{ action.url }}" class="{{ action.css_class }}">
                                {{ action.label }}
                            </a>
                        {% endfor %}

                        {% for action in model_admin.list_actions %}
                            <a href="{{ action.url }}" class="{{ action.css_class }}">
                                {{ action.label }}
                            </a>
                        {% endfor %}
                    </div>
                </div>
            {% endfor %}
        </section>
    {% endfor %}
{% endblock %}

Override App Dashboard

Create djadmin/app_dashboard.html:

{% extends "djadmin/admin_base.html" %}

{% block title %}{{ app_config.verbose_name }}{% endblock %}

{% block content %}
    <h1>{{ app_config.verbose_name }}</h1>

    {% for model_admin in model_admins %}
        <div class="model-card">
            <h2>{{ model_admin.display_name }}</h2>

            <div class="actions">
                {% for action in model_admin.general_actions %}
                    <a href="{{ action.url }}">{{ action.label }}</a>
                {% endfor %}
            </div>
        </div>
    {% endfor %}
{% endblock %}

Model-Specific Dashboard Cards

Create djadmin/<app>/<model>_dashboard_card.html for custom display:

{# djadmin/webshop/product_dashboard_card.html #}
<div class="product-card">
    <h3>Products</h3>
    <p>{{ product_count }} products in stock</p>

    <div class="quick-stats">
        <span>Active: {{ active_count }}</span>
        <span>Low Stock: {{ low_stock_count }}</span>
    </div>

    <div class="actions">
        {% for action in general_actions %}
            <a href="{{ action.url }}">{{ action.label }}</a>
        {% endfor %}
    </div>
</div>

Typical navigation pattern:

Home > Webshop > Products > Product List
│      │         │          └─ ListView
│      │         └─ Model Dashboard (if implemented)
│      └─ App Dashboard
└─ Project Dashboard
{# In templates #}
<nav class="breadcrumbs">
    <a href="{% url 'djadmin:index' %}">Dashboard</a>
    <a href="{% url 'djadmin:app_index' app_label='webshop' %}">Webshop</a>
    <span>Products</span>
</nav>

Best Practices

1. Use Meaningful General Actions

Provide clear entry points:

general_actions = [
    ListAction,           # Standard list view
    AnalyticsAction,          # Custom analytics
]

2. Keep Dashboard Clean

Don't overwhelm users with too many actions:

# Good - focused entry points
general_actions = [ListAction]
list_actions = [AddAction]

# Too much - cluttered dashboard
general_actions = [
    ListAction, AnalyticsAction, ReportsAction,
    ExportAction, ImportAction  # Too many!
]

Use app structure to group related ModelAdmins:

Webshop App
├── Products
├── Categories
├── Tags
└── Reviews

Orders App
├── Orders
├── OrderItems
└── Invoices

4. Multiple ModelAdmins for Different Users

Create role-specific views:

@register(Product)
class ManagerProductAdmin(ModelAdmin):
    """Full access for managers"""
    list_display = ['name', 'sku', 'price', 'cost', 'margin']
    general_actions = [ListAction]

@register(Product)
class StaffProductAdmin(ModelAdmin):
    """Limited view for staff"""
    list_display = ['name', 'sku', 'price', 'stock_quantity']
    general_actions = [ListAction]
    list_actions = []  # Can't add
    record_actions = []  # Can't edit/delete

5. Descriptive Model Meta

Use verbose names for better dashboard labels:

class Product(models.Model):
    class Meta:
        verbose_name = "Product"
        verbose_name_plural = "Products"  # Dashboard uses this

Common Patterns

Analytics Dashboard

class AnalyticsDashboardAction(GeneralActionMixin, BaseAction):
    label = 'Analytics'
    icon = 'chart'

@register(Product)
class ProductAdmin(ModelAdmin):
    general_actions = [ListAction, AnalyticsDashboardAction]

Quick Add from Dashboard

# list_actions appear in dashboard
list_actions = [AddAction]  # "Add" button in dashboard

Minimal Dashboard Entry

@register(Product)
class MinimalProductAdmin(ModelAdmin):
    general_actions = [ListAction]
    list_actions = []
    record_actions = []

Troubleshooting

ModelAdmin Not Appearing in Dashboard

Problem: Registered ModelAdmin doesn't show in dashboard

Solution: Check that general_actions is not empty:

@register(Product)
class ProductAdmin(ModelAdmin):
    general_actions = [ListAction]  # Required for dashboard display

Too Many Dashboard Entries

Problem: Same model appears multiple times

Cause: Multiple @register() decorators create separate entries

Solution: This is by design. Each registration creates a separate ModelAdmin entry.

Actions Not Showing

Problem: Actions don't appear in dashboard

Solution: Verify action URLs are being generated:

# Debug in view
for action in model_admin.general_actions:
    print(action.label, action.url)

See Also