Create A Bmi Calculator Website With Django And Bokeh

Selmi Abderrahim
17 min readNov 26, 2020

In this article, we will include Bokeh To Our Bmi Calculator To Represent The Result In Graphs. So We Will Learn How To Create A Simple Django App And How To Work With DataBase In Django, Plus The Graph Representation Using Bokeh.

Setting up the environment:

First thing, we need is a folder for our project:

$ mkdir bmi

Then create a virtual environment:

$ virtualenv env

And for people who don’t know, Python virtual environment is a tool to create an isolated environment for Python projects.

And to install it just type:

$ pip install virtualenv.

pip is the reference Python package manager. It’s used to install and update packages.

In case you don’t have pip installed in your system, type:

$ sudo apt-get install python3-pip

Next, activate the virtualenv we just created, using this command line:

$ source env/bin/activate

Then, you’ll see “env” inside parentheses, like this:

(env) $

After that, we will install django version 3.0.8, this is the version we will work with along side this tutorial.

$ pip install django==3.0.8

Now, create a django project:

$ django-admin startproject project .

Note: the dot in the end of this command line is to make sure that all folder are located in one place.

Then we create our first app, and to do so, make sure you’re in the same directory as manage.py and type this command:

$ python manage.py startapp bmi

Then I’ll use atom to edit our files.

$ atom .

Update Project Folder:

Now, we will start with settings.py file, it is the central configuration for all Django projects.

To consider the app in your project, we need to specify the app name in INSTALLED_APPS list as follows in settings.py.

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
#local_apps
'bmi', #new
]

Then, we tell django where to check for the template files, I meant the html files.

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, "templates")], #NEW
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]

Same thing with static files including css, js, images and so on.

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")] #NEW

Next, we will move to the urls.py. It should looks like this:

from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include("bmi.urls")),#NEW
]

We added a line that will import bmi.urls. we will also need to change the from django.urls… line because we are using the include function here, so you will need to add that import to the line.

Note: We are still in the project folder

Update The App Folder:

Then, create an empty urls.py file in our bmi folder:

from django.urls import path
from .views import home
urlpatterns = [
path('', home, name="home"),
]

First, we import the Django’s function path.

After that, we can add our first URL pattern.

Then import our view, We don’t have it yet, but we will get to that in a minute!

So, let’s create a simple view function to render our home page.

def home(request):
context = {}
return render(request, "bmi/index.html", context)

Create Templates:

In that case, we need to create templates and static folders, and create our home page template, which is index.html

$ mkdir templates
$ mkdir static
$ mkdir templates/bmi
$ touch templates/bmi/index.html

Then, create a random string for testing:

<!--templates/bmi/index.html-->
<h1>Tesing</h1>

Migrate our app:

$ python manage.py migrate

And run the server using:

$ python manage.py runserver

And as you see, there are no errors.

credit: realpython.com

Now, we will add a base template file, Think of it as the frame for all pages in our project. It sets the menu bar, the footer, and provides a body skeleton for any page to customize. By using it, we can ensure a standard look and feel without having to duplicate lots of HTML code.

$ touch templates/bmi/base.html

We will use Bootstrap along side this tutorial, Bootstrap is a free CSS framework directed at responsive, mobile-friendly front-end web development.

<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<!--bmi style-->
<link rel="stylesheet" href="{% static "bmi/style.css" %}">
<title>Bmi Calculator</title>
</head>
<body>
{% block content %} {% endblock content %}
</script>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>
</html>

The {% block content %} tag you see here will be used a lot. Blocks are a key part of the Django template language. They give you the ability to specify parts of the base.html file that can be changed by pages that extend it.

You’ll understand more in the next few examples.

Create The Model:

Now time to discover Django models, we need to open and edit the models.py file so that it contains the code for our BMI model. And this model contains the following database fields:

  • user, which is a relationship field. But first, we need to import the get_user_model, which will attempt to retrieve the user model class. which we will need for the foreignkey field.
  • we need also, fields for weight, height, bmi, the three of them will take float type fields. And at the end, we need a date field, with option of auto_now_add set to True, to make sure that the date added automatically.
