Django Chart Visualization

4 minute read

1. Add chart.js and jquery (layouts.html)

This is currently located at head, but later Iโ€™ve to move those scripts down to the end of the body tag.

<!--                 layouts.html                       -->
<!--                 head tag                       -->
...
<!-- Chart js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js" integrity="sha256-Uv9BNBucvCPipKQ2NS9wYpJmi8DTOEfTA/nH2aoJALw=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.css" integrity="sha256-aa0xaJgmK/X74WM224KMQeNQC2xYKwlAt08oZqjeF0E=" crossorigin="anonymous" />
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
...

2. Make a naive chart.html template

<!--                  chart.html                          -->
{% extends 'layouts.html' %} {% comment %} load layouts using extends {% endcomment %}

{%block title%}Chart Page{%endblock%}

{% block content %}
{% comment %} {% include 'slider.html'%} {% endcomment %}
<div class="jumbotron bg-white text-black">
  <h1 class="text-center">Chart page</h1><br>
{% comment %} </div>
 <div class="row chartContainer">
  <div class="column"><canvas id="chart1" style="width:30vw; height:50vh"></canvas></div>
  <div class="column"><canvas id="chart2" style="width:30vw; height:50vh"></canvas></div>
</div>  {% endcomment %}
{% endblock %}

3. Upload csv file to Django by bulk_create

3-1. Prepare Dataset

csv file with no header.

df.to_csv("yourfile.csv", index=False, header=None)

3-2. Create a Model

# EdaApp/models.py
from django.db import models

# Create your models here.
class Product(models.Model):
    InvoiceId = models.CharField(default="", max_length=30) # null=False(default): must have value
    Branch = models.CharField(default="", max_length=2)
    City = models.CharField(default="", max_length=30)
    CustomerType = models.CharField(default="", max_length=6)
    Gender = models.CharField(default="", max_length=6)
    ProductLine = models.CharField(default="", max_length=30)
    UnitPrice = models.FloatField(default=0)
    Quantity = models.IntegerField(default=0)
    Tax = models.FloatField(default=0)
    Total = models.FloatField(default=0)
    Date = models.DateField()
    Time = models.TimeField()
    Payment = models.CharField(default="", max_length=20)
    Cogs = models.FloatField(default=0)
    GrossMargin = models.FloatField(default=0)
    GrossIncome = models.FloatField(default=0)
    Rating = models.FloatField(default=0)
    Year = models.IntegerField(default=0)
    Month = models.IntegerField(default=0)
    Day = models.IntegerField(default=0)

3-3. Add data to Model with bulk_create

python manage.py shell
>>> import csv
>>> from EdaApp.models import Product
>>> data = open('market.csv')
>>> reader = csv.reader(data)
>>> reader
<_csv.reader object at 0x7f12b88c0f90>
>>> bulk_list = []
>>> for row in reader:
...     bulk_list.append(Product(
...             InvoiceId = row[0],
...             Branch = row[1],
...             City = row[2],
...             CustomerType = row[3],
...             Gender = row[4],
...             ProductLine = row[5],
...             UnitPrice = row[6],
...             Quantity = row[7],
...             Tax = row[8],
...             Total = row[9],
...             Date = row[10],
...             Time = row[11],
...             Payment = row[12],
...             Cogs = row[13],
...             GrossMargin = row[14],
...             GrossIncome = row[15],
...             Rating = row[16],
...             Year = row[17],
...             Month = row[18],
...             Day = row[19]))

>>> Product.objects.bulk_create(bulk_list)

4. Add ProductChartView to views.py

template_name is dir of the template to use.
Using Sum in django.db.models, doing groupby Branchsโ€™ sum of UnitPrice
For more about annotate, go to appendix.

from django.db.models import Sum
...
class ProductChartView(TemplateView):
    template_name = "EdaApp/chart.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['qs'] = Product.objects.all()
        context['branch_sum_agg'] = Product.objects.values('Branch').annotate(branch_sum=Sum('UnitPrice'))
        return context

context[โ€˜branch_sum_aggโ€™] looks like:

