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官方网站下载并在本地使用它,也可以通过CDN使用它。示例场景
我将使用与本教程如何使用Django ORM创建按查询分组的示例相同的示例,它是对本教程的很好补充,因为实际上处理图表的棘手部分是如何转换数据以便使其适合条形图/折线图等。
我们将使用以下两种模型,Country以及City:
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()
并将原始数据存储在数据库中:
| 城市 | |||
|---|---|---|---|
| ID | 名称 | country_id | 人口 |
| 1 | 东京 | 28 | 36,923,000 |
| 2 | 上海 | 13 | 34,000,000 |
| 3 | 雅加达 | 19 | 30,000,000 |
| 4 | 汉城 | 21 | 25,514,000 |
| 5 | 广州 | 13 | 25,000,000 |
| 6 | 北京 | 13 | 24,900,000 |
| 7 | 卡拉奇 | 22 | 24,300,000 |
| 8 | 深圳 | 13 | 23,300,000 |
| 9 | 新德里 | 25 | 21,753,486 |
| 10 | 墨西哥城 | 24 | 21,339,781 |
| 11 | 拉各斯 | 9 | 21,000,000 |
| 12 | 圣保罗 | 1 | 20,935,204 |
| 13 | 孟买 | 25 | 20,748,395 |
| 14 | 纽约市 | 20 | 20,092,883 |
| 15 | 大阪 | 28 | 19,342,000 |
| 16 | 武汉市 | 13 | 19,000,000 |
| 17 | 成都市 | 13 | 18,100,000 |
| 18 | 达卡 | 4 | 17,151,925 |
| 19 | 重庆市 | 13 | 17,000,000 |
| 20 | 天津 | 13 | 15,400,000 |
| 21 | 加尔各答 | 25 | 14,617,882 |
| 国家 | |
|---|---|
| ID | 名称 |
| 1 | 巴西 |
| 2 | 土耳其 |
| 3 | 意大利 |
| 4 | 孟加拉国 |
| 5 | 加拿大 |
| 6 | 法国 |
| 7 | 秘鲁 |
| 8 | 阿根廷 |
| 9 | 奈及利亚 |
| 10 | 澳大利亚 |
| 11 | 伊朗 |
| 12 | 新加坡 |
| 13 | 中国 |
| 14 | 智利 |
| 15 | 泰国 |
| 16 | 德国 |
| 17 | 西班牙 |
| 18 | 菲律宾 |
| 19 | 印度尼西亚 |
| 20 | 美国 |
| 21 | 南韩 |
| 22 | 巴基斯坦 |
| 23 | 安哥拉 |
| 24 | 墨西哥 |
| 25 | 印度 |
| 26 | 英国 |
| 27 | 哥伦比亚 |
| 28 | 日本 |
| 29 | 台湾 |
示例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,
})
基本上在上面的视图中,我们遍历Cityqueryset并构建的列表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逻辑。当我们放置时,我们直接在JavaScript代码中注入来自服务器的变量,如{{ data|safe}}
上面的代码展示效果如下所示:

示例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基础(24): aggregate和annotate方法使用详解与示例
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>
我们添加了获取数据的data-url。稍后,我们将使用它来执行Ajax调用。
var $populationChart = $("#population-chart");
$.ajax({
url: $populationChart.data("url"),
success: function (data) {
// 生成bar图标实例,展示data。
}
});
success以后,在回调内部,我们最终使用JsonResponse数据执行与Chart.js相关的代码, 展示效果如下图所示:

小结
我希望本教程可以帮助您开始使用Chart.js处理图表。不久前,我使用Highcharts库发布了
关于同一主题的另一篇教程。方法大致相同:如何将Highcharts.js与Django集成。
如果您想获取本教程中使用的代码,可以在这里找到:
github.com/sibtc/django-chartjs-example。
大江狗翻译整理,原作Vitor Freitas
原文地址:
https://simpleisbetterthancomplex.com/tutorial/2020/01/19/how-to-use-chart-js-with-django.html
相关阅读
Django 3.0实战: 仿链家二手房信息查询网(附GitHub源码)
Django实战:Django 3.0 +Redis 3.4 +Celery 4.4异步生成静态HTML文件
Django实战: 利用自定义模板标签实现仿CSDN博客月度归档
