You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django环境下用d3.js加载自定义JSON数据绘制直方图?

如何在D3.js中使用Django输出的JSON数据绘制直方图

刚接触JS、JSON和D3的话,确实容易卡在「后端数据怎么喂给前端可视化」这一步,我来一步步帮你解决这个问题:

第一步:先确保Django正确输出JSON数据

你提到的playAround视图还没写完,首先得把它补全,确保返回的是前端能直接用的JSON格式。比如我们要按月份统计客户数量,返回每个月的日期字符串和对应数量:

from django.http import JsonResponse
from django.db.models import TruncMonth, Count
from .models import Customer

def playAround(request):
    # 按月份分组统计客户数,记得排序保证顺序正确
    raw_data = Customer.objects.annotate(
        month=TruncMonth('Update')  # 把Update字段截断到月份
    ).values('month').annotate(
        count=Count('id')  # 统计每个月的客户数量
    ).order_by('month')
    
    # 把Django的日期对象转成字符串,避免JSON序列化问题
    formatted_data = []
    for item in raw_data:
        formatted_data.append({
            'month': item['month'].strftime('%Y-%m'),  # 转成YYYY-MM格式,前端好处理
            'count': item['count']
        })
    
    # 返回JSON数组,safe=False因为我们返回的是列表不是字典
    return JsonResponse(formatted_data, safe=False)

写完之后,直接访问你的API路由/api/playAround,看看返回的是不是类似这样的格式:

[{"month": "2024-01", "count": 8}, {"month": "2024-02", "count": 12}, ...]

如果返回正确,那后端就没问题了。

第二步:用D3.js异步获取并使用数据

你之前参考的示例用的是内置数据,现在要换成D3的d3.json()方法来请求Django的API。注意这个方法是异步的,所有绘制逻辑都要放在then()回调里,等数据拿到再执行:

// 替换你原来的内置数据部分
d3.json('/api/playAround')
  .then(function(data) {
    // 先在控制台打印数据,确认拿到的格式正确
    console.log('从Django获取的数据:', data);
    
    // 接下来就是你原来的直方图绘制逻辑,这里给你一个基础框架示例:
    const svg = d3.select("svg");
    const margin = {top: 20, right: 30, bottom: 30, left: 40};
    const width = svg.attr("width") - margin.left - margin.right;
    const height = svg.attr("height") - margin.top - margin.bottom;
    const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
    
    // 设置X轴比例尺(月份分类)
    const xScale = d3.scaleBand()
      .domain(data.map(item => item.month))  // 用每个月的字符串作为X轴域
      .range([0, width])
      .padding(0.1);  // 条形之间的间距
    
    // 设置Y轴比例尺(数量线性)
    const yScale = d3.scaleLinear()
      .domain([0, d3.max(data, item => item.count)])  // Y轴从0到最大数量
      .nice()  // 自动调整Y轴刻度更美观
      .range([height, 0]);  // D3的Y轴是从上到下,所以range是[height, 0]
    
    // 绘制条形
    g.selectAll(".bar")
      .data(data)
      .enter()
      .append("rect")
        .attr("class", "bar")
        .attr("x", item => xScale(item.month))
        .attr("y", item => yScale(item.count))
        .attr("width", xScale.bandwidth())
        .attr("height", item => height - yScale(item.count))
        .attr("fill", "#69b3a2");
    
    // 添加X轴
    g.append("g")
      .attr("transform", `translate(0, ${height})`)
      .call(d3.axisBottom(xScale));
    
    // 添加Y轴
    g.append("g")
      .call(d3.axisLeft(yScale));
  })
  .catch(function(error) {
    // 如果请求失败,在控制台打印错误信息方便排查
    console.error('获取数据出错:', error);
  });

几个关键注意点:

  1. URL路径:如果你的JS代码是写在Django模板里的,最好用Django的{% url 'playAround' %}生成URL,避免硬编码路径出错:

    d3.json("{% url 'playAround' %}")
    

    如果是单独的JS文件,那可以在模板里定义一个全局变量传递URL:

    <script>const apiUrl = "{% url 'playAround' %}";</script>
    

    然后在JS文件里用apiUrl代替硬编码的路径。

  2. CSRF令牌问题:Django默认开启CSRF保护,AJAX请求需要带上CSRF令牌。你可以在模板里添加这段代码来处理:

    // 获取CSRF令牌的工具函数
    function getCookie(name) {
        let cookieValue = null;
        if (document.cookie && document.cookie !== '') {
            const cookies = document.cookie.split(';');
            for (let i = 0; i < cookies.length; i++) {
                const cookie = cookies[i].trim();
                if (cookie.substring(0, name.length + 1) === (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
    const csrftoken = getCookie('csrftoken');
    
    // 请求时带上令牌
    d3.json('/api/playAround', {
      headers: {
        'X-CSRFToken': csrftoken
      }
    })
    .then(...)
    .catch(...);
    
  3. 日期格式处理:如果之后你想用时间比例尺(比如更灵活的日期显示),可以把前端的月份字符串转成日期对象:

    const parseMonth = d3.timeParse("%Y-%m");
    data.forEach(item => {
      item.month = parseMonth(item.month);
    });
    

    然后用d3.scaleTime()代替scaleBand()做X轴比例尺。

第三步:排查常见问题

  • 如果控制台报错「403 Forbidden」,大概率是CSRF令牌没带对,检查上面的CSRF代码。
  • 如果数据为空,确认Customer表中有数据,且Update字段有合法的日期值。
  • 如果条形不显示,检查控制台的data输出,看看是不是字段名写错了(比如你Django返回的是month,前端写成了Month)。

这样一步步来,应该就能把Django的数据用在D3的直方图里了!

内容的提问来源于stack exchange,提问作者kingbrain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:53:40