<QuerySet [{'Branch': 'A', 'branch_sum': 18550.8}, {'Branch': 'B', 'branch_sum': 18478.88}, {'Branch': 'C', 'branch_sum': 18567.76}]>

5. Add the ProductChartView to urls.py

from EdaApp.views import home, eda, ProductChartView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', home, name='home'),
    path('eda/', eda, name='eda'),
    path('chart/', ProductChartView.as_view(), name='chart'),
]

6. Making chart in chart.html

Example of Two chart

6-1. Add scripts block in layouts.html

Content block is used for h1 tag, so create scripts block to its below.

{% block content%}
{% endblock %}

{% block scripts%}
{% endblock %}

6-2. Insert script in chart.html

Create two charts like this:

  • Go to chart.html
  • loop branch_sum_agg
    • get Branch name by x.Branch (labels)
    • get branch sum by x.branch_sum (data)
    • set label as UnitPrice
{% block scripts%}
<script>
$(document).ready(function(){
  var ctx = document.getElementById('chart1');
  var chart1 = new Chart(ctx, {
      type: 'bar',
      data: {
          labels: [{% for x in branch_sum_agg %}'{{x.Branch}}',{% endfor %}],          
          datasets: [{
              label: 'UnitPrice',
              data: [{% for x in branch_sum_agg %}'{{x.branch_sum}}',{% endfor %}],
              backgroundColor: [
                  'rgba(255, 99, 132, 0.2)',
                  'rgba(54, 162, 235, 0.2)',
                  'rgba(255, 206, 86, 0.2)',
                  'rgba(75, 192, 192, 0.2)',
                  'rgba(153, 102, 255, 0.2)',
                  'rgba(255, 159, 64, 0.2)'
              ],
              borderColor: [
                  'rgba(255, 99, 132, 1)',
                  'rgba(54, 162, 235, 1)',
                  'rgba(255, 206, 86, 1)',
                  'rgba(75, 192, 192, 1)',
                  'rgba(153, 102, 255, 1)',
                  'rgba(255, 159, 64, 1)'
              ],
              borderWidth: 1
          }]
      },
      options: {
          responsive: false,
          scales: {
              y: {
                  beginAtZero: true
              }
          }
      }
  });
  })
</script>
<script>
$(document).ready(function(){
  var ctx = document.getElementById('chart2');
  var chart2 = new Chart(ctx, {
      type: 'bar',
      data: {
          labels: [{% for x in branch_sum_agg %}'{{x.Branch}}',{% endfor %}],
          datasets: [{
              label: 'UnitPrice',
              data: [{% for x in branch_sum_agg %}'{{x.branch_sum}}',{% endfor %}],
              backgroundColor: [
                  'rgba(255, 99, 132, 0.2)',
                  'rgba(54, 162, 235, 0.2)',
                  'rgba(255, 206, 86, 0.2)',
                  'rgba(75, 192, 192, 0.2)',
                  'rgba(153, 102, 255, 0.2)',
                  'rgba(255, 159, 64, 0.2)'
              ],
              borderColor: [
                  'rgba(255, 99, 132, 1)',
                  'rgba(54, 162, 235, 1)',
                  'rgba(255, 206, 86, 1)',
                  'rgba(75, 192, 192, 1)',
                  'rgba(153, 102, 255, 1)',
                  'rgba(255, 159, 64, 1)'
              ],
              borderWidth: 1
          }]
      },
      options: {
          responsive: false,
          scales: {
              y: {
                  beginAtZero: true
              }
          }
      }
  });
  })
</script>
{% endblock %}

6-3. Insert div for chart with two canvases inside of the content block


{% block content %}
<div class="jumbotron bg-white text-black">
  <h1 class="text-center">Chart page</h1><br>

<div class='chartContainer'>
  <canvas id="chart1" style="width:30vw; height:50vh; display: inline-block; margin-right:200px;"></canvas>
  <canvas id="chart2" style="width:30vw; height:50vh; display: inline-block;"></canvas>
</div>
{% endblock %}

class=โ€™chartContainerโ€™ has two canvases.
To align these charts to the center and horizontally, make style to inline-block.

Appendix

<script>

