Examples
This page provides a set of practical examples to demonstrate how to build various widgets. Let's assume we have a pizza delivery app with the following models:
# your_app/models.py
from django.db import models
class Pizza(models.Model):
name = models.CharField(max_length=100, unique=True)
def __str__(self):
return self.name
class Restaurant(models.Model):
name = models.CharField(max_length=100, unique=True)
menu = models.ManyToManyField(Pizza, related_name='restaurants')
def __str__(self):
return self.name
class Order(models.Model):
created = models.DateTimeField(auto_now_add=True)
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE, related_name='orders')
pizza = models.ForeignKey(Pizza, on_delete=models.CASCADE, related_name='orders')
We will define all our widgets in a dashboards.py
file.
# your_app/dashboards.py
import datetime
from collections import defaultdict
from django.db.models import Count
from django.utils import timezone
from django.utils.timesince import timesince
from controlcenter import Dashboard, widgets, app_settings
from .models import Order, Pizza, Restaurant
1. Scrollable ItemList with Fixed Height
To create a scrollable list, set the height
attribute on your widget. This example shows a list of pizzas ordered today from a specific restaurant.
class MenuWidget(widgets.ItemList):
title = 'Pizzas Ordered Today'
model = Pizza
list_display = ['name', 'order_count']
# Sets the widget's max-height to 300px, making it scrollable
height = 300
# We want all items, not just the default 10
limit_to = None
def get_queryset(self):
# We could filter by a specific restaurant, e.g., 'Ciao'
# restaurant = Restaurant.objects.get(name='Ciao')
today = timezone.now().date()
return Pizza.objects.filter(orders__created__gte=today)
.annotate(order_count=Count('orders'))
.order_by('-order_count')
# Use the annotation 'order_count' from the queryset
def order_count(self, obj):
return obj.order_count
order_count.short_description = 'Orders Today'
2. Sortable and Numbered ItemList
To make a list sortable, set sortable = True
. To add a row number column, include app_settings.SHARP
in list_display
.
class LatestOrdersWidget(widgets.ItemList):
title = 'Latest 20 Orders'
model = Order
queryset = Order.objects.select_related('pizza').order_by('-created')
limit_to = 20
sortable = True
# Add '#' for row numbers, 'pizza' for the relationship, and a custom method 'ago'
list_display = (app_settings.SHARP, 'pk', 'pizza', 'ago')
# A custom method to display how long ago the order was created
def ago(self, obj):
return timesince(obj.created)
ago.short_description = 'When'
3. Bar Chart of Most Popular Restaurants
This SingleBarChart
shows the total number of orders per restaurant. We use the legend()
method to display the order count, as Chartist.js doesn't show values on bars by default.
class RestaurantPopularityChart(widgets.SingleBarChart):
title = 'Most Popular Restaurants'
model = Restaurant
class Chartist:
options = {
'axisY': {'onlyInteger': True}
}
def legend(self):
# Display series values in the legend
return [y for x, y in self.values]
def values(self):
# Returns pairs of (restaurant name, order count)
queryset = self.get_queryset()
return queryset.annotate(order_count=Count('orders')) \
.order_by('-order_count') \
.values_list('name', 'order_count')
4. Line Chart with Multiple Series
This example shows daily order counts for multiple restaurants over the last week. It demonstrates handling multiple data series and dealing with missing data points.
RESTAURANTS = ['Mama', 'Ciao', 'Sicilia']
class WeeklyOrdersChart(widgets.LineChart):
title = 'Orders This Week'
width = widgets.LARGER # Make it wider
def legend(self):
return RESTAURANTS
def labels(self):
# X-axis: The last 7 days
today = timezone.now().date()
return [(today - datetime.timedelta(days=i)).strftime('%d.%m')
for i in range(7)]
def series(self):
# Fetch the prepared data
data = self.values
# Prepare a list of series for Chartist.js
# Each inner list corresponds to a restaurant in the legend
return [
[data.get(restaurant, {}).get(label, 0) for label in self.labels()]
for restaurant in self.legend()
]
def values(self):
# Fetch order counts grouped by restaurant and day
today = timezone.now().date()
week_ago = today - datetime.timedelta(days=7)
queryset = Order.objects.filter(created__gte=week_ago) \
.extra({'day': "date(created)"}) \
.values('restaurant__name', 'day') \
.annotate(count=Count('id'))
# Restructure the data into a nested dictionary:
# {'Ciao': {'23.01': 5, '24.01': 8}, 'Mama': ...}
data = defaultdict(dict)
for row in queryset:
day_str = datetime.datetime.strptime(row['day'], '%Y-%m-%d').strftime('%d.%m')
data[row['restaurant__name']][day_str] = row['count']
return data