How to turn Django Admin into a lightweight dashboard

For a better reading experience, check out this article on my website.

Django Admin is a powerful tool for managing data in your app. However, it was not designed with summary tables and charts in mind. Luckily, the developers of Django Admin made it easy for us to customize.

We are going to turn Django Admin into a dashboard by adding a chart and a summary table.

This is what it’s going to look like at the end:

Image for post
Image for post

Why would I want to do that?

There are a lot of tools, apps and packages out there that can produce very nice looking dashboards. I personally found that unless the product is an actual dashboard, most of the time all you need is a simple summary table and a few charts.

Second, and just as important — no dependencies.

If all you need is a little boost to your admin interface this approach is definitely worth considering.


We are going to use a made up Sale model.

To harness the full power of Django Admin we are going to base our dashboard on a the built-in ModelAdmin.

To do that we need a model:

A proxy model extends the functionality of another model without creating an actual table in the database.

Now that we have a model we can create the ModelAdmin:

Because we are using a standard ModelAdmin we can use its features. In this example I added a date_hierarchy to filter sales by creation date. We are going to use this later for the chart.

To keep the page looking like a “regular” admin page we extend Django’s change_list template and place our content in the result_list block:

This is what our page looks like at this point:

Image for post
Image for post

Adding a summary table

The context sent to the template is populated by the ModelAdmin in a function called changelist_view.

To render the table in the template we fetch the data in changelist_view and add it to the context:

Let’s break it down:

  1. Call super to let Django do its thing (populate headers, breadcrumbs, queryset, filters and so on).
  2. Extract the queryset created for us from the context. At this point the query is filtered with any inline filters or date hierarchy selected by the user.
  3. If we can’t fetch the queryset from the context it’s most likely due to invalid query parameters. In cases like this Django will redirect so we don’t interfere and return the response.
  4. Aggregate total sales by category and return a list (the “metrics” dict will become clear in the next section).

Now that we have the data in the context we can render it in the template:

The markup is important — to get the native Django look we need to render tables in the same way Django renders them.

This is what we have so far:

Image for post
Image for post

A summary table is not much without a bottom line. We can use the metrics and do some Django ORM voodoo to quickly calculate the bottom line:

That’s a pretty cool trick…

Lets add the bottom line to the table:

This is starting to take shape:

Image for post
Image for post

Adding filters

We are using a “regular” model admin so filters are already baked in.

Let’s filter by device:

And the result:

Image for post
Image for post

Adding a chart

A dashboard is not complete without a chart.

We are going to add a bar chart to show sales over time.

To build our chart we are going to use plain HTML and some good ol’ CSS with flexbox. The data for the chart is going to be a time series of percents to use as the bar height.

Back to our changelist_view:

Let’s add the bar chart to the template and style it a bit:

For those of you not familiar with flexbox, that piece of CSS means “draw from the bottom up, pull to the left and adjust the width to fit”.

This is how it looks like now:

Image for post
Image for post

That’s looking pretty good, but…

Each bar in the chart represents a day. What will happen when we try to show data for a single day? Or several years?

Image for post
Image for post

A chart like that is both unreadable and dangerous. Fetching so much data will flood the server and generate a huge HTML file.

Django Admin has a date hierarchy — let’s see if we can use that to adjust the period of the bars based on the selected date hierarchy:

  • If the user filtered a single day each bar will be one hour (max 24 bars).
  • If the user selected a month each bar will be one day (max 31 bars).
  • If the user selected a year each bar will be one week (max 52 bars).
  • More than that and each bar will be one month.

Now we need just one small adjustment to the change list view:

The period argument passed to Trunc is now a parameter.

The result:

Image for post
Image for post

That’s a beautiful trend…

Where can we take it from here?

Now that you have all this spare time from not rolling your own dashboard you can:

Written by

Full Stack Developer, Team Leader, Independent. More from me at

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store