Having set up a Django project, defined the models and customized the admin interface in the first part of this series, we will continue designing the blog application by creating the necessary views, URLs and templates.
I strongly advised that you first go through the previous series before attempting this part, if you have not already done so, to understand what we are doing.
The Views
Since I have already given you a glimpse of how to implement functional-based views in the portfolio project we designed previously, I will use class-based views for this blog application. Add the following to the views.py
file in your blog folder.
from django.views.generic import ListView, DetailView from .models import Post # Create your views here. class BlogList(ListView): queryset = Post.objects.filter(status=1).order_by('-created_on') template_name = 'index.html' class BlogDetail(DetailView): model = Post template_name = 'blog_detail.html'
The generic class-based ListView
and DetailView
from Django are commonly used in web development to list and show the full content of our database model respectively. All we need to do is to specify the template.
For the BlogList
class, we apply a filter to show only recently published posts, and for the BlogDetail
class, we define which model we are using.
No doubt, using class-based views saves us a lot of code.
The URLs
It’s time to map the URLs to the views. Here we will define which pages to build based on browser requests. We first start with the project-level URLs where we will use the include()
method to point to the app-level URLs. Open the urls.py
file in the project folder.
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('', include('blog.urls')), ]
Go to the blog
folder, and create a urls.py
file for the app-level URLs.
from .views import BlogList, BlogDetail from django.urls import path urlpatterns = [ path('', BlogList.as_view(), name='home'), path('<slug:slug>/', BlogDetail.as_view(), name='blog_detail'), ]
At the end of the view name, we add as_view()
. This is always the case whenever class-based views are created.
Recall while creating the model class, we specified a slug
field; and in our admin.py
file, we defined that the slug field be automatically generated based on the title field. So, we use angle bracket <>
to get the slug which will return the page that matches the slug.
Passing the name parameter may appear optional but it’s considered best practice as we will see in the template files.
The Templates
Run the following command on your terminal to create a template folder for our markup files and a static folder for static files.
mkdir templates static/css -p
Then, go to the settings.py
file to register these folders. The templates will be registered under the TEMPLATES section.
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], #add these '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', ], }, }, ]
For the static folder, scroll down to where you will see STATIC_URL
, under it, write this:
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
Inside the templates folder, create a base.html file which will be inherited by other markup files.
{% load static %} <!DOCTYPE html> <html> <head> <title>Django Blog</title> <link href="https://fonts.googleapis.com/css?family=Roboto:400,700" rel="stylesheet"> <meta name="google" content="notranslate" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263X> crossorigin="anonymous" /> <link rel="stylesheet" href="{% static 'css/styles.css' %}"> </head> <body> <header class="masthead"> <div class="overlay"></div> <div class="container"> <div class="row"> <div class=" col-md-8 col-md-10 mx-auto"> <div class="site-heading"> <h3 class=" site-heading my-4 mt-3 text-white"> Welcome to my Health Blog Created with Django </h3> <a class="text-white" href="{% url 'home' %}">Home</a> </div> </div> </div> </div> </header> {% block content %} <!-- Content Goes here --> {% endblock content %} <!-- Footer --> <footer class="py-3 bg-grey"> <p class="m-0 text-dark text-center ">Copyright © Django Blog</p> </footer> </body> </html>
On the top, we load the static files. Then, we add a link to the static files and home page using Django templating engine (the {…}
block) as seen in the a and link tags.
Notice how using the name
parameter we defined in the app-level URLs saves us a lot of code, and makes it look neat and readable. Bootstrap is used to style the page.
Inside the {% block content %}
and {% endblock content %}
code block is where scripts from other markup files will be placed. For this to work, the code block also has to appear in those markup files.
Next, create and add these to the index.html
file:
{% extends "base.html" %} {% block content %} <div class="container"> <div class="row"> <!-- Blog Entries Column --> <div class="col-md-8 mt-3 left"> {% for post in post_list %} <div class="card mb-4"> <div class="card-body"> <h2 class="card-title">{{ post.title }}</h2> <p class="card-text text-muted h6">{{ post.author }} | {{ post.created_on}} </p> {% for category in post.categories.all %} <p class="card-text">Category: {{ category.name }}</p> {% endfor %} <a href="{% url 'blog_detail' post.slug %}" class="btn btn-primary">Read More →</a> </div> </div> {% endfor %} </div> {% block sidebar %} {% include 'sidebar.html' %} {% endblock sidebar %} </div> </div> {% endblock %}
Without placing the aforementioned code block, nothing here will be displayed. The post_list
object is returned by ListView
and is now used in our templates.
All the blog articles you stored in the database through the admin interface in the first part of this series are now displayed.

