csc_cog Clayton Cook P.E. | Engineer, Programmer, and Musician
csc_cog
Clayton Cook P.E. | Engineer, Programmer, and Musician
image_1400x440
Generated with AI February 28, 2025 at 11:56 AM

Graphing with Django and Plotly

Febuary 28, 2025
Tags:
Django Plotly Graphing Python Javascript

Graphing with Django and Plotly is a powerful combination for creating interactive and visually appealing graphs on the web. In this blog post, we will explore how to set up a Django project for graphing. I will cover using either Plotly.js in the browser or generating graphs with Plotly.py on the backend. By the end of this post, you will have a solid understanding of how to use Django and Plotly together to create graphs for your web applications.

Full Code on Github

Setting Up the Django Project

  • Installing necessary dependencies
    • pip install django plotly
  • Creating a new Django project.
    • django-admin startproject core
    • Rename core folder to DjangoApp
    • Create App, py manage.py startapp base
    • Add app to settings.py

Creating Models And Importing Data

  • Defining Django models to store data for graphing in 'base\models.py'
    • We will be creating a model that represents population data for counties located in Kentucky, United States. See model class definition below.

      # base/models.py
      from django.db import models
      
      # Create your models here.
      class Population(models.Model):
      state = models.CharField(max_length=50)
      county = models.CharField(max_length=50)
      pop2020 = models.IntegerField('Population in 2020')
      pop2024 = models.IntegerField('Population in 2024')
      areaMi = models.FloatField("Area in square miles")
      density = models.FloatField("Calculated density of population per square mile")
      growthSince2020 = models.FloatField("Growth since 2020")
      
      def __str__(self):
      return self.county

  • Defining Django Admin models to display data for admin in 'base\admin.py'
    • We will be creating a model admin class that will display the population data in the Django admin interface. See model admin class definition below.

      # base/admin.py
      from django.contrib import admin
      from .models import Population
      
      # Register your models here.
      @admin.register(Population)
      class PopulationAdmin(admin.ModelAdmin):
      list_display = ('county',)
      ordering = ('county',)
      search_fields = ('county',)

  • Running migrations to create database tables. python manage.py makemigrations & python manage.py migrate
  • Create an Admin python manage.py createsuperuser
  • Creating fixtures with data in 'base\fixtures\mydata.json'
    • I created this fixtures in json format after importing data into my database from a csv file. Fixtures are useful for importing data into the database for testing or seeding data. The file has data from all 120 counties in Kentucky. See snippet of the fixture below.

      # base/fixtures/mydata.json
      [
          {
              "model": "base.population",
              "pk": 1,
              "fields": {
              "state": "Kentucky",
              "county": "Jefferson County",
              "pop2020": 782845,
              "pop2024": 771193,
              "areaMi": 381.0,
              "density": 2024.1286089238845,
              "growthSince2020": -0.0148841724734781
          }
          },
          {
              "model": "base.population",
              "pk": 2,
              "fields": {
      ...

  • Importing fixtures with data. py manage.py loaddata mydata
  • Verify that the data was import correctly with the admin interface.

Building the Backend with Django For Chart Generation

  • Add base.urls.py to urls.py
    • # base/urls.py
      from django.urls import path
      
      from . import views
      
      urlpatterns = [
      path("", views.index, name="index")
      ]

      # core/urls.py
      from django.contrib import admin
      from django.urls import include, path
      
      urlpatterns = [
      path('admin/', admin.site.urls),
      path('', include('base.urls')),
      ]

  • Creating views to handle data processing and graph generation.
    • Creating view in 'base\views.py'
    • # base/views.py
      from django.shortcuts import render
      from base.models import Population
      from base.graphs import histogram_plot_pop, histogram_plot_pop_change
      
      # Create your views here.
      def index(request):
      
      # Get the population data
      county_pop = Population.objects.all() #<- for backend processing
      county_pop_json = list(Population.objects.values()) #<- for frontend consumption
      
      # Backend Histogram Plots
      pop_hist_2024 = histogram_plot_pop(county_pop)
      pop_hist_change_2024 = histogram_plot_pop_change(county_pop)
      
      return render(request, "base/index.html", {
      "county_pop": county_pop,
      "county_pop_json": county_pop_json,
      "pop_hist_2024": pop_hist_2024,
      "pop_hist_change_2024": pop_hist_change_2024
      })

    • You can see from base/views.py above that we have a variable county_pop_json that will be used for front end javascript consumption and two functions that generate histograms for population data that we have not created yet. These functions will be defined in base/graphs.py.

Plotly with the Backend

  • Creating the two functions for backend graph creation.
    • # base/graphs.py
      from plotly.offline import plot
      from plotly.graph_objs import Histogram, Layout
      from plotly.graph_objs.layout import XAxis, YAxis
      from base.models import Population
      
      def histogram_plot_pop(population: Population) -> str:
          if population.count() == 0:
              return "No data to plot"
          
          x_data = list(population.values_list("pop2024", flat=True))
      
          # Create a histogram
          trace = Histogram(
              x=x_data,
              opacity=0.7,
              xbins=dict(size=10000)
          )
      
          plotly_html = plot({
              "data": [trace],
              "layout": Layout(
                  title="Kentucky Counties 2024 Population Histogram",
                  bargap=0.1,
                  yaxis=YAxis(
                      title="Count",
      
                      gridcolor="#b0b0b0",
                  ),
                  xaxis=XAxis(
                      title="Population"
                  ),
                  paper_bgcolor='rgba(0,0,0,0)',
                  plot_bgcolor='rgba(0,0,0,0)'
              )
          }, 
          output_type="div",
          include_plotlyjs=False)
      
          return plotly_html
      
      def histogram_plot_pop_change(population: Population) -> str:
          if population.count() == 0:
              return "No data to plot"
          
          x_data = list(population.values_list("growthSince2020", flat=True))
          x_data_percent = [x * 100 for x in x_data ]
      
          # Create a histogram
          trace = Histogram(
              x=x_data_percent,
              opacity=0.7
          )
      
          plotly_html = plot({
              "data": [trace],
              "layout": Layout(
                  title="Kentucky Counties 2024 Population Change Histogram",
                  bargap=0.1,
                  yaxis=YAxis(
                      title="Count",
      
                      gridcolor="#b0b0b0",
                  ),
                  xaxis=XAxis(
                      title="Population Change (%)"
                  ),
                  paper_bgcolor='rgba(0,0,0,0)',
                  plot_bgcolor='rgba(0,0,0,0)'
              )
          }, 
          output_type="div",
          include_plotlyjs=False)
      
          return plotly_html

  • Passing graph html code to front end through the pop_hist_2024 and pop_hist_change_2024 variables. Then displaying that in the index.html file.

    # base/templates/index.html
    ... 
    
    {% autoescape off %} {{ pop_hist_2024 }} {% endautoescape %} {% autoescape off %} {{ pop_hist_change_2024 }} {% endautoescape %}
    ...

Plotly with the Frontend

  • In order to use Plotly.js on the front end you will need <script src="https://cdn.plot.ly/plotly-3.0.0.min.js" charset="utf-8"></script> in the head of your html file.
  • Next up is creating the templates for that graph creation. I like to separate out these into a sub template, 'base\templates\base\partials\index_county_pop.html'.
  • We can then convert our county_pop_json variable from the view.py file into json data that can be consumed by our javascript code.

    
    <div id="plot_001"></div>
    <div id="plot_002"></div>
    
    {{ county_pop_json|json_script:"county_pop_json" }} <- this line converts the data to json
    
    <script type="text/javascript" defer>
    // This reads the JSON data
    const data = JSON.parse(document.getElementById('county_pop_json').textContent);
    	
    // Histogram Plot County Population 2024
    canvas_001 = document.getElementById('plot_001');
    const trace_001 = {
        x: [...data.map((county) => county.pop2020)],
        type: 'histogram',
        opacity: 0.7,
        xbins: {size: 10000}
    };
    const layout_001 = {
        bargap: 0.1,
        title: {
            text: "Kentucky Counties 2024 Population Histogram",
        },
        xaxis: { title: {'text': 'Population'}},
        yaxis: { title: {'text':'Count'}},
    };
    Plotly.newPlot(canvas_001, [trace_001], layout_001);
    
    // Histogram Plot County Population Change 2024
    canvas_002 = document.getElementById('plot_002');
    const trace_002 = {
        x: [...data.map((county) => (county.growthSince2020) * 100)],
        type: 'histogram',
        opacity: 0.7
    };
    const layout_002 = {
        bargap: 0.05,
        title: {
            text: "Kentucky Counties 2024 Population Change Histogram",
        },
        xaxis: { title: {'text': 'Population Change (%)'}},
        yaxis: { title: {'text':'Count'}},
    };
    Plotly.newPlot(canvas_002, [trace_002], layout_002);
    </script>

  • Now you can add {% include "base/partials/index_county_pop.html" with county_pop_json=county_pop_json %} into your index.html to include the plotly.js graphs.

Looks of the Browser

picture
Figure: Graphs in the Browser

After all of this work, you should have a Django project that can display population data in plotly generated histograms. The front end will display the histograms in the browser using Plotly.js or you can create the same graphs with Plotly.py on the backend. This is a powerful combination that can be used to create interactive and visually appealing graphs for your web applications.

Pros and Cons of Plotly.js vs. Plotly.py

Using Plotly.js provides highly interactive graphs that can be manipulated directly in the browser, reducing server load by handling rendering on the client side and integrating well with various frontend frameworks. However, it requires knowledge of JavaScript, raises potential data privacy concerns due to client-side data handling, and may involve more complex setup. On the other hand, Plotly.py allows developers to stay within the Python ecosystem, processes data on the server side for better control over sensitive information, and can be simpler to set up within a Python environment. Despite these advantages, Plotly.py might not offer the same level of interactivity and responsiveness as Plotly.js, and it can increase server load with many users generating graphs simultaneously. Ultimately, the choice between Plotly.js and Plotly.py depends on the specific needs of the project.

Further Resources