from django.db import models
from django.contrib.auth import get_user_model
User = get_user_model()class Bmi(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
weight = models.FloatField()
height = models.FloatField()
bmi = models.FloatField()
date = models.DateField(auto_now_add=True)
def __str__(self):
return self.user.username

Then we will use the str function to override the default name of the objects of this class, so in the admin page, you’ll see the username instead of object (1) or object (2).

Update The Admin File:

In the admin.py file, we use The Django admin to add our models to our a site area that you can create, view, update, and delete records or data.

from django.contrib import admin
from .models import Bmi
class BmiAdmin(admin.ModelAdmin):
list_display = ('user', 'weight', 'height', 'bmi', 'date')
admin.site.register(Bmi, BmiAdmin)

Here, we need to import our model class.

Then, create BmiAdmin class with The field names to be displayed on our website, specifically, in the admin interface, the list are declared in a tuple in the required order, as you see here.

After the that, we register our model and save the file.

Migrate The Bmi App:

So, now time to migrate our model, just in two steps:

$ python manage.py makemigrations bmi$ python manage.py migrate bmi

Also, I would like to create a super user, by typing:

$ python manage.py createsuperuser

Add Header To The Base Template:

In the base.html file, I’ll add a header, so it will be displayed in all our website pages, but we have here only the home page.

We will use Bootstrap’s powerful, and responsive navigation header, the navbar.It Includes support for branding, navigation, and collapse plugin, which will make our navbar mobile friendly.

...
</head>
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light container mb-5">
<button type="button" class="navbar-toggler" data-toggl="collapse" data-target="#MyNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="MyNav">
<ul class="navbar-nav">
<li class="navbar-item">
<a href="{% url 'home' %}" class="nav-link">Home</a>
</li>

</ul>
</div>
</nav>
</header>
<body>
...

The url tag helps us to generate links in the templates. As you see here, we will use it to generate a link to our home page.

Design The Home Page:

Let’s move to the index.html file, it’s time to design our home page. It will look like this:

{% extends 'bmi/base.html' %}{% block content %}<div class="container">
<div class="form-row">
<!-#########################Col1###############-->
<div class="col-md-6 col-sm-12 col-xs-12">
<div class="tab-bmi">
<h3>Bmi Calculator</h3>
<div class="tab">
<button class="tablinks" onclick="openUnit(event, 'metric')">Metric</button>
<button class="tablinks" onclick="openUnit(event, 'imperial')">Imperial</button>
</div>
<div class="tabcontent" id="metric">
<form method="post">
{% csrf_token %}
<div class="col-12">
<label for="">Weight: </label>
<input type="text" name="weight-metric" class="form-control" required placeholder="kg">
</div>
<div class="col-12">
<label for="">Height: </label>
<input type="text" name="height-metric" class="form-control" required placeholder="m">
</div>
{% if request.user.is_authenticated %}
<div class="form-check my-5">
<input class="form-check-input" checked="checked" name="save" value="on" type="checkbox">
<label for="" class="form-check-label">Save It.
<a href="#" title="Why to save it?" data-toggle="popover" data-trigger="focus"
data-content="In case you want to track your weight, you can save it.(login required)">?</a>
</label>
</div>
{% else %}
<div class="form-check my-5">
<input disabled class="form-check-input" checked="checked" name="save" value="on" type="checkbox">
<label for="" class="form-check-label">Save It.
<a href="#" title="Why to save it?" data-toggle="popover" data-trigger="focus"
data-content="In case you want to track your weight, you can save it.(login required)">?</a>
</label>
</div>
{% endif %}
<button type="submit" class="btn btn-primary" action="{% url 'home' %}">Calculate</button> </form>
</div>
<div class="tabcontent" id="imperial">
<form method="post">
{% csrf_token %}
<div class="col-12">
<label for="">Weight: </label>
<input type="text" name="weight-imperial" class="form-control" required placeholder="lbs">
</div>
<div class="col-12">
<label for="">Height: </label>
<div class="input-group">
<input type="text" name="feet" placeholder="feet" class="form-control">
<input type="text" name="inches" placeholder="inches" class="form-control">
</div>
</div>
{% if request.user.is_authenticated %}
<div class="form-check my-5">
<input class="form-check-input" checked="checked" name="save" value="on" type="checkbox">
<label for="" class="form-check-label">Save It.
<a href="#" title="Why to save it?" data-toggle="popover" data-trigger="focus"
data-content="In case you want to track your weight, you can save it.(login required)">?</a>
</label>
</div>
{% else %}
<div class="form-check my-5">
<input disabled class="form-check-input" checked="checked" name="save" value="on" type="checkbox">
<label for="" class="form-check-label">Save It.
<a href="#" title="Why to save it?" data-toggle="popover" data-trigger="focus"
data-content="In case you want to track your weight, you can save it.(login required)">?</a>
</label>
</div>
{% endif %}
<button type="submit" class="btn btn-primary" action="{% url 'home' %}">Calculate</button> </form>
</div>
</div>
<!-#########################Col2###############-->
</div>
</div></div>{% endblock content %}

As you see here, we used the onclick attrinute with the two buttons:

<div class="tab">
<button class="tablinks" onclick="openUnit(event, 'metric')">Metric</button>
<button class="tablinks" onclick="openUnit(event, 'imperial')">Imperial</button>
</div>

We will use a JavaScript function to let users display whether the metric or imperial tab when they click on the button.

It’s a very basic javascipt function, we will name it openUnit with two parameters, one is the event and the other will take the ids of the divs.

/*BMI TAB*/function openUnit(evt, unitName) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
document.getElementById(unitName).style.display = "block";
evt.currentTarget.className += " active";
}

