Skip to content

Sidebar Widgets

Added in: Milestone 2 (Phase 1A)

Sidebar widgets provide a flexible, plugin-based system for displaying auxiliary content alongside action views (like ListView). Any plugin can register widgets to appear in the sidebar.

Overview

The sidebar widget system allows plugins to: - Add custom UI elements to the sidebar - Display conditional content based on view context - Control widget ordering and visibility - Provide dynamic context to widget templates

SidebarWidget Dataclass

The SidebarWidget dataclass defines a sidebar widget:

from dataclasses import dataclass
from typing import Optional, Callable

@dataclass
class SidebarWidget:
    """Configuration for a sidebar widget."""

    template: str  # Path to widget template
    context_callback: Optional[Callable] = None  # Dynamic context provider
    order: int = 100  # Display order (lower = first)
    condition: Optional[Callable] = None  # Display condition
    identifier: Optional[str] = None  # Unique ID

Attributes

template (required) - Path to the widget's Django template - Example: 'djadmin/includes/search_widget.html'

context_callback (optional) - Callable that receives (view) and returns a dict - Use this to add dynamic context to the widget template - Example: lambda view: {'filterset': view.filterset}

order (optional, default: 100) - Controls widget display order in sidebar - Lower values appear first - Standard values: - 0 - Search widget - 10 - Filter widget - 100 - Default for custom widgets

condition (optional) - Callable that receives (view) and returns bool - Widget only displays if condition returns True - Example: lambda view: hasattr(view, 'filterset')

identifier (optional) - Unique string identifier for the widget - Useful for debugging and widget management

Plugin Hook

Plugins register sidebar widgets via the djadmin_get_sidebar_widgets hook:

# myapp/djadmin_hooks.py
from djadmin.plugins import hookimpl
from djadmin.dataclasses import SidebarWidget
from djadmin.actions.list_view import ListAction

@hookimpl
def djadmin_get_sidebar_widgets(action):
    """Register sidebar widgets for ListView."""

    def get_my_widget_context(view):
        return {
            'data': view.get_my_data(),
            'count': view.get_my_count(),
        }

    def should_display(view):
        return view.model_admin.my_feature_enabled

    return {
        ListAction: [
            SidebarWidget(
                template='myapp/my_widget.html',
                context_callback=get_my_widget_context,
                condition=should_display,
                order=50,
                identifier='myapp_widget',
            )
        ]
    }

Template Structure

Widget templates should NOT include the outer wrapper div. The sidebar rendering system adds the wrapper automatically.

❌ BAD - Don't add wrapper:

{# DON'T DO THIS #}
<div class="sidebar-widget">
    <h3>My Widget</h3>
    <p>Widget content...</p>
</div>

✅ GOOD - No wrapper:

{# myapp/my_widget.html #}
<h3>My Widget</h3>
<p>Widget content: {{ data }}</p>
<ul>
    {% for item in items %}
        <li>{{ item }}</li>
    {% endfor %}
</ul>

Built-in Widgets

Search Widget

Order: 0 (top of sidebar) Template: djadmin/includes/search_widget.html Condition: Only displays when search_fields is configured

Provides a search input with magnifying glass icon. Preserves other query parameters when submitting.

Filter Widget (djadmin-filters plugin)

Order: 10 (below search) Template: djadmin/djadmin_filters/filter_widget.html Condition: Only displays when filterset exists

Provides a form-based filter UI with filter fields and clear button.

Example: Custom Help Widget

# myapp/djadmin_hooks.py
from djadmin.plugins import hookimpl
from djadmin.dataclasses import SidebarWidget
from djadmin.actions.list_view import ListAction

@hookimpl
def djadmin_get_sidebar_widgets(action):
    """Add a help widget to ListView."""

    def get_help_context(view):
        return {
            'model_name': view.model._meta.verbose_name,
            'help_text': view.model_admin.help_text,
            'doc_url': view.model_admin.doc_url,
        }

    def has_help_text(view):
        return hasattr(view.model_admin, 'help_text')

    return {
        ListAction: [
            SidebarWidget(
                template='myapp/help_widget.html',
                context_callback=get_help_context,
                condition=has_help_text,
                order=200,  # Display after filters
                identifier='help_widget',
            )
        ]
    }
{# myapp/templates/myapp/help_widget.html #}
<h3>Help</h3>
<p>{{ help_text }}</p>
{% if doc_url %}
    <a href="{{ doc_url }}" target="_blank">View Documentation →</a>
{% endif %}

Accessing Widget Context in Templates

Widgets are available in the template context as sidebar_widgets:

{# djadmin/includes/_sidebar.html #}
<aside class="sidebar">
    {% for widget in sidebar_widgets %}
        <div class="sidebar-widget">
            {% include widget.template %}
        </div>
    {% endfor %}
</aside>

The widget's context (from context_callback) is merged into the template context before rendering.

Styling

Sidebar widgets use the default theme's styles:

/* Sidebar layout */
.content-wrapper {
    display: flex;
    gap: 1.5rem;
}

.primary-content {
    flex: 1;
    min-width: 0;
}

.sidebar {
    width: 280px;
    flex-shrink: 0;
}

/* Widget container (added by sidebar rendering) */
.sidebar-widget {
    background: white;
    border: 1px solid #e5e7eb;
    border-radius: 0.375rem;
    padding: 1rem;
    margin-bottom: 1rem;
}

/* Dark mode */
.dark .sidebar-widget {
    background: #1f2937;
    border-color: #374151;
}

See Also