Bar chart
Use a bar chart to compare a few categories: orders per status, members per role, messages per weekday. For many categories or long names, use a horizontal bar chart.
from django.db.models import Count, F
from django_insights.metrics import metrics
from django_insights.metrics_types import BarChartResult
from shop.models import Order
@metrics.barchart(
question="How are the orders doing?",
desc="Orders by status",
xlabel="Status",
ylabel="Orders",
)
def orders_by_status() -> BarChartResult:
return (
Order.objects.values("status")
.annotate(total=Count("pk"))
.annotate(orders=F("total"))
.order_by("-total")
.values_list("total", "orders", "status")
)
Rows #
Each row is (height, unused, label).
- The first value is the height of the bar, the last one the label.
- The middle value is stored but not drawn. Pass any number,
0is fine. When you build rows from a query it is easiest to annotate a copy of the total, as above, becausevalues_list("total", "total", ...)with the same name twice collapses to one column on some Django versions. - A
Nonelabel is shown as an empty label.
Keep empty categories #
A category that has no rows is missing from a query. To always show all of them, build the rows yourself:
from django.db.models.functions import ExtractWeekDay
WEEKDAYS = [
(2, "Monday"),
(3, "Tuesday"),
(4, "Wednesday"),
(5, "Thursday"),
(6, "Friday"),
(7, "Saturday"),
(1, "Sunday"),
]
@metrics.barchart(
question="On which weekday do people order?",
xlabel="Weekday",
ylabel="Orders",
)
def orders_by_weekday() -> BarChartResult:
totals = dict(
Order.objects.annotate(day=ExtractWeekDay("created_at"))
.values("day")
.annotate(total=Count("pk"))
.values_list("day", "total")
)
return [(totals.get(day, 0), 0, name) for day, name in WEEKDAYS]