djangospice_widget
Reusable, composable, request-aware UI widgets for Django.
djangospice_widget provides a declarative widget system for building server-rendered Django interfaces from small, reusable UI units. Widgets can render templates or direct content, accept request data, expose actions, compose other widgets, define named slots, integrate with HTMX, and support lazy loading, refreshing, permissions, and caching.
Features
- Class-based and function-based widgets
- Automatic widget registration
- Template-based or direct HTML content
- Request-aware widgets
- Query parameters and request data
- Widget composition
- Child widgets
- Named slots
- Widget actions
- Permission-aware rendering
- Lazy loading
- HTMX integration
- Automatic refresh
- Widget endpoints
- Model and queryset support
- Object and multiple-object selection
- Optional widget caching
Installation
pip install djangospice-widget
Add the package to INSTALLED_APPS:
INSTALLED_APPS = [
# ...
"djangospice_widget",
]
Defining Widgets
Class-Based Widgets
A widget is a reusable UI unit.
from djangospice_widget import Widget
class StudentStatisticsWidget(Widget):
name = "student_statistics"
title = "Student Statistics"
template_name = "students/widgets/statistics.html"
def get_context(self):
context = super().get_context()
context["students"] = self.get_queryset()
return context
Widgets have a stable key based on their application and name:
students.student_statistics
Function-Based Widgets
For simple widgets, a function can be used instead of defining a class.
from djangospice_widget import widget
@widget(
name="welcome",
title="Welcome",
template="dashboard/widgets/welcome.html",
)
def welcome_widget(widget):
return {
"message": "Welcome to ScholarMIS",
}
Function-based widgets are useful when a dedicated widget class is unnecessary.
Widget Configuration
Widgets can define metadata and behavior through class attributes.
class StudentStatisticsWidget(Widget):
name = "student_statistics"
title = "Student Statistics"
description = "Summary of current student records."
group = "students"
template_name = "students/widgets/statistics.html"
permission = "students.view_student"
enabled = True
priority = 100
Common configuration options include:
| Option | Purpose |
|---|---|
name |
Widget name |
title |
Human-readable title |
description |
Widget description |
group |
Logical grouping |
template_name |
Widget template |
permission |
Required Django permission |
enabled |
Enables or disables the widget |
priority |
Ordering priority |
lazy |
Enables lazy loading |
refreshable |
Enables automatic refresh |
refresh_interval |
Refresh interval |
cache_timeout |
Enables widget caching |
model |
Associated Django model |
Widget Identity
Every widget has a stable widget key:
For example:
students.student_statistics
Widget keys provide a consistent way to reference registered widgets.
Rendering Widgets
Load the widget template tags:
{% load djangospice_widget %}
A registered widget can be rendered by its key:
{% render_widget "students.student_statistics" %}
Parameters can be passed to the widget:
{% render_widget "students.student_statistics" campus=campus %}
The current Django request is automatically available to the widget.
Rendering from Python
Widgets can also be rendered directly from Python:
from djangospice_widget import WidgetRenderer
widget = StudentStatisticsWidget(request=request)
html = WidgetRenderer(widget).render()
Widget Content and Templates
Widgets can provide direct content:
class MessageWidget(Widget):
def get_content(self):
return "<strong>Hello</strong>"
Or render a template using context:
class MessageWidget(Widget):
template_name = "messages/message.html"
def get_context(self):
context = super().get_context()
context["message"] = "Hello"
return context
Templates are recommended for reusable UI.
Widget Composition
Widgets can contain other widgets.
For example:
Dashboard
├── Student Statistics
├── Attendance Summary
└── Recent Registrations
A parent widget can define its children:
class DashboardWidget(Widget):
template_name = "dashboard/dashboard.html"
def configure(self):
self.add_children(
StudentStatisticsWidget(request=self.request),
AttendanceSummaryWidget(request=self.request),
RecentRegistrationsWidget(request=self.request),
)
The children form part of the parent's widget composition.
Rendering Children
A widget's children can be rendered from its template:
{% render_children widget %}
For example:
<div class="dashboard">
{% render_children widget %}
</div>
render_children operates on the current widget instance.
Named Slots
Widgets can provide named areas for extending their content.
For example, a dashboard can provide a toolbar:
class DashboardWidget(Widget):
def configure(self):
self.add_to_slot(
"toolbar",
RefreshButtonWidget(request=self.request),
)
self.add_children(
StudentStatisticsWidget(request=self.request),
AttendanceSummaryWidget(request=self.request),
)
The template can render the slot:
<div class="dashboard">
<div class="dashboard-toolbar">
{% render_slot widget "toolbar" %}
</div>
<div class="dashboard-content">
{% render_children widget %}
</div>
</div>
Slots are useful for creating reusable container widgets with extension points.
Children and Slots
Children and slots serve different purposes.
Children represent the normal contents of a widget:
{% render_children widget %}
Slots provide named extension points:
{% render_slot widget "toolbar" %}
A widget can therefore define a reusable structure while allowing other widgets to populate specific areas.
Template Tags
The package provides three primary rendering tags.
render_widget
Renders a registered widget using its widget key:
{% render_widget "students.statistics" %}
render_children
Renders the children of an existing widget:
{% render_children widget %}
render_slot
Renders widgets assigned to a named slot:
{% render_slot widget "toolbar" %}
The inputs are intentionally different:
| Tag | Input | Purpose |
|---|---|---|
render_widget |
Widget key | Render a registered widget |
render_children |
Widget instance | Render the widget's children |
render_slot |
Widget instance + slot | Render widgets in a named slot |
Widget keys are used to reference registered widgets, while widget instances are used to compose existing widget trees.
Request Awareness
Widgets receive the current Django request.
class CurrentUserWidget(Widget):
def get_context(self):
context = super().get_context()
context["user"] = self.user
return context
Widgets can access request-related information through:
widget.request
widget.user
widget.request_data
GET requests expose query parameters, while modifying requests expose submitted request data.
Query State and URLs
Widgets can work with the current query state:
widget.query_state
Widget URLs can preserve or modify query parameters:
widget.url(page=2)
This is useful for:
- filtering;
- pagination;
- sorting;
- tabs;
- search;
- other stateful interfaces.
Widget Endpoints
Registered widgets have a canonical endpoint:
widget.endpoint
Widget endpoints can be used for HTMX requests and other widget interactions.
HTMX
Widgets can progressively enhance server-rendered interfaces with HTMX.
Lazy Loading
Enable lazy loading with:
class StatisticsWidget(Widget):
lazy = True
The widget can initially display a placeholder and load its content from its endpoint.
Automatic Refresh
Widgets can periodically refresh their content:
class StatisticsWidget(Widget):
refreshable = True
refresh_interval = 30
This is useful for dashboards, counters, status information, and other dynamic UI.
Permissions and Visibility
Widgets can declare a Django permission:
class StudentStatisticsWidget(Widget):
permission = "students.view_student"
Widgets can also implement application-specific visibility rules:
class StudentStatisticsWidget(Widget):
def is_visible(self):
return self.user.is_staff
This allows widgets to follow both Django permissions and application-specific rules.
Widget Actions
Widgets can expose reusable actions:
class ViewStudentAction:
...
class EditStudentAction:
...
class DeleteStudentAction:
...
Actions can be declared together:
class StudentTableWidget(Widget):
row_actions = Actions(
ViewStudentAction,
EditStudentAction,
DeleteStudentAction,
)
Multiple action collections can be declared and composed, including nested collections.
Actions can be invoked through the widget's interaction endpoint.
HTTP Methods
Widgets can respond to HTTP methods:
class StudentWidget(Widget):
def get(self):
...
def post(self):
...
def delete(self):
...
GET is the default rendering operation.
Other methods can be implemented for interactive widgets and operations.
Models and Querysets
Widgets can optionally be associated with a Django model:
class StudentListWidget(Widget):
model = Student
The widget can access its queryset through:
widget.get_queryset()
The queryset can be customized:
class StudentListWidget(Widget):
model = Student
def get_queryset(self):
return (
super()
.get_queryset()
.filter(active=True)
)
Widgets can also resolve selected objects:
widget.get_object()
widget.get_objects()
The default selection parameters are:
selected_id
selected_ids
Caching
Widgets can optionally cache rendered output:
class StatisticsWidget(Widget):
cache_timeout = 300
Caching can be useful for widgets that perform expensive operations, such as:
- dashboards;
- statistics;
- reports;
- summaries;
- expensive database queries.
Complete Example
Widget
from djangospice_widget import Widget
class DashboardWidget(Widget):
name = "dashboard"
title = "Dashboard"
template_name = "dashboard/dashboard.html"
def configure(self):
self.add_children(
StudentStatisticsWidget(
request=self.request,
),
AttendanceSummaryWidget(
request=self.request,
),
)
self.add_to_slot(
"toolbar",
RefreshButtonWidget(
request=self.request,
),
)
Template
{% load djangospice_widget %}
<div class="dashboard">
<header class="dashboard-header">
<h1>{{ widget.title }}</h1>
<div class="dashboard-toolbar">
{% render_slot widget "toolbar" %}
</div>
</header>
<main class="dashboard-content">
{% render_children widget %}
</main>
</div>
Usage
{% render_widget "dashboard.dashboard" %}
Requirements
- Python 3.11+
- Django 4.2+
Status
djangospice-widget is under active development.
APIs may evolve before the first stable release.
License
This package is licensed under the MIT License.
See LICENSE for the full license text.
Release files for djangospice-widget 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| djangospice_widget-0.2.0.tar.gz | 19.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| djangospice_widget-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 48.1 kB
Release files / djangospice_widget-0.2.0.tar.gz
| Download URL | djangospice_widget-0.2.0.tar.gz |
|---|---|
| Size | 19.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2408b0a2a23aaed6242677eba576394a771fa7c9041d87861d07ca35b2a88b8e
|
|
BLAKE2b-256 checksum How to use checksums |
e9675882bde8ffae2a26d1b7fde03dc50448242d2e98c7482d3857dea74f889a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.
Transparency logRelease files / djangospice_widget-0.2.0-py3-none-any.whl
| Download URL | djangospice_widget-0.2.0-py3-none-any.whl |
|---|---|
| Size | 28.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ba53826a61e97ed8c991c268c6ff4a1de8ff378aebdc30d5e63a3ce5e25d6083
|
|
BLAKE2b-256 checksum How to use checksums |
6a9ec58207854edc94b39b81432b9b663453fa22806e845abe6e23273602c68f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.
Transparency log