To make it simple for you, in case you find JavaScript complicated. What we are doing here is we’re creating a function to let user’s see the tab content when they click on one of the two buttons, If they don’t click, content won’t be displayed.

And to make sure that our script will work, we must import our javascript file to the base template:

...
<!--custom scripts-->
<script type="text/javascript" src="{% static "bmi/custom.js" %}">
</body>

Also,

In our template, we have <form>…</form> that allow a visitor to enter his weight, height, to use them to calculate the bmi.

The method of the form will be post, which the browser bundles up the form data, encodes it for transmission, sends it to the server, and then receives back its response.

POST request should come always with CSRF protection that offers more control over access, and to do that just type csrf_token.

The rest are simple inputs, but the important thing is the name of the input, because we will use it later to retrieve the data, the user entered via these inputs.

I added a checkbox too, to let the user to save the result in the database, in case he has an account here.

The tooltip,

<label for="" class="form-check-label">Save It.
<a href="#" title="Why to save it?" data-toggle="popover" data-trigger="focus"
data-content="In case you want to track your weight, you can save it.(login required)">?</a>
</label>

Also, I created a tooltip for the save checkbox, tooltip is small pop-up box that appears when the user moves the mouse pointer over the element i’ll add beside the checkbox, you’ll se what I’m talking about.

And Tooltips must be initialized with jQuery: and we should use the tooltip() method.

Which means we will create a javascript function again.

/*static/bmi/custom.js*/$(document).ready(function(){
$('[data-toggle="popover"]').popover();
});

Add Styling To The Home Page

First, let’s create a css file besides the javascript file we just created in the static folder.

$ touch static/bmi/style.css

Then Add some styling:

.tab-bmi{
font-family: Arial;
}
/*style the tab*/.tab-bmi .tab{
overflow:hidden;
border:1px solid #ccc;
background-color:#f1f1f1;
}
/*the buttons*/
.tab-bmi .tab button{
background-color: inherit;
float:left;
border:none;
cursor:pointer;
outline:none;
padding:14px 16px;
transition: 0.3s;
font-size: 17px;
}
/*add hover to the buttons*/
.tab-bmi .tab button:hover{
background-color: #ddd;
}
/*Create an active/current tablink class*/
.tab-bmi .tab button.active{
background-color:#ccc;
}
/*style the tb content*/
.tab-bmi .tabcontent{
display: none;
padding: 6px 12px;
--webkit-animation: fadeEffect 1s;
animation: fadeEffect 1s;
}
@-webkit-keyframes fadeEffect {
from {opacity:0;}
to {opacity: 1;}
}
@keyframes fadeEffect {
from {opacity:0;}
to {opacoty: 1;}
}

And let’s link our css file with the base template file.

<!--bmi style-->
<link rel="stylesheet" href="{% static "bmi/style.css" %}">
</head>

Handle form data in the POST request:

Now, we will Handle form data in the POST request. in other words, we will access post method data in the views.py file.

