2014-10-19 19 views
16

Tôi đang cố gắng viết biểu đồ thanh rất cơ bản bằng cách sử dụng Django và D3.js. Tôi có một đối tượng được gọi là chơi với một trường datetime được gọi là ngày tháng. Những gì tôi muốn làm là hiển thị số lượt phát theo thời gian được nhóm theo tháng. Về cơ bản tôi có hai câu hỏi:Truyền dữ liệu từ Django đến D3

  1. Làm thế nào để tôi nhận được những nhóm lại theo tháng với đếm số lượt trong tháng đó
  2. cách tốt nhất để có được thông tin này từ Django vào một cái gì đó có thể sử dụng bởi D3 là gì .

Bây giờ tôi đã xem xét một số câu trả lời khác trên đây và đã cố gắng

json = (Play.objects.all().extra(select={'month': "extract(month FROM date)"}) 
.values('month').annotate(count_items=Count('date'))) 

này được gần trhe thông tin tôi muốn nhưng khi tôi cố gắng ra nó trong mẫu nói ra như sau (với Ls) vào cuối tháng. Điều này có nghĩa rằng rõ ràng nó không phải là js hợp lệ (không có qoutes) và tôi không thực sự muốn Ls vào cuối có anyway.

mẫu:

<script> 
     var test ={{ json|safe }}; 
     alert("test"); 

    </script> 

đầu ra:

var test = [{'count_items': 10, 'month': 1L}, {'count_items': 5, 'month': 2L}]; 

Tôi cũng đã cố gắng json.dumps trên dữ liệu này nhưng tôi đã đươc nghe nó không phải là JSON hợp lệ. Điều này cảm thấy như nó nên được nhiều hơn nữa đơn giản để làm trong Django vì vậy có lẽ tôi đang đi xuống con đường worng hoàn toàn.

+0

Bạn đang sử dụng Django 1.7? Tôi đã có giải pháp tùy chỉnh cho bạn, chỉ để xác minh rằng chúng tôi đang sử dụng cùng một phiên bản. –

+0

Trên 1.4.1 nâng cấp khi chúng tôi nói và sau đó tôi sẽ thử câu trả lời của bạn. –

+0