Next, create and add these to blog_detail.html
.
{% extends 'base.html' %} {% block content %} <div class="container"> <div class="row"> <div class="col-md-8 card mb-4 mt-3 left top"> <div class="card-body"> <h1>{% block title %} {{ object.title }} {% endblock title %}</h1> <p class=" text-muted">{{ post.author }} | {{ post.created_on }}</p> {% for category in post.categories.all %} <p class="text-muted">{{ category.name }}</p> {% endfor %} <p class="card-text ">{{ object.body | safe }}</p> </div> </div> {% block sidebar %} {% include 'sidebar.html' %} {% endblock sidebar %} </div> </div> {% endblock content %}
As we can see above, we can choose to use post, the lowercase name of our model or object – a context object, all returned by DetailView
. By using the safe
keyword, we are telling Django that the content is safe and needs no further processing.

Do you notice the statement rendered as a sidebar?
Django templating engine plus the include
keyword was used to display such. So, let’s create the sidebar.html
file.
{% block sidebar %} <!-- Sidebar Widgets Column --> <div class="col-md-4 float-right "> <div class="card my-4"> <h5 class="card-header">Hello Friend,</h5> <div class="card-body"> <p class="card-text">Ensure you read these articles to take good care of your health because health is wealth.</p> </div> </div> </div> {% endblock sidebar %}
Again, without the content wrapped in the code block, nothing will be displayed. Finally, the styles.css
that you’ll place inside the css
folder.
body { font-family: "Roboto", sans-serif; font-size: 17px; background-color: green; } .shadow { box-shadow: 0 4px 2px -2px rgba(0, 0, 0, 0.1); } .btn-danger { color: #fff; background-color: #f00000; border-color: #dc281e; } .masthead { background: #3398E1; height: auto; padding-bottom: 15px; box-shadow: 0 16px 48px #E3E7EB; padding-top: 10px;
You are free to style the web page according to your taste.
Conclusion
So far, in this second part of the series, we have our app running on the local server. This was made possible through the configurations we set in this tutorial.
From defining the views to mapping them to the URLs to displaying them in the template files, our blog app has taken shape. Check my GitHub page for the full code.
Learning web development with Python Django is not an easy one. I guess you are already sweating on this project. However, it is not over until it is over. We have to add comment sections, images, pagination and other things in this blog app. We also want the CRUD operation to be performed seamlessly.
In the third part of this project article, we will see how we can go about doing them starting with adding comments in our blog application. Stay Tuned!
💡 Third Part: How I Created a Blog Application Using Django – Part 3

Jonathan Okah is a professional freelance Python developer whose primary goal is to solve a perceived problem with the use of technology, thus making life easy for you.
He has basic knowledge of Python, C, JavaScript, HTML, CSS and R, but has taken his knowledge of Python to the next level at Upwork, where he performs services for clients.
Jonathan has a flair for writing and has written several blog articles for clients. He is constantly learning to improve his skills. Currently, he is studying software engineering, hoping to become a full-stack web developer.
In his leisure time, Jonathan enjoys playing games and watching the English Premier League. You can contact him for your freelance projects at Upwork.