from django.shortcuts import render
from .models import Bmi
def home(request):
context = {}
if request.method=="POST":
weight_metric = request.POST.get("weight-metric")
weight_imperial = request.POST.get("weight-imperial")
if weight_metric:
weight = float(request.POST.get("weight-metric"))
height = float(request.POST.get("height-metric"))
elif weight_imperial:
weight = float(request.POST.get("weight-imperial"))/2.205
height = (float(request.POST.get("feet"))*30.48 + float(request.POST.get("inches"))*2.54)/100
bmi = (weight/(height**2))
save = request.POST.get("save")
if save == "on":
Bmi.objects.create(user=request.user,weight=weight, height=height, bmi=round(bmi))
if bmi < 16:
state = "Severe Thinness"
elif bmi > 16 and bmi < 17:
state = "Moderate Thinness"
elif bmi > 17 and bmi < 18:
state = "Mild Thinness"
elif bmi > 18 and bmi < 25:
state = "Normal"
elif bmi > 25 and bmi < 30:
state = "Overweight"
elif bmi > 30 and bmi < 35:
state = "Obese Class I"
elif bmi > 35 and bmi < 40:
state = "Obese Class II"
elif bmi > 40:
state = "Obese Class III"
context["bmi"] = round(bmi)
context["state"] = state
return render(request, "bmi/index.html", context)

So we will work only if there is post request, let’s get the metric weight, weight-metric here is the name of the input if you remember, then we do the same thing with the imperial weight.

For the metric units, we will only convert the strings to float, because we got strings from the inputs.

We will do the same things with the imperial units, but we will convert weight to kg by dividing it by 2.205 and we will convert inches and feet to cm.

Also, I added a state for each bmi value, if bmi< 16, state will be severe thinness, between 16 and 17 it’s miderate thinness, mild thinness for bmi between 17 and 18, and it’s normal if it’s between 18 and 25, overweight for values between 25 and 30, obese class I for bmi between 30 and 35, obese class II for bmi over 35 and less than 40, and finally obesse class III for values over 40.

Update index.html

We will add this to the second column of the row:

{% if bmi %}
<div class="col-md-6 col-sm-12 col-xs-12">
<h2>Result</h2>
<p>Your BMI = {{bmi}} kg/m² | {{state}}</p>
<div class="progress">
<div class="progress-bar bg-danger" style="width:25%">
Underweight
</div>
<div class="progress-bar bg-success" style="width:25%">
Normal
</div>
<div class="progress-bar bg-warning" style="width:25%">
Overweight
</div>
<div class="progress-bar bg-danger" style="width:25%">
Obesity
</div>
</div>
</div>
{% endif %}

Django Authentication

To let the users to create an account nd login, we will use the django-allauth, it is an Integrated set of Django applications that gives users the options of authentication, registration, account management as well as social account authentication.

So first thing, is installing the package using:

$ pip install django-allauth

We should make sure that these are existing in our settings.py file.

# Specify the context processors as follows:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
# Already defined Django-related contexts here
# `allauth` needs this from django
'django.template.context_processors.request',
],
},
},
]

Next, add this block of code to the end of the settings.py file:

AUTHENTICATION_BACKENDS = [
# Needed to login by username in Django admin, regardless of `allauth`
'django.contrib.auth.backends.ModelBackend',
# `allauth` specific authentication methods, such as login by e-mail
'allauth.account.auth_backends.AuthenticationBackend',
]

And make sure to have these apps in your settings:

INSTALLED_APPS = [
...
# The following apps are required:
'django.contrib.auth',
'django.contrib.messages',
'django.contrib.sites',
'allauth',
'allauth.account',
'allauth.socialaccount',
...
]

After that, the url pattern of allauth app.

urlpatterns = [
...
path('accounts/', include('allauth.urls')),
...
]

Finlly, migrate the app.

$ python manage.py migrate

That’s it.

If we visit this link, …/accounts/logout. you’ll see the login page, which means everything is well installed.

Now, since users can create accounts nd so on, I’ll add two links to the header, one link to login nd another one to sign up.

<header>
...
<li class="navbar-item">
<a href="{% url 'account_login' %}" class="nav-link">Login</a>
</li>
{% if request.user.is_authenticated %}
<li class="navbar-item">
<a href="{% url 'account_logout' %}" class="nav-link">Logout</a>
</li>
{% else %}
<li class="navbar-item">
<a href="{% url 'account_signup' %}" class="nav-link">SignUp</a>
</li>
{% endif %}
...
</header>

Note: if you get a “connection refused” error, add this line to the settings file to void this error:

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend

