如何在Django中使用Chart.js来处理图表的详细教程

217 阅读3分钟

Chart.js是一个很酷的开源JavaScript库,可以帮助你渲染HTML5图表。它是响应式的,有8种不同的图表类型。

在本教程中,我们将探讨一下如何让Django与Chart.js对话,并根据从模型中提取的数据渲染一些简单的图表。

安装

在本教程中,你所要做的就是将Chart.js库添加到你的HTML页面中:

<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>

你可以从Chart.js官方网站下载并在本地使用,也可以通过上面的URL从CDN使用。

场景示例

我将使用我在教程《如何用Django ORM创建Group By查询》中使用的相同例子,这是对本教程的一个很好的补充,因为实际上使用图表工作的棘手部分是转换数据,使其能够适应柱状图/折线图/等。

我们将使用下面的两个模型,CountryCity

class Country(models.Model):
    name = models.CharField(max_length=30)

class City(models.Model):
    name = models.CharField(max_length=30)
    country = models.ForeignKey(Country, on_delete=models.CASCADE)
    population = models.PositiveIntegerField()

例子1:饼状图

在第一个例子中,我们只打算检索人口最多的前5个城市,并将其呈现为饼图。 在这个策略中,我们将把图表数据作为视图上下文的一部分返回,并使用Django模板语言将结果注入JavaScript代码中。

views.py

from django.shortcuts import render
from mysite.core.models import City

def pie_chart(request):
    labels = []
    data = []

    queryset = City.objects.order_by('-population')[:5]
    for city in queryset:
        labels.append(city.name)
        data.append(city.population)

    return render(request, 'pie_chart.html', {
        'labels': labels,
        'data': data,
    })

基本上在上面的视图中,我们正在迭代City queryset并建立一个labels 和一个data 的列表。在这种情况下,data 是保存在City 模型中的人口数。

对于urls.py ,只是一个简单的路由。

urls.py

from django.urls import path
from mysite.core import views

urlpatterns = [
    path('pie-chart/', views.pie_chart, name='pie-chart'),
]

现在是模板。我从Chart.js的饼图文档中得到了一个基本的片段。

pie_chart.html

{% extends 'base.html' %}

{% block content %}
  <div id="container" style="width: 75%;">
    <canvas id="pie-chart"></canvas>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>
  <script>

    var config = {
      type: 'pie',
      data: {
        datasets: [{
          data: {{ data|safe }},
          backgroundColor: [
            '#696969', '#808080', '#A9A9A9', '#C0C0C0', '#D3D3D3'
          ],
          label: 'Population'
        }],
        labels: {{ labels|safe }}
      },
      options: {
        responsive: true
      }
    };

    window.onload = function() {
      var ctx = document.getElementById('pie-chart').getContext('2d');
      window.myPie = new Chart(ctx, config);
    };

  </script>

{% endblock %}

在上面的例子中,base.html 模板并不重要,但你可以在我在这篇文章末尾分享的代码例子中看到它。

这种策略并不理想,但效果不错。不好的地方是,我们使用Django模板语言来干扰JavaScript逻辑。当我们把 {{ data|safe}}的时候,我们直接在JavaScript代码中注入了一个来自服务器的变量。

上面的代码看起来是这样的:

Pie Chart


例2:带Ajax的条形图

正如标题所说,我们现在要用一个异步调用来渲染一个条形图。

views.py

from django.shortcuts import render
from django.db.models import Sum
from django.http import JsonResponse
from mysite.core.models import City

def home(request):
    return render(request, 'home.html')

def population_chart(request):
    labels = []
    data = []

    queryset = City.objects.values('country__name').annotate(country_population=Sum('population')).order_by('-country_population')
    for entry in queryset:
        labels.append(entry['country__name'])
        data.append(entry['country_population'])
    
    return JsonResponse(data={
        'labels': labels,
        'data': data,
    })

所以这里我们使用两个视图。home 视图将是加载图表的主页面。另一个视图population_chart ,它的唯一职责是汇总数据,并返回一个带有标签和数据的JSON响应。

如果你想知道这个查询集在做什么,它正在按国家对城市进行分组,并汇总每个国家的总人口。其结果将是一个国家+总人口的列表。要了解更多关于这种查询的信息,请看这篇文章。如何使用Django ORM创建Group By查询

urls.py

from django.urls import path
from mysite.core import views

urlpatterns = [
    path('', views.home, name='home'),
    path('population-chart/', views.population_chart, name='population-chart'),
]

home.html

{% extends 'base.html' %}

{% block content %}

  <div id="container" style="width: 75%;">
    <canvas id="population-chart" data-url="{% url 'population-chart' %}"></canvas>
  </div>

  <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>
  <script>

    $(function () {

      var $populationChart = $("#population-chart");
      $.ajax({
        url: $populationChart.data("url"),
        success: function (data) {

          var ctx = $populationChart[0].getContext("2d");

          new Chart(ctx, {
            type: 'bar',
            data: {
              labels: data.labels,
              datasets: [{
                label: 'Population',
                backgroundColor: 'blue',
                data: data.data
              }]          
            },
            options: {
              responsive: true,
              legend: {
                position: 'top',
              },
              title: {
                display: true,
                text: 'Population Bar Chart'
              }
            }
          });

        }
      });

    });

  </script>

{% endblock %}

现在我们有了一个更好的关注点分离。看一下图表容器。

<canvas id="population-chart" data-url="{% url 'population-chart' %}"></canvas>

我们添加了一个对持有图表渲染逻辑的URL的引用。后来我们用它来执行Ajax调用。

var $populationChart = $("#population-chart");
$.ajax({
  url: $populationChart.data("url"),
  success: function (data) {
    // ...
  }
});

success 回调里面,然后我们最后使用JsonResponse 数据执行Chart.js相关的代码:

Bar Chart


结论

我希望这个教程能帮助你开始使用Chart.js来处理图表。不久前,我发表了另一篇关于同一主题的教程,但使用的是Highcharts库。其方法基本相同。 如何将Highcharts.js与Django集成