No Time Dad

A blog about web development written by a busy dad

Using a Django Queryset in JavaScript

I’m trying to make my Django applications more interactive. Usually when I need more interactivity in my apps, I reach for React. But I’ve been on a quest lately to use Django with a little bit of JavaScript to create interactivity. React and other frontend web frameworks are great, but sometimes I just enjoy using features Django provides out of the box and not creating more complexity. Also I like seeing how far I can push Django without using frontend frameworks.

Recently, I was working on an app where I needed the client to display the status of some long running tasks in a queue. I wanted to have the statuses update without the user needing to refresh the page. The quick solution for this is to use JavaScript to poll the queue and update the value on the page. But this means that I needed to access the Django queryset in JavaScript.

My first attempt at this involved passing the queryset template variable directly to the JavaScript I’d written in a script tag in the template. This didn’t go well. A lot of json parse errors and other issues. In what turned out to be not much of a surprise to me, Django has a preferred way of accessing data from the queryset in script tags. That is to use the json_script template filter

How I send the data to the template is mostly up to me, but I found it easiest to create a values_list from the queryset and wrap that in a list. This gives me a list with just the values that I can iterate or do whatever with in the JavaScript.

In my example for getting the task ids, I used Django’s context in the view to pass the custom formatted data to the template. The code below is from a Django class based view where I’m overriding the get_context_data method to add custom data to the context.

There a lot of different ways to do this, including not using the context at all and just accessing the queryset directly in the view, but this way made the most sense given the other things I was doing in the view.

# In the view for the template
...
task_ids = self.object_list.values_list("task_id", flat=True)
context["task_ids"] = list(task_ids)
...

Once I have the task_ids in the context, I can access it in the template and use the json_script filter that’ll allow the queryset data to be available to my JavaScript. The magic here is that the Django json_script filter creates a new script element containing the data from the queryset.

<!-- Django template for the view -->
<!-- Other html in template...  -->

{{ task_ids|json_script:"task_ids" }}

It’d look something like the below when redered on the page.

<!-- Resulting html for the json_script created by Django -->
<script id="task_ids" type="application/json">
  [
    "ea57eb2c-4b11-4206-b530-eee3317cf50a",
    "14f7b62c-5666-4991-a73c-a55ae5a1c7a5",
    "fee82acb-6aea-4315-92cc-5cb669a1aeed",
    "4016ff4d-e7ac-425c-abcd-e55dda178442"
  ]
</script>

As shown above, Django created a script element with the id “task_ids” as I defined using the json_script filter. All I have to do now is write the JavaScript to get the element above by the id and read its contents. Then I can treat the data as JSON and do whatever I need to do with it in my JavaScript code.

<!-- Django template for the view -->
<!-- Other html in template...  -->

{{ task_ids|json_script:"task_ids" }}
<script>
  const taskIds = JSON.parse(document.getElementById("task_ids").textContent)
  if (taskIds) {
    // do something with taskIds...
  }
</script>