The reason of this error is we have not got an e-mail server running on our project y. Therefore, django-allauth app is unable to send verification mails.

Design And Create The Graph:

Now, time to move to Bokeh, Bokeh is a Python library for interactive visualization that targets web browsers for representation.

And that’s what we will do today, we will represent the saved data using some graphs, so the user can track his weight loss easily without reading the bmi value for each date.

Now, let’s install bokeh using:

$ pip install bokeh.

We need to setup some things for Bokeh, i’ll just copy and paste the css file of Bokeh in the base template, same thing with the scripts.

So my base template looks as follow:

{% load static %}<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!--bokeh-->
<link rel="stylesheet" type="text/css" href="http://cdn.pydata.org/bokeh/release/bokeh-2.1.1.min.css"/>
<link rel="stylesheet" type="text/css" href="http://cdn.pydata.org/bokeh/release/bokeh-widgets-2.1.1.min.css"/>
<link rel="stylesheet" type="text/css" href="http://cdn.pydata.org/bokeh/release/bokeh-tables-2.1.1.min.css"/>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<!--bmi style-->
<link rel="stylesheet" href="{% static "bmi/style.css" %}">
<title>Bmi Calculator</title>
</head>
<body>
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light container mb-5">
<button type="button" class="navbar-toggler" data-toggl="collapse" data-target="#MyNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="MyNav">
<ul class="navbar-nav">
<li class="navbar-item">
<a href="{% url 'home' %}" class="nav-link">Home</a>
</li>
<li class="navbar-item">
<a href="{% url 'account_login' %}" class="nav-link">Login</a>
</li>
{% if request.user.is_authenticated %}
<li class="navbar-item">
<a href="{% url 'account_logout' %}" class="nav-link">Logout</a>
</li>
{% else %}
<li class="navbar-item">
<a href="{% url 'account_signup' %}" class="nav-link">SignUp</a>
</li>
{% endif %}
</ul>
</div>
</nav>
</header>
{% block content %} {% endblock content %} <!--Bokeh-->
<script type="text/javascript" src="http://cdn.pydata.org/bokeh/release/bokeh-2.1.1.min.js"></script>
<script type="text/javascript" src="http://cdn.pydata.org/bokeh/release/bokeh-widgets-2.1.1.min.js"></script>
<script type="text/javascript" src="http://cdn.pydata.org/bokeh/release/bokeh-tables-2.1.1.min.js"></script>
<script type="text/javascript" src="http://cdn.pydata.org/bokeh/release/bokeh-api-2.1.1.min.js"></script>
{{script|safe}} <!--custom scripts-->
<script type="text/javascript" src="{% static "bmi/custom.js" %}">
</script>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>
</html>
<script type="text/javascript">
$(document).ready(function(){
$('[data-toggle="popover"]').popover();
});
</script>

One important thing, make sure that the version of the Bokeh in the scripts and css links is the same version in the the package you isntalled using pip.

And in the views file, we neeed to import some modules.

Now let’s design our graph.

#views.py ...if request.user.is_authenticated:
dates = []
bmis = []
num = 1
dates_queryset = Bmi.objects.all().filter(user = request.user)
for qr in dates_queryset:
dates.append(str(qr.date)+"("+str(num)+")")
bmis.append(int(qr.bmi))
num += 1
plot = figure(x_range=dates, plot_height=600, plot_width=600, title="Bmi Statistics",
toolbar_location="right", tools="pan, wheel_zoom, box_zoom, reset, hover, tap, crosshair")
plot.title.text_font_size = "20pt"
plot.xaxis.major_label_text_font_size = "14pt" # add a step renderer
plot.step(dates, bmis, line_width=2)
plot.legend.lable_text_font_size = '14pt'
plot.xaxis.major_label_orientation = pi/4
script, div = components(plot)
context["script"] = script
context["div"] = div
return render(request, "bmi/index.html", context)

Sure, only the athenticated users will see the presentation, that why we added the if condition.

We need two lists for our representation, one that holds the dates, and to get the right data of the authenticated user, we will use the filter function.

We will loop over this queryset to append data to our list. and we used the str() function, because we need them as strings not date time format.

and we will do the same thing with the bmi’s value, but we will convert them to integers.

Now we create the plot, the x_range is the dates list, then we set up the height and the width of the plot and give it a random title.

