Django Charts - Simple Bar Chart displayed in three ways
Hello Coders,
This article presents an open-source Django project that provides Bar Chart widgets coded in three different ways using MorisJS library:
- (LIVE Demo) Charts from JSON - using this CSV sample
- (LIVE Demo) Charts from Table - simple STATS table
- (LIVE Demo) Charts from DATA chunk - using this (CSV) sample file
The code can be downloaded from Github and used in hobby & commercial products based on the permissive license.
Resources
For more resources and support please access:
- Project source code: Django Simple Charts
- More Django Dashboards - open-source and paid projects
Django Charts - From JSON
The starter provides a controller that loads a JSON file and simply injected into the view.
chart_morris.json
{
"element": "morris-bar-chart",
"data": [
{ "y": "2017", "a": "150", "b": "90", "c": "80" },
{ "y": "2018", "a": "220", "b": "350", "c": "50" },
{ "y": "2019", "a": "80", "b": "300", "c": "240" },
{ "y": "2020", "a": "180", "b": "30", "c": "10" }
],
"xkey": "y",
"barSizeRatio": 0.70,
"barGap": 3,
"resize": true,
"responsive": true,
"ykeys": ["a", "b", "c"],
"labels": ["Product A", "Product B", "Product C"],
"barColors": ["0-#1de9b6-#1dc4e9", "0-#899FD4-#A389D4", "#04a9f5"]
}
Controller Code
Source: app/views.py - charts_file():
# Partial content from file: app/views.py
def charts_file(request):
context = {'segment': 'charts_from_file'}
html_template = loader.get_template('charts-from-file.html')
with open('sample_data/chart_morris.json', 'r') as f:
context['chart_data'] = json.dumps(json.load(f))
return HttpResponse(html_template.render(context, request))
Chart Output in View
Django Charts - From input
This time the information is loaded from STATS table where the information is saved.
Charts Render flow
The STATS model comes with an inner method that selects all rows: get_report(). Once the STATS rows are selected, the controller reads the page template and injects the information in view.
Source: app/views.py - charts_input():
# Partial content from file: app/views.py
def charts_input(request):
context = {'segment': 'charts_from_input'}
html_template = loader.get_template('charts-from-input.html')
stats, labels = Stats.get_report()
data = [
{
'y': year,
'a': '{:.2f}'.format( stats[year].get('prod1_sales') ),
'b': '{:.2f}'.format( stats[year].get('prod2_sales') ),
'c': '{:.2f}'.format( stats[year].get('prod3_sales') )
} for year in stats
]
context['chart_data'] = json.dumps({
'element': 'morris-bar-chart',
'data': data,
'xkey': 'y',
'barSizeRatio': 0.70,
'barGap': 3,
'resize': True,
'responsive': True,
'ykeys': ['a', 'b', 'c'], # it can be custom
'labels': labels,
'barColors': ['0-#1de9b6-#1dc4e9', '0-#899FD4-#A389D4', '#04a9f5'] # it can be custom
})
return HttpResponse(html_template.render(context, request))
Database Model - STATS
The model comes with an inner method that selects all rows and exposes the relevant information: get_report().
class Stats(models.Model):
year = models.IntegerField(_('year') , db_index=True)
prod1_sales = models.IntegerField(_('product 1 sales'), db_index=True)
prod2_sales = models.IntegerField(_('product 2 sales'), db_index=True)
prod3_sales = models.IntegerField(_('product 3 sales'), db_index=True)
class Meta:
verbose_name = _('statistic')
verbose_name_plural = _('stats')
@classmethod
def get_report(cls):
data = {}
labels = ['prod1_sales', 'prod2_sales', 'prod3_sales']
stats = Stats.objects.order_by('year').values()
for line in stats:
if line['year'] not in data:
data[line['year']] = {}
data[ line['year'] ]['prod1_sales'] = line['prod1_sales']
data[ line['year'] ]['prod2_sales'] = line['prod2_sales']
data[ line['year'] ]['prod3_sales'] = line['prod3_sales']
return data, labels
Chart Output in View
Django Charts - Load from CSV file
This time we have a new model called SALES provisioned from a CSV file. To upload the information we need to use django-import-export Python package and a superuser to execute the import.
The SALES model
class Sale(models.Model):
amount = models.FloatField(_('amount'), db_index=True)
product_name = models.CharField(_('product name'), max_length=40, db_index=True)
created_time = models.DateTimeField(verbose_name=_('creation On'), db_index=True)
updated_time = models.DateTimeField(verbose_name=_('modified On'), auto_now=True)
class Meta:
verbose_name = _('sale')
verbose_name_plural = _('sales')
@classmethod
def get_sales_report(cls):
annotates = {'total_amount': Sum('amount')}
sales = cls.objects.annotate(
year=TruncYear('created_time')
).values('product_name', 'year').order_by().annotate(**annotates)
data = {}
for sale in sales:
if sale['year'].year not in data:
data[sale['year'].year] = {}
data[sale['year'].year][sale['product_name']] = sale['total_amount']
labels = list(sales.values_list('product_name', flat=True).distinct())
return data, labels
Inner method get_sales_report() scan the information and return a dictionary with formatted data.
Controller method
# Partial content from file: app/views.py
def charts_load(request):
context = {'segment': 'charts_from_load'}
html_template = loader.get_template('charts-from-load.html')
# -----------------------------------------------
# Extract data from Sale table
# -----------------------------------------------
sales, labels = Sale.get_sales_report()
data = [
{
'y': year,
'a': '{:.2f}'.format(sales[year].get('A')),
'b': '{:.2f}'.format(sales[year].get('B')),
'c': '{:.2f}'.format(sales[year].get('C'))
} for year in sales
]
context['chart_data'] = json.dumps({
'element': 'morris-bar-chart',
'data': data,
'xkey': 'y',
'barSizeRatio': 0.70,
'barGap': 3,
'resize': True,
'responsive': True,
'ykeys': ['a', 'b', 'c'], # it can be custom
'labels': labels,
'barColors': ['0-#1de9b6-#1dc4e9', '0-#899FD4-#A389D4', '#04a9f5'] # it can be custom
})
return HttpResponse(html_template.render(context, request))
Chart Output in View
Adi Chirilov rascal
whats the import statement
yes even i had the same doubt
++