Simon Willison’s Weblog

VIAL is now live, plus django-sql-dashboard

Originally posted to my internal blog at VaccinateCA

Our new Django backend has now officially graduated from preview mode! We’ve been running it to collect caller reports for Oregon for over a week now, and today we finally turned off the old Heroku app and promoted to be the place that our caller app writes to.

We also have as a staging environment.

Calls made in California are still being logged directly to Airtable. The next big milestone for VIAL will be replacing Airtable for our California calls. That’s going to be my focus for this week.

django-sql-dashboard for reporting

This weekend I span up a new package which acts as a Django-based imitation of (and research playground for) my Datasette project.

django-sql-dashboard takes some of the key ideas from Datasette—the ability to use raw SQL queries in a safe, read-only, time limited environment to build bookmarkable interfaces—and turns it into a dashboard for applications written in Django and running against PostgreSQL.

It’s part of my ongoing goal to replace Airtable while avoiding the loss of the flexibility that has made Airtable so valuable to us.

If you are a staff user, you can access it at The default interface lets you start running PostgreSQL SQL queries against a subset of our database tables:


It borrows a number of key features from Datasette. Firstly, queries can be bookmarked and shared. Any time you submit new queries you get back a URL with signed parameters, which you can share with other staff users. The page in the screenshot is at

Secondly, you can use named parameters in your queries—select * from location where state_id = (select id from state where name = %(state)s) for example—and the dashboard will extract those parameters out and turn them into form fields.


Unlike Datasette, it allows multiple SQL queries to be embedded in the same page. This means you can build quite complex dashboards, all sharing the same user-configurable parameter.

The most exciting feature though is custom widgets. If you write a SQL query that returns columns with specific names, custom widgets will kick in to render that data in formats other than a table.

This is best illustrated by the dashboard I built here:—this is an example of a “stored dashboard” which has been written to the database.


Note how the following SQL query is rendered as a “big number”:

select 'Number of locations in ' || %(state_name)s as label,
count(*) as big_number
from location where state_id = (select id from state where name = %(state_name)s)

And the bar chart at the bottom of the page is rendered automatically for this SQL query, because it returns columns called bar_quantity and bar_label:

select count(*) as bar_quantity,
to_char(created_at, 'YYYY-MM-DD') as bar_label
from reports
where reports.location_id in
(select id from location where state_id = (select id from state where name = %(state_name)s))
group by to_char(created_at, 'YYYY-MM-DD')

You can even create SQL statements that return dynamically concatenated markdown or HTML (run through Bleach to avoid any nasty XSS problems).

select '# Chart: number of reports filed per day in ' || %(state_name)s as markdown

This ability to construct dashboards by saving a list of SQL queries feels really powerful to me—it takes some of the best aspects of Datasette (which currently only works against SQLite databases) and makes them available to us within our Django/PostgreSQL app, protected by the Django authentication mechanism.

You can follow ongoing developement of django-sql-dashboard in the issues at

This is VIAL is now live, plus django-sql-dashboard by Simon Willison, posted on 15th March 2021.

Part of series VaccinateCA internal blog

  1. The simplest possible call queue - March 6, 2021, 5 p.m.
  2. New call queue ready to test. Also geography. - March 7, 2021, 5 p.m.
  3. APIs for importing locations - March 9, 2021, 5 p.m.
  4. VIAL is now live, plus django-sql-dashboard - March 15, 2021, 5 p.m.
  5. The Airtable formulas at the heart of everything - March 23, 2021, 5 p.m.
  6. VIAL: Preparing for some collaborative testing - April 1, 2021, 5 p.m.
  7. A CSV export, JSON import workflow for bulk updating our data - April 28, 2021, 5 p.m.

Next: Weeknotes: django-sql-dashboard widgets

Previous: Weeknotes: tableau-to-sqlite, django-sql-dashboard