Also, change the title font size to 20 points, it’s optional.

Do the same thing with the xaxis font size, change it to 14 points.

After that, we add step renderer to our plot, this will give the shape of our graph.

And to render the Bokeh graph to our template, we need two things, the script and the div.

These two things will be inserted into our HTML file, the div , and the script.

The div should be replaced inside our index.html file where we want to render our graph and the script in the scripts section in the base template.

<!--templates/bmi/index.html-->...
{{div|safe}}
</div>
{% endblock content %}

Deploy To Heroku:

I think our project is ready for deployment now.

We will use the free tier in Heroku.

First of all, let’s create an account, it’s an easy step. Just fill in your data and confirm the email and you have a free heroku account.

For security reason, we should keep the secret key in somewhere safe, We can do it this way. We will load it from an environment variable.

$ nano ~/.secret_key

Inside of this file, we will add the secret key:

export KEY='9+o#ha*h!-b08(6!a(8rzwh^deeyy%o_y*%*x_@%-o&&xao#(r'

So we go the settings file, and add:

#settings.py...# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get("KEY")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
...

Next, we will create a Procfile, that will handle our server stuff, which is gunicorn for our case.

And make sure to add the right name of your project folder, the folder that contains the wsgi file.

$ nano Procfileweb: gunicorn project.wsgi

And for that, we need to install gunicorn, by:

$ pip install gunicorn

To make sure that there is connection between our Django app and Heroku, we will use the django_heroku.

#settings.py...import os
import django_heroku
...django_heroku.settings(locals())

Then install the django-heroku.

$ pip install django-heroku

If we type pip freeze, you’ll see all the dependencies that we installed in our project.

asgiref==3.2.10
bokeh==2.1.1
certifi==2020.6.20
chardet==3.0.4
defusedxml==0.6.0
dj-database-url==0.5.0
Django==3.0.8
django-allauth==0.42.0
django-heroku==0.3.1
gunicorn==20.0.4
idna==2.10
Jinja2==2.11.2
MarkupSafe==1.1.1
numpy==1.19.1
oauthlib==3.1.0
packaging==20.4
Pillow==7.2.0
psycopg2==2.8.5
pyparsing==2.4.7
python-dateutil==2.8.1
python3-openid==3.2.0
pytz==2020.1
PyYAML==5.3.1
requests==2.24.0
requests-oauthlib==1.3.0
six==1.15.0
sqlparse==0.3.1
tornado==6.0.4
typing-extensions==3.7.4.2
urllib3==1.25.10
whitenoise==5.2.0

We need these packages for Heroku too, so we will save it in a file we will name it requirements.txt.

$ pip freeze > requirements.txt

Next step, is installing the Heroku comand line interface on our machine, via:

$ sudo snap install heroku --classic

To login to our heroku account, we can type:

$ heroku login

And wait for the browser to load or login via the terminal by adding -i to the this command line.

$ heroku login -i

If you don’t have git installed in your system, install it. If you’re using Linux, just type this command in your terminal.

$ sudo apt-get install git

So let’s initialize a .git folder for our project:

$ git init

Let’s add all the files into this folder.

$ git add .

Create our first commit.

$ git commit -m "initial commit"

Create the app on heroku.

$ heroku create bmi-calculator-app

Now we need the secret key to add it to our heroku app.

$ heroku config:set export KEY='9+o#ha*h!-b08(6!a(8rzwh^deeyy%o_y*%*x_@%-o&&xao#(r'

And will disable the colect static:

$ heroku config:set DISABLE_COLLECTSTATIC=1

And finally push our project and wait. It will take some time:

$ git push heroku master

Everything is done now. Let’s login to our website via this link.

bmi-calculator-app.herokuapp.com

The last thing we need to do is migrate the app and create a super user.

It’s similar to the terminal command, just here add heroku run before the command.

$ heroku run python manage.py makemigrations
$ heroku run python manage.py migrate
$ heroku run python manage.py createsuperuser

Watch the tutorial on YouTube: https://www.youtube.com/watch?v=bAL3gzEf-dQ

Download The Code: https://github.com/SelmiAbderrahim/Bmi-Calculator-Django-Bokeh

--

--

Selmi Abderrahim

I’m a computer science student. I do freelancing in my free time. I am a big fan of affiliate marketing, it helped me a lot to monetize my daily stuff.