Django is an open-source, high-level Python web framework that abstracts away much of the difficulty of web development and allows you to focus on writing your application. Django includes support for common web development tasks such as user authentication, templates, views, models, and security.
IBM starter kits are a great way to get you started on IBM Cloud as they contain all the necessary files you need to start coding and deploy your applications.
Learning objectives
In this tutorial, you will learn how to:
- Create a Django application running on the IBM Cloud.
- Create a PostgreSQL database on the IBM Cloud.
- Connect your PostgreSQL database to you Django application.
- Deploy your application using Cloud Foundry or IBM Cloud Kubernetes Service.
- Create a simple application to create blogs.
- Create models, views, forms, URLs, and HTML/CSS files.
- Deploy the blog application on the IBM Cloud using Git (continuous delivery) or Cloud Foundry through the terminal.
Prerequisites
To complete this tutorial, you need:
- Access to the IBM Cloud Databases for PostgreSQL service
- A provisioned Kubernetes cluster on IBM Cloud
- Python
- Pip
- PgAdmin
- Git
Estimated time
You can complete this tutorial in about an hour.
Steps
- Step 1. Create a PostgreSQL database
- Step 2. Create a Python Django starter application
- Step 3. Enable continuous delivery
- Step 3a. Deploy through Cloud Foundry
- Step 3b. Deploy through a Kubernetes cluster
- Step 4. Connect services to your application
- Step 5. Create your blog application
- Step 6. Deploy your blog application
Step 1. Create a PostgreSQL database
Start with provisioning the PostgreSQL database, since it takes about 5 to 10 minutes.
- Log into your IBM Cloud account.
- Open the Databases for PostgreSQL service.
Select your preferred region from the Select a region list.
Type in a unique name for your database service in the Service name field.
- Select a resource group from the list.
- You can let the remaining fields remain as the default values.
Click Create to start provisioning your database service.
Step 2. Create a Python Django starter application
- Go to the Python Django App starter kit
- Click Get started.
- Click on the Create tab.
- Enter a unique name for your application within the App name field. Note that the name of your application will be part of the application URL.
- Choose a resource group.
Click the Create button. This will create your application on IBM Cloud and then allow you to deploy it.
Once the application is created, you will be redirected to the App details page for your application.
Click the Deploy your app button to enable the continuous delivery pipeline for your application.
Step 3. Enable continuous delivery
You can deploy your application using any of the three deployment targets displayed on the screen: IBM Cloud Kubernetes Service, Red Hat OpenShift, or Cloud Foundry. This tutorial covers deployment through Cloud Foundry and the IBM Cloud Kubernetes Service.
Step 3a. Deploy through Cloud Foundry
This step shows how to set up the continuous delivery pipeline that will deploy your application into Cloud Foundry on IBM Cloud.
- In the Deployment target section, select Cloud Foundry.
- Click the New button to create an IBM Cloud API key.
- Select the default value of the API key and click Create. The API key allows the deployment to access your resources.
- Select the preferred Region, Organization, and Space for your deployment.
- Ensure the memory allocation per instance value is set to a minimum of 128 MB.
Click Next to continue.
Configure the DevOps toolchain by selecting the region it should be created in. It is preferable to choose the same region as the one you chose earlier in this step (within task 4).
Click Create. This will return you to the application overview page and start the deployment process.
The Deployment Automation section now contains the details of your delivery pipeline. The Status field of the pipeline will eventually change from
In progress
toSuccess
after it is successfully deployed. Deployment takes a few minutes to complete.Click Status to view the progress of the pipeline deployment.
Within the Delivery Pipeline status page, click View logs and history to view the progress.
Back on the App details page, click on the App URL (within the Details section) to view the starter kit code that deployed.
Step 3b. Deploy through a Kubernetes cluster
This step shows how to set up the continuous delivery pipeline that will deploy your application using the IBM Cloud Kubernetes Service.
- In the Deployment target section, select Kubernetes Service.
- Click the New button to create an IBM Cloud API key.
- Select the default value of the API key and click Create. The API key allows the deployment to access your resources.
- Select the appropriate Cluster registry region, Cluster registry namespace, Cluster region, Cluster resource group, and Cluster namespace.
- In the Cluster name field, select the cluster you identified for the Prerequisites section.
Click Next to continue.
Configure the DevOps toolchain by selecting the region it should be created in. It is preferable to choose the same region as the one you chose earlier in this step (within task 4).
Click Create. This will return you to the application overview page and start the deployment process.
The Deployment Automation section now contains the details of your delivery pipeline. The Status field of the pipeline will eventually change from
In progress
toSuccess
after it is successfully deployed. Deployment takes about 5 minutes to complete. (There is an additional stage in comparison to deployment through Cloud Foundry.)Click Status to view the progress of the pipeline deployment.
Within the Delivery Pipeline status page, click View logs and history to view the progress.
On the App details page, within the Details section, click on the App URL to view the starter kit code that deployed.
Step 4. Connect services to your application
- You can now connect your PostgreSQL database to your application.
On the App details page, click Connect existing services.
Select the database service you created.
Click Next to finish the process. Your service will be connected to the application in a few minutes.
Step 5. Create your blog application
The deployed application also creates a GitLab project.
- Back on the App details page, click on the Source link to open the Gitlab project in a new tab.
Click Clone to clone the project.
Go to the directory where you cloned your project and create a virtual environment (also known as
vitualenv
). It is preferable to run your project in a virtual environment as that creates an isolation for your Python/Django project and allows you to run different projects on different versions without affecting the others.#this will install the necessary files to create a virtualenv pip install virtualenv #name your virtual environment, I am calling it venv virtualenv venv #activate your virtualenv source venv/bin/activate #Once your work on this project is over, deactivate your virtualenv deactivate
Open the code in your preferred integrated development environment (IDE). I am using PyCharm. You will see the
venv
and all the other files created for you.In the
pip
file, add the following code:# To install Django and psycopg2 for the postgresql Django = "==2.2.8" psycopg2-binary = "==2.8.5"
Note: We are using
psycopg2-binary
because it does not require configuration of psycopg2.Save the file. Open your terminal and run the following commands:
# this creates a pipfile.lock with all the dependeicies installed. pipenv install
Once the
pipfile.lock
is successfully created, go back to your IDE.In the
.gitignore
file, add yourvirtualenv
files..env .venv env/ venv/ ENV/ env.bak/ venv.bak/
You will notice that you have 2 apps already created.
app
is where you put all your source code andpythondjangoapp
is for the settings and base URLs.Go to
pythondjangoapp > settings > base.py
. After theWSGI_APPLICATION
, you can add your database credentials to connect your application to the database. Put in the name, user, password, host, and port with the details from the service you created on the cloud.Save the file and make migrations using the following commands. This will create the default tables that come with PostgreSQL.
python manage.py makemigrations python manage.py migrate
To locally run the application, you can use the following command:
python manage.py runserver
In the app folder, you now create your database table in the
models.py
file as follows:# -*- coding: utf-8 -*- from django.db import models from django.contrib.auth.models import User from datetime import datetime, date class BlogPost(models.Model): author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='author') title = models.CharField(max_length=200, unique=True) post = models.TextField() created_on = models.DateTimeField(auto_now_add=True) updated_on = models.DateTimeField(auto_now=True) status = models.BooleanField(default=False)
Create a
forms.py
file under the app folder and add the following code:from django import forms from .models import BlogPost class BlogForm(forms.ModelForm): title = forms.CharField(required=True, max_length=200, widget=forms.TextInput( attrs={'placeholder': 'Enter the title', 'class': 'form-control'} )) post = forms.CharField(required=True, max_length=5000, widget=forms.Textarea( attrs={'placeholder':'Write your post here', 'rows': 10,'cols': 75, 'style': 'border: 1px solid; border-radius: 8px;'}) ) class Meta: model = BlogPost fields = ('title', 'post', 'status') exclude = ('created_on', 'updated_on', 'author')
In the
views.py
file, add the following code:from __future__ import unicode_literals from django.http import JsonResponse from django.views.generic import TemplateView from django.http import Http404 from django.shortcuts import render, redirect, get_object_or_404 from .models import BlogPost from .forms import BlogForm from django.utils import timezone class Home(TemplateView): template_name = 'index.html' def get(self, request, *args): published = BlogPost.objects.filter(status=True) drafts = BlogPost.objects.filter(status=False) data = BlogPost.objects.all() for d in data: print("post:", d.title) args = {'data': data, 'pub': published, 'drafts': drafts} return render(request, self.template_name, args) class EditPost(TemplateView): template_name = 'edit_post.html' def get(self, request, *args, **kwargs): id = int(kwargs['id']) post = get_object_or_404(BlogPost, id=id) print("post:", post) form = BlogForm(instance=post) data = BlogPost.objects.all() for d in data: print("post:", d.title) args = {'id': id, 'form': form, 'post': post, 'data': data} return render(request, self.template_name, args) def post(self, request, **kwargs): post = get_object_or_404(BlogPost, id=int(kwargs['id'])) if request.method == "POST": form = BlogForm(request.POST, instance=post) if request.POST.get("submitbtn"): if form.is_valid(): print("validating form") post = form.save(commit=False) post.author = request.user post.updated_on = timezone.now() post.status = True post.save() print("form saved") return redirect('home') else: print("invalid form") print("errors:", form.errors) class CreatePost(TemplateView): template_name = 'new_post.html' def get(self, request, *args): form = BlogForm() posts = BlogPost.objects.filter(status=False, author_id=request.user.id) print("user:", request.user) print("user:", request.user.id) args = {'form': form, 'posts': posts} return render(request, self.template_name, args) def post(self, request): print("inside post") if request.method == "POST": form = BlogForm(request.POST) if request.POST.get("submitbtn"): print("Submit clicked") if form.is_valid(): print("validating form") post = form.save(commit=False) post.author = request.user post.created_on = timezone.now() post.updated_on = timezone.now() post.status = True post.save() print("form saved") return redirect('home') else: print("invalid form") print("errors:", form.errors) elif request.POST.get("savebtn"): print("Submit clicked") if form.is_valid(): print("validating form") post = form.save(commit=False) post.author = request.user post.created_on = timezone.now() post.updated_on = timezone.now() post.status = False post.save() print("form saved") return redirect('home') else: print("invalid form") print("errors:", form.errors) def health(request): state = {"status": "UP"} return JsonResponse(state) def handler404(request): return render(request, '404.html', status=404) def handler500(request): return render(request, '500.html', status=500)
In
urls.py
file, add the following code:from django.urls import path from . import views from django.contrib import admin from .views import CreatePost, Home, EditPost urlpatterns = [ path('home/', Home.as_view(), name='home'), path('', Home.as_view(), name='home'), path('new_post/', CreatePost.as_view(), name='new_post'), path('edit_post/<int:id>/', EditPost.as_view(), name='edit_post'), path('admin/', admin.site.urls), path('health', views.health, name='health'), path('404', views.handler404, name='404'), path('500', views.handler500, name='500'), ]
Under the
templates
folder, create two additional HTML files. Name themnew_post.html
andedit_post.html
.Replace the
index.html
file with the following code:<!DOCTYPE html> {% load static %} <html lang="en"> <head> <meta charset="UTF-8"> <title>Home</title> <script src='https://kit.fontawesome.com/a076d05399.js'></script> <link href="{% static 'css/default.css' %}" rel="stylesheet" type="text/css" media="all"/> </head> <body> <div class="header"> <h1>Blogs</h1> </div> <div align="center"> <a href="{% url 'new_post' %}"> Create a new post <i class="fa fa-plus-circle" aria-hidden="true"></i></a> </div> <div class="content"> <H1>List of posts</H1> <br><br> {% for p in pub %} <a href="{% url 'edit_post' p.id %}"><h3> <i class="fa fa-pen"></i> {{ p.title }}</h3> </a> <p class="card-text text-muted h6">{{ p.author }} | {{ p.updated_on}} </p> <p class="card-text">{{p.post|slice:":200" }}</p> <br> {% endfor %} </div> <div class="content"> <H1>List of Drafts</H1> <br><br> {% for d in drafts %} <a href="{% url 'edit_post' d.id %}"><h3> <i class="fa fa-pen"></i> {{ d.title }}</h3> </a> <p class="card-text text-muted h6">{{ d.author }} | {{ d.updated_on}} </p> <p class="card-text">{{d.post|slice:":200" }}</p> <br> {% endfor %} </div> </body> </html>
In the
new_post.html
file, add the following code:<!DOCTYPE html> {% load static %} <html lang="en" class="no-js"> <head> {% block title %} <title>Blog </title> {% endblock title %} <meta content="width=device-width, initial-scale=1.0" name="viewport"> <!-- Place your favicon.ico and apple-touch-icon.png in the template root directory --> <link href="favicon.ico" rel="shortcut icon"> <link rel="shortcut icon" href="" type="image/ico"> <link href="{% static 'css/default.css' %}" rel="stylesheet" type="text/css" media="all"/> <style> input[type=text], select { width: 40%; padding: 12px 20px; margin: 8px 0; display: inline-block; border: 1px solid #ccc; border-radius: 8px; box-sizing: border-box; } </style> </head> <body> <div class="header"> <h1>Blogs</h1> </div> <div align="center"> <H2>Create new post</H2> <br><br> </div> <div align="center"> <form method="POST"> {% csrf_token %} <div class="u-form-group"> <label>Title: </label> {{ form.title }} <br><br> <label>Post:</label>{{ form.post }}<br/> <button class="button button2" id="submitbtn" type="submit" name="submitbtn" value="submitbtn">Create new post</button> <button class="button button2" id="savebtn" type="submit" name="savebtn" value="savebtn">Save as Draft</button> </div> </form> </div> </body> </html>
In the
edit_post.html
file, add the following code:<!DOCTYPE html> {% load static %} <html lang="en" class="no-js"> <head> {% block title %} <title>Blog </title> {% endblock title %} <meta content="width=device-width, initial-scale=1.0" name="viewport"> <!-- Place your favicon.ico and apple-touch-icon.png in the template root directory --> <link href="favicon.ico" rel="shortcut icon"> <link rel="shortcut icon" href="" type="image/ico"> <link href="{% static 'css/default.css' %}" rel="stylesheet" type="text/css" media="all"/> <style> input[type=text], select { width: 40%; padding: 12px 20px; margin: 8px 0; display: inline-block; border: 1px solid #ccc; border-radius: 8px; box-sizing: border-box; } </style> </head> <body> <div class="header"> <h1>Blogs</h1> </div> <div align="center"> <H2>Edit Blog</H2> <br><br> </div> <div align="center"> <form method="POST"> {% csrf_token %} <div> <label>Title: </label> {{ form.title }} <br><br> <label>Post:</label>{{ form.post }}<br/> <button class="button button2" id="submitbtn" type="submit" name="submitbtn" value="submitbtn">Submit</button> </div> </form> </div> </body> </html>
Go to
Static folder> css folder> default.css
and add the following CSS code. You will also have to add this file to the main directory,"staticfiles" > css > default.css
./* Style the body */ body { font-family: Arial; margin: 0; } /* Header/Logo Title */ .header { text-align: center; background: #00cdcd; color: white; font-size: 30px; } /* Page Content */ .content {padding:20px;} .button { background-color: #00cdcd; /* Green */ border-radius: 8px; color: black; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 18px; font-weight: bold; margin: 4px 2px; cursor: pointer; -webkit-transition-duration: 0.4s; /* Safari */ transition-duration: 0.4s; } .button2:hover { box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24),0 17px 50px 0 rgba(0,0,0,0.19); } #new-container { margin: 0 auto; width: 70%; } .link-section { margin: auto; padding: 1em; width: 80%; } .ibm-cloud { display: block; margin-left: auto; margin-right: auto; width: 50%; } .ibm-cloud img { display:block; margin:auto; } .link-item a { color: #6e98df; text-decoration: underline; font-size: 1em; } .left-link-items { float: left; padding: 1em; margin-left: 20%; } .right-link-items { float: right; padding: 1em; margin-right: 20%; } .right-arrow{ padding-right: 0.5em; } .title { color: black; text-align: center; } .title-404 { color: black; text-align: center; font-size: 4em; } .subtitle { color: black; text-align: center; } .ibm-404 { margin: auto; width: 35%; } .ibm-404 a { color: #6e98df; text-decoration: underline; }
Save the files. You must make migrations again, since you added a model table in the
models.py
file.python manage.py makemigrations python manage.py migrate
Create a super user for the Django admin.
python manage.py createsuperuser
Step 6. Deploy your blog application
You are now ready to deploy your application to IBM Cloud. Since you cloned the project from GitLab and created your delivery pipeline, the deployment process now becomes very simple.
On your terminal, commit your files and push them to GitLab.
#To find the files that you have made changes to git status #To add the files to the project git add * #Commit the files that you made changes to git commit -m “enter your message here” #push your code git push
Back on the App details page within IBM Cloud, in the Deployment Automation section, you will notice that the Status field changes to
In progress
. Your application will deploy within the next few minutes.Alternatively, if you followed Step 3a. Deploy through Cloud Foundry, you can also use your terminal to deploy the application with Cloud Foundry with the following commands:
#Login to IBM Cloud ibmcloud login #Deploy your application to Cloud Foundry ibmcloud dev deploy -t buildpack
Similarly, if you followed Step 3b. Deploy through a Kubernetes cluster, you can use the same commands in your terminal to deploy the application using Kubernetes. These commands automatically start the deployment process on the cloud since you created an automated delivery pipeline earlier in Step 3b.
Your application is now up and running on the URL (the one located in the App URL field on the App details page within IBM Cloud). Visit
<Your URL>/admin/
and log in with your superuser credentials. This step is necessary since you do not have a login page. By logging in through the admin account, you are able to use the username as the author name in this application.The Home page of the application opens, where you can view all of the created posts and a list of draft posts that are yet to be published.
Click Create a new post.
- Enter a title in the Title field and add content within the Post field.
You can either click Create new post to publish your content or select Save as Draft.
Summary
Congratulations! You successfully created a blog application connected to a relational database, PostgreSQL, that is hosted on the IBM Cloud. You also learned how to edit the application code, perform create, retrieve, update, and delete operations, and automatically deploy changes.
To continue learning how to deploy applications to a public cloud, visit the following resources:
- Blog post: Deploy IBM Cloud starter kits on Red Hat OpenShift
- Code pattern: A Python Flask audio search application
- Tutorial: Create a Node-RED starter application