Reputation: 669
Dear community members,
I really hope that you will help me to add a Bar Graph to my Django project. 1. I have a Database with around 1000 items. 2. I need to be able visualise a 3 month sales for each item when needed. Not sure what is a correct approach.
here is my models.py:
from django.db import models from math import * from decimal import *
class Itemslist(models.Model):
item_n = models.CharField(max_length=200)
sales_this_month = models.DecimalField(blank=True, null=True, max_digits=19,
decimal_places=0)
saleslm = models.DecimalField(blank=True, null=True, max_digits=19, decimal_places=0)
sales2m = models.DecimalField(blank=True, null=True, max_digits=19, decimal_places=0)
sales3m = models.DecimalField(blank=True, null=True, max_digits=19, decimal_places=0)
def __str__(self):
return self.item_n
here is my views.py file, that as an experiment I have created, using the last solution provided:
def charts(request):
charts = Itemslist.objects \
.values('saleslm') \
.annotate(lm=Count('saleslm')) \
.annotate(m2=Count('sales2m')) \
.annotate(3m3=Count('sales3m')) \
.order_by('saleslm')
return render(request, 'charts.html', {'charts': charts})
As you can see, this is not a solution I need, I was just trying to come up with at least something , and eaven that has shown me the graph with the same values. here is my hmtl code:
{% extends 'base.html' %}
{% block js %}
{% if user.is_authenticated %}
{% load loads_extra %}
{% load static %}
<br>
<p>Logged in user: {{ user.username }}</p>
<br>
<html>
<head>
<meta charset="utf-8">
<title>Django Highcharts Example</title>
</head>
<body>
<div id="container"></div>
{
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script>
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Sales previous 3 months'
},
xAxis: {
categories: ['sales']
},
series: [{
name: '3mBack',
data: [ {% for entry in charts %}{{ entry.m3 }}{% endfor %} ]
}, {
name: '2mBack',
data: [ {% for entry in charts %}{{ entry.m2 }}{% endfor %} ]
}, {
name: 'Lmonth',
data: [ {% for entry in charts %}{{ entry.lm }}{% endfor %} ]
}, ]
});
</script>
</body>
</html>
{% endif %}
{% endblock %}
<!-- charting tutorial to follow : https://simpleisbetterthancomplex.com/tutorial/2018/04/03/how-to-integrate-highcharts-js-with-django.html -->
I have to create a request button for charts and then chart has to be generated with a right parameters.
Have looked at this question: Displaying multiple bar graph in django
Also have searched through this solution https://code.djangoproject.com/wiki/Charts
And looked at this article https://simpleisbetterthancomplex.com/tutorial/2018/04/03/how-to-integrate-highcharts-js-with-django.html
The last article was the clearest one, and you can see, that I have just copy pasted the solution from there, with small changes. here is a script that I have placed in my base.html file:
<script src="https://code.highcharts.com/highcharts.src.js"></script>
and that's the chart that I have finally got displayed:
But still can’t find how to deal with it in my situation.
All this solutions, as far as I can see, are showing how to implement charting to one array, or to sum or self generated array. But I want to be able to chose, when to show a graph and for which Item.
the button is placed on this html file:
{% extends 'base.html' %}
{% block js %}
{% if user.is_authenticated %}
{% load loads_extra %}
{% load static %}
<br>
<p>Logged in user: {{ user.username }}</p>
<br>
<body>
<table id="example" class="table table-striped table-bordered dt-responsive nowrap" style="width:100%">
<thead>
<tr>
<th>SUP:</th>
<th>Item N.:</th>
<th>SKU</th>
<th>Description</th>
<th>3mBack</th>
<th>2mBack</th>
<th>Lmonth</th>
<th>CMonth</th>
<th>Nmonth</th>
<th>N2Month</th>
<th>N3month</th>
<th></th>
</tr>
</thead>
<tbody>
{% for records in sorted %}
<tr>
<td>{{ records.sup }}</td>
<td>{{ records.item_n }}</td>
<td>{{ records.sku }}</td>
<td>{{ records.description }}</td>
<td>{{ records.sales3m }}</td>
<td>{{ records.sales2m }}</td>
<td>{{ records.saleslm }}</td>
<td>{{ records.sales_this_month }}</td>
<td>{{ records.m1predicted }}</td>
<td>{{ records.m2predicted }}</td>
<td>{{ records.m3predicted }}</td>
<td>
<a href="{% url 'edit' records.id %}" class="btn btn-secondary">Edit</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<script>
$(document).ready(function() {
var table = $('#example').DataTable( {
fixedColumns: true,
lengthChange: true,
buttons: [ 'copy', 'excel', 'csv', 'pdf', 'colvis' ]
} );
table.buttons().container()
.appendTo( '#example_wrapper .col-md-6:eq(0)' );
} );
</script>
</body>
</html>
{% endif %}
<div></div>
{% endblock js %}
This is my first question in this community, so if something is not clear, please help me to correct it in a right way.
waiting for any helpful answers!!!
Upvotes: 2
Views: 4813
Reputation: 669
Good News community.
I have been offered a very good solution, and now my question is completely sorted.
At least for my needs, this is a perfect solution.
instead of highcharts, I have been offered to use d3js.org, which is absolutely fine for me.
The logic behind a code is that you request data from the values displayed, hopefully it will be clear, when you will go through the code with comments.
as it's a django project, here's the heading of my html file:
{% extends 'base.html' %}
{% block js %}
{% if user.is_authenticated %}
{% load loads_extra %}
{% load static %}
<br>
<p>Logged in user: {{ user.username }}</p>
<br>
here is a part of my html code that displays table/value:
<table id="example" class="table table-striped table-bordered dt-responsive nowrap" style="width:100%">
<thead>
<tr>
<th>SUP:</th>
<th>Item N.:</th>
<th>SKU</th>
<th>Description</th>
<th>6mBack</th>
<th>5mBack</th>
<th>4mBack</th>
<th>3mBack</th>
<th>2mBack</th>
<th>Lmonth</th>
<th>CMonth</th>
<th>Nmonth</th>
<th>N2Month</th>
<th>N3month</th>
<th>AVGrowth</th>
<th></th>
<!-- This is header for new button to draw the Bar Charts -->
<th></th>
</tr>
</thead>
<tbody>
{% for records in sorted %}
<tr>
<td>{{ records.sup }}</td>
<td>{{ records.item_n }}</td>
<td>{{ records.sku }}</td>
<td>{{ records.description }}</td>
<td>{{ records.sales6m }}</td>
<td>{{ records.sales5m }}</td>
<td>{{ records.sales4m }}</td>
<td>{{ records.sales3m }}</td>
<td>{{ records.sales2m }}</td>
<td>{{ records.saleslm }}</td>
<td>{{ records.sales_this_month }}</td>
<td>{{ records.m1predicted }}</td>
<td>{{ records.m2predicted }}</td>
<td>{{ records.m3predicted }}</td>
<td>{{ records.avgrowths }}</td>
<td>
<a href="{% url 'edit' records.id %}" class="btn btn-secondary">Edit</a>
</td>
<!-- Add new button for drawing Bar Charts -->
<td>
<button class="btn btn-secondary" onclick="draw_chart(this)" data-toggle="modal" data-target="#myModal">Chart</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
please read comments to understand what is what.
and here is the rest of html, that is generating a chart on a topup window,by pressing a button in front of an array that has to be visualised:
<!-- Modal which Bar Chart will be placed -->
<div id="myModal" class="modal fade" role="dialog">
<div class="modal-dialog" style="max-width: 900px !important">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<div class="modal-body">
<!-- <svg> element which will contains the Bar Chart -->
<svg width="1000" height="500"></svg>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Insert D3.js Library -->
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
$(document).ready(function() {
var table = $('#example').DataTable( {
fixedColumns: true,
lengthChange: true,
buttons: [ 'copy', 'excel', 'csv', 'pdf', 'colvis' ]
} );
table.buttons().container()
.appendTo( '#example_wrapper .col-md-6:eq(0)' );
} );
// Main functions for Drawing Bar chart using D3.js
function draw_chart(item){
// `item` is the current clicked button element
// `row_ele` is the parent <tr> element of the current clicked button element
row_ele = item.closest('tr');
// Get the value from the <td> element using nth-child()
val_6mBack = row_ele.querySelector("td:nth-child(5)");
val_5mBack = row_ele.querySelector("td:nth-child(6)");
val_4mBack = row_ele.querySelector("td:nth-child(7)");
val_3mBack = row_ele.querySelector("td:nth-child(8)");
val_2mBack = row_ele.querySelector("td:nth-child(9)");
val_Lmonth = row_ele.querySelector("td:nth-child(10)");
val_CMonth = row_ele.querySelector("td:nth-child(11)");
val_Nmonth = row_ele.querySelector("td:nth-child(12)");
val_N2Month = row_ele.querySelector("td:nth-child(13)");
val_N3month = row_ele.querySelector("td:nth-child(14)");
// `data` is variable which store the data for Bar Charts
data = []
// Pushing data as key/value type objects into the `data` variable
data.push({'label':'6mBack', 'value': val_6mBack.innerHTML})
data.push({'label':'5mBack', 'value': val_5mBack.innerHTML})
data.push({'label':'4mBack', 'value': val_4mBack.innerHTML})
data.push({'label':'3mBack', 'value': val_3mBack.innerHTML})
data.push({'label':'2mBack', 'value': val_2mBack.innerHTML})
data.push({'label':'Lmonth', 'value': val_Lmonth.innerHTML})
data.push({'label':'CMonth', 'value': val_CMonth.innerHTML})
data.push({'label':'Nmonth', 'value': val_Nmonth.innerHTML})
data.push({'label':'N2Month', 'value': val_N2Month.innerHTML})
data.push({'label':'N3month', 'value': val_N3month.innerHTML})
// Set <svg> element's width and height
var svg = d3.select("svg"),
margin = 200,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin
// Remove the old contents of the <svg> element
svg.selectAll('*').remove()
// Initialize X-axis and Y-axis for Bar Chart
var xScale = d3.scaleBand().range([0, width]).padding(0.4),
yScale = d3.scaleLinear().range([height, 0]);
// Set all group which is placed in the <svg>element
// transform to (50,100) on <svg> area , margint in svg has been changed to 300 and instead 50/100, changed to 100/300, but then back.
var g = svg.append("g")
.attr("transform", "translate(" + 50 + "," + 100 + ")");
xScale.domain(data.map(function(d) { return d.label; }));
// If all values of data will be zero, we will fix the range of the Y-axis
if(d3.max(data, function(d) { return d.value; }) == 0){
yScale.domain([0, 10]);
}else{
// If all is not zero, we will set Y-axis from 0 to maximum value.
yScale.domain([0, d3.max(data, function(d) { return Number(d.value); })]);
}
// Set X- axis
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
// Set Y-axis using ticket
g.append("g")
.call(d3.axisLeft(yScale).tickFormat(function(d){
return d;
}).ticks(10));
console.log(data)
// Draw Bar Chart using <rect> element by data which is stored in the `data` variable
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return xScale(d.label); })
.attr("y", function(d) { return yScale(d.value); })
.attr("width", xScale.bandwidth())
.attr("height", function(d) { return height - yScale(d.value); })
.style('fill','#899da6');
}
</script>
</body>
{% endif %}
{% endblock js %}
so finally here is how my html looks like live:
and here is a graph, that I get by pressing chart button:
in order to help community, please ask questions, and please add/correct if there is a better way.
Thank you very much for your help and attention!!!
Upvotes: 2
Reputation: 169
views.py You have an error on annotate(3m3 its cannot start with 3 here. You also have same function name and variable name def charts and charts inside. Python might be smart enough to figure it out but try to avoid doing this. I am also against naming variables saleslm and sales2m be explicit here.
def charts(request):
return render(request, 'charts.html')
def charts_ajax(request):
charts_data = Itemslist.objects \
.values('saleslm') \
.annotate(lm=Count('saleslm')) \
.annotate(m2=Count('sales2m')) \
.annotate(m3=Count('sales3m')) \
.order_by('saleslm')
return JsonResponse({'charts': list(charts_data)})
url.py
path('charts/', views.charts, name='charts_home'),
path('charts_ajax/', views.charts_ajax, name='render_charts_ajax')
html You have multiple issues in your html file.
</html>
{% endif %}
<div></div>
{% endblock js %}
You have div after the html has ended. You should not have anything after html ends. Few other organizational issues here. I usually would have a block content and then block js while you have everything inside block js. I would clean those. Now that you added datables also. You can add buttons as mentioned here. https://datatables.net/extensions/buttons/examples/initialisation/custom.html but if i were you i would try to make it work with simple table and move on to datables.
<html>
<head>
<meta charset="utf-8">
<title>Django Highcharts Example</title>
</head>
<body>
<div id="container">
</div>
<button id="render_chart" type="button">Render Chart</button>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"></script>
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script>
$(document).ready(function(){
$('#render_chart').on('click', function(e){
$.ajax({
url: "{% url 'render_charts_ajax'%}",
type: "GET",
dataType: "json",
success: function (data) {
console.log(data.charts[0].saleslm)
Highcharts.chart('container',{
chart:{
type:'bar'
},
title:{
text:"Sales Last Month"
},
series:[{
name:"Sales LM",
data:[parseInt(data.charts[0].saleslm)]
}]
})
}
})
})
});
</script>
</body>
</html>
Your json might need some processing before the chart comes out right.
Upvotes: 1