Ok. Đó là vì [django.http.JsonResponse] (https://docs.djangoproject.com/en/dev/ref/request-response/#jsonresponse-objects) đã được giới thiệu vào ngày 1.7. –

Trả lời

47

Vì D3.js v3 có bộ sưu tập đẹp là methods to load data from external resources ¹, Tốt hơn hết là bạn không nhúng dữ liệu vào trang của mình, bạn chỉ cần tải nó.

Đây sẽ là câu trả lời bằng ví dụ.

Hãy bắt đầu với một định nghĩa mô hình:

# models.py 
from django.db import models 


class Play(models.Model): 
    name = models.CharField(max_length=100) 
    date = models.DateTimeField() 

Một URLconf:

# urls.py 
from django.conf.urls import url 


from .views import graph, play_count_by_month 

urlpatterns = [ 
    url(r'^$', graph), 
    url(r'^api/play_count_by_month', play_count_by_month, name='play_count_by_month'), 
] 

Chúng tôi đang sử dụng hai url, một trở về html (xem), và các url khác (xem play_count_by_month) làm api để chỉ trả về dữ liệu dưới dạng JSON.

Và cuối cùng quan điểm của chúng tôi:

# views.py 
from django.db import connections 
from django.db.models import Count 
from django.http import JsonResponse 
from django.shortcuts import render 

from .models import Play 


def graph(request): 
    return render(request, 'graph/graph.html') 


def play_count_by_month(request): 
    data = Play.objects.all() \ 
     .extra(select={'month': connections[Play.objects.db].ops.date_trunc_sql('month', 'date')}) \ 
     .values('month') \ 
     .annotate(count_items=Count('id')) 
    return JsonResponse(list(data), safe=False) 

Ở đây chúng ta định nghĩa một cái nhìn để trả lại dữ liệu của chúng tôi như JSON, lưu ý rằng tôi đã thay đổi thêm để có cơ sở dữ liệu thuyết bất khả tri, vì tôi đã thử nghiệm với SQLite.

Và sau mẫu của chúng tôi graph/graph.html cho thấy một đồ thị của số lần nghe nhạc theo tháng:

<!DOCTYPE html> 
<meta charset="utf-8"> 
<style> 

body { 
    font: 10px sans-serif; 
} 

.axis path, 
.axis line { 
    fill: none; 
    stroke: #000; 
    shape-rendering: crispEdges; 
} 

.x.axis path { 
    display: none; 
} 

.line { 
    fill: none; 
    stroke: steelblue; 
    stroke-width: 1.5px; 
} 

</style> 
<body> 
<script src="http://d3js.org/d3.v3.js"></script> 
<script> 

var margin = {top: 20, right: 20, bottom: 30, left: 50}, 
    width = 960 - margin.left - margin.right, 
    height = 500 - margin.top - margin.bottom; 

var parseDate = d3.time.format("%Y-%m-%d").parse; // for dates like "2014-01-01" 
//var parseDate = d3.time.format("%Y-%m-%dT00:00:00Z").parse; // for dates like "2014-01-01T00:00:00Z" 

var x = d3.time.scale() 
    .range([0, width]); 

var y = d3.scale.linear() 
    .range([height, 0]); 

var xAxis = d3.svg.axis() 
    .scale(x) 
    .orient("bottom"); 

var yAxis = d3.svg.axis() 
    .scale(y) 
    .orient("left"); 

var line = d3.svg.line() 
    .x(function(d) { return x(d.month); }) 
    .y(function(d) { return y(d.count_items); }); 

var svg = d3.select("body").append("svg") 
    .attr("width", width + margin.left + margin.right) 
    .attr("height", height + margin.top + margin.bottom) 
    .append("g") 
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 

d3.json("{% url "play_count_by_month" %}", function(error, data) { 
    data.forEach(function(d) { 
    d.month = parseDate(d.month); 
    d.count_items = +d.count_items; 
    }); 

    x.domain(d3.extent(data, function(d) { return d.month; })); 
    y.domain(d3.extent(data, function(d) { return d.count_items; })); 

    svg.append("g") 
     .attr("class", "x axis") 
     .attr("transform", "translate(0," + height + ")") 
     .call(xAxis); 

    svg.append("g") 
     .attr("class", "y axis") 
     .call(yAxis) 
    .append("text") 
     .attr("transform", "rotate(-90)") 
     .attr("y", 6) 
     .attr("dy", ".71em") 
     .style("text-anchor", "end") 
     .text("Play count"); 

    svg.append("path") 
     .datum(data) 
     .attr("class", "line") 
     .attr("d", line); 
}); 

</script> 
</body> 
</html> 

này sẽ trả về một đồ thị đẹp như thế này (dữ liệu ngẫu nhiên): Graph of Play counts by month

Cập nhật 1: D3 v4 sẽ di chuyển mã để tải dữ liệu ngoài vào một lib chuyên dụng, vui lòng xem d3-request. Cập nhật 2: Để trợ giúp, tôi đã đặt tất cả các tệp lại với nhau thành một dự án mẫu, trên github: github.com/fgmacedo/django-d3-example

+0

Hmmm vì vậy tôi đã thử đặt tất cả những thứ này lại với nhau và tất cả những gì tôi nhận được là phần bên trái của biểu đồ (không có dòng hoặc nhãn). đây là kết quả đầu ra trang play_count_by_month của tôi trông như thế nào: '[{" count_items ": 10," month ":" 2013-01-01T00: 00: 00Z "}, {" count_items ": 5," month ":" 2013-02-01T00: 00: 00Z "}]' Tôi không thấy bất kỳ lỗi JS nào hoặc bất kỳ sự cố nào ở phía mạng (có vẻ như nó đã làm cho yêu cầu được chấp nhận) –

+1

Có sự cố với định dạng ngày. Kết quả của tôi về 'play_count_by_month' như sau:' [{"count_items": 731, "month": "2014-01-01"}, {"count_items": 404, "month": "2014-02-01" }] '. Bạn có thể thử thay đổi hàm 'parseDate' trên graph.html thành hàm này:' var parseDate = d3.time.format ("% Y-% m-% dT00: 00: 00Z") phân tích cú pháp; ' –

+0

Đã hoạt động! cám ơn rất nhiều. –

Các vấn đề liên quan