๋ธŒ๋ผ์šฐ์ €๋Š” HTML ๋ฌธ์„œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋‹ค๊ฐ€ <script> ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ๋งŒ๋‚˜๋ฉด src ์†์„ฑ์— ๋ช…์‹œ๋œ ๊ฒฝ๋กœ์˜ ํŒŒ์ผ์„ ๋‚ด๋ ค๋ฐ›์•„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
์•„๋ž˜์™€ ๊ฐ™์ด <script> ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ <body> ์—˜๋ฆฌ๋จผํŠธ์˜ ์ค‘๊ฐ„์— ์˜ค๊ฒŒ๋˜๋ฉด ์–ด๋–ค ์ผ์ด ์ผ์–ด๋‚ ๊นŒ์š”?

<body>
  <h2>A</h2>
  <script src="./script.js"></script>
  <h2>B</h2>
</body>

์œ„ HTML ๋ฌธ์„œ๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

    <h2>A</h2>๊ฐ€ ํ™”๋ฉด์— ์ถœ๋ ฅ๋จ
    script.js ํŒŒ์ผ์„ ๋‚ด๋ ค๋ฐ›์•„ ์ž๋ฐ”์Šค๋ฆฝํŠธ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋จ
    <h2>B</h2>๊ฐ€ ํ™”๋ฉด์— ์ถœ๋ ฅ

์ „ํ†ต์ ์œผ๋กœ HTML ์ž…๋ฌธ์ž๋“ค์€ <script> ์—˜๋ฆฌ๋จผํŠธ๋ฅผ <head> ์—˜๋ฆฌ๋จผํŠธ ์•ˆ์— ๋„ฃ๋„๋ก ๋ฐฐ์šฐ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•˜์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ ๊ฐœ๋ฐœ์„ ํ•˜๋‹ค๋ณด๋ฉด <body> ์—˜๋ฆฌ๋จผํŠธ์˜ ์ œ์ผ ๋งˆ์ง€๋ง‰์—์„œ <script> ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ๋ณด๊ฒŒ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ํ›จ์”ฌ ๋งŽ๋‹ค๋Š” ๊ฒƒ์„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด๋‹ค ์ตœ์ ํ™”๋œ ์›นํŽ˜์ด์ง€ ๋กœ๋”ฉ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๋ ค๋ฉด <script> ์—˜๋ฆฌ๋จผํŠธ๋ฅผ <body> ์—˜๋ฆฌ๋จผํŠธ์˜ ๋งˆ์ง€๋ง‰์— ๋„ฃ๋Š” ๊ฒƒ์ด ์œ ๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

Django annotate Vs. aggregate

>>> from django.db.models import Sum
>>> data = Product.objects.values('Branch').annotate(branch_sum=Sum('UnitPrice'))
>>> data
<QuerySet [{'Branch': 'A', 'branch_sum': 18550.8}, {'Branch': 'B', 'branch_sum': 18478.88}, {'Branch': 'C', 'branch_sum': 18567.76}]>

References

best for chart.js (2 years ago):
https://www.youtube.com/watch?v=1OL5n06kO_w

best for django rest api with chart.js, (4 years ago):
https://www.youtube.com/watch?v=B4Vmm3yZPgc

for multiple charts:
https://canvasjs.com/docs/charts/how-to/render-multiple-charts-in-a-page/


ref:

https://testdriven.io/blog/django-charts/
https://www.freecodecamp.org/news/how-to-create-an-analytics-dashboard-in-django-app/
https://www.youtube.com/watch?v=jrT6NiM46jk
https://dowtech.tistory.com/3
https://m.blog.naver.com/tablesetter/221499332197
https://www.youtube.com/watch?v=B4Vmm3yZPgc
script: https://www.daleseo.com/js-script-defer-async/ csv to django DB: https://velog.io/@swhybein/Django-bulkcreate%EC%9C%BC%EB%A1%9C-csv%ED%8C%8C%EC%9D%BC-%EC%98%AC%EB%A6%AC%EA%B8%B0
django-annotate: https://velog.io/@may_soouu/%EC%9E%A5%EA%B3%A0-Annotate-Aggregate
django-annotate official docs: https://docs.djangoproject.com/en/3.2/topics/db/aggregation/

Leave a comment