Introduction
Django is a high-level Python web development framework that prioritizes efficiency, security, and scalability, it is no surprise that Django in recent times has emerged among the 10 most popular web development frameworks, it follows a "batteries included" approach, providing a comprehensive set of tools, libraries, and features out of the box for rapid development.
Django is known for its robustness, excellent documentation, and built-in features like Object-relational mapping (ORM), authentication, and an admin interface. Django's architecture, coupled with features like caching, database connection pooling, and middleware support, enables the development of robust and performant web applications.
Django’s robustness, versatility, and massive community support amongst others, make it an excellent choice for building a blog regardless of the size of the blog to be built, Django is ideally suited as it allows you to focus on creating engaging content and crafting unique blog experiences while leveraging a powerful and mature web development framework.
This article provides a comprehensive guide on harnessing Django's capabilities to construct a sleek and functional blog application.
Prerequisites
Prior to diving in, it's essential to ensure you are familiar with and have the following technologies installed on your local machine to effectively follow this guide:
- Basic HTML/CSS Knowledge
- Mid-level proficiency in Python & Django web development framework
- Have Python v3.10+ installed
- Have Django v4.0+ installed
- Have Virtualenv installed
About our Blog Application
We are building a simple yet functional web application for our sample blog application that allows us to create and publish blog posts.
It consists of a homepage that aggregates all the blog posts on one page in descending order, each post is represented by a thumbnail image, a title, and a brief headline of the post's content.
Clicking on a post from the homepage redirects to a single blog “detail page” which contains details from just one blog post, it includes the full title, featured image, and the full body text.
The introductory blog design focuses on simplicity and readability, allowing the content to take center stage. It provides a clean and intuitive user interface, making it easy for visitors to browse through posts, and read full articles.
Step 1: Setting up the Django project
Let’s quickly start by creating a virtual environment for our blog application using the virtualenv command.
virtualenv blogvenv
This command creates a virtual environment, to enable our application to have an isolated and clean environment, it makes managing packages and dependencies easier and reduces the time and effort spent on debugging.
Overall, creating virtual environments for individual projects/applications promotes good development practices and improves project organization, making it easier to maintain and collaborate on your project.
blogvenv\scripts\activate
Now that we’re in our new virtual environment, let's install django and pillow (package for handling and manipulating images).
Run the code below with django and pillow separated by a space to install both applications using one command.
pip install django pillow
We can now create our blog project using the “Django-admin” command.
django-admin startproject blog
We can now create our application within the new “blog” project
python manage.py startapp blogapp
Now add the blogapp into your INSTALLED_APPS list in the Settings.py;
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blogapp'
]
Step 2: Create the blog post model, and register it on the admin dashboard
Now we set up the database tables to handle different parts of our blog posts, models.py;
from django.db import models
from django.urls import reverse
class Post(models.Model):
title = models.CharField(max_length=128)
body = models.TextField()
headline = models.TextField()
image = models.ImageField(default = "default.jpg", upload_to = "Posts-Images")
slug = models.SlugField(null=False, unique=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('detail', kwargs={'slug': self.slug})
We can now register the Post model on the Django admin dashboard and define how we want our posts to look too in the admin.py file;
from django.contrib import admin
from blogapp.models import Post
class BlogAdmin(admin.ModelAdmin):
list_display = ['title', 'body', 'headline', 'image']
prepopulated_fields = {'slug': ('title',)}
admin.site.register(Post, BlogAdmin)
Step 3: Configure the template Engine, Static and Media Files Serving
To configure the template engine, create a new directory named “templates” in the blog directory;
mkdir templates
Now, link the "templates" directory, which will be the location for our HTML templates, to the Django template engine within the Settings.py configuration file;
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR/ 'templates'],
'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 files serving, we create a directory named “static” in the blog directory
mkdir static
Now in our settings.py;
STATIC_URL = 'static/'
STATIC_ROOT = 'BASE_DIR/assets'
STATICFILES_DIRS = [
BASE_DIR/'static'
]
Now for the media file storage of our blog, we will add a MEDIA_URL and MEDIA_ROOT in the Settings.py;
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR/'media'
Step 4: Create templates to be used
For our blogapp, we need 2 templates, the first for the index.html page aggregating all our blog posts and the detail.html page, the display for a single post.
To ease the work we will first create a base.html parent template so that we can extend it from the index.html and detail.html to avoid repetition and to properly reuse our code;
base.html;
{% load static %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Blog App</title>
<link
rel="stylesheet"
type="text/css"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65"
crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="../static/style.css">
<link rel="stylesheet" type="text/css" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="{% url 'home' %}">BlogApp</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Community</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Contribute</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">About</a>
</li>
</ul>
<form class="d-flex">
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success" type="submit">Search</button>
</form>
</div>
</div>
</nav>
{% block content %}
{% endblock content %}
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js">
</body>
</html>
Index.html;
{% extends "base.html" %}
{% load static %}
{% block content %}
{% for post in posts %}
<div class="container mt-4 posts">
<div class="card p-3">
<div class="row">
<div class="col-md-4">
<div class="position-relative snipimage">
<a href="{{post.get_absolute_url}}"><img src="{{post.image.url}}" class="rounded img-fluid w-100 img-responsive"></a>
</div>
</div>
<div class="col-md-8">
<div class="mt-2">
<div class="d-flex justify-content-between align-items-center">
<a href="{{post.get_absolute_url}}"><h5 class="mb-1">{{post.title}}</h5></a>
<span><i class="fa fa-heart text-danger"></i> </span>
</div>
<div class="d-flex justify-content-md-start justify-content-between views-content mt-2">
<div class="d-flex flex-row align-items-center">
<i class="fa fa-eye"></i>
<span class="ms-1 views">570</span>
</div>
<div class="d-flex flex-row align-items-center ms-2">
<i class="fa fa-heart"></i>
<span class="ms-1 views">4565</span>
</div>
</div>
<div class="d-flex flex-row mt-3">
<img src="{% static 'user.jpg' %}" width="50" class="rounded-circle">
<div class="ms-2 d-flex flex-column">
<div class="d-flex flex-row align-items-center">
<h6>Clarkson Pwaveino</h6>
<span class="dots"></span>
</div>
<span class="days-ago">2 days ago</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
{% endblock content %}
detail.html;
{% extends "base.html" %}
{% block content %}
<div class="container">
<div class="jumbotron">
<h1 class="display-4">{{post.title}}</h1>
<img src="{{post.image.url}}" class="img-fluid" alt="...">
<p class="lead">{{post.headline}}</p>
<hr class="my-4">
<p>{{post.body}}</p>
<p class="lead">
<a class="jumbo btn btn-outline-success" href="{% url 'home' %}" role="button">Go back</a>
</p>
</div>
</div>
{% endblock content %}
To add a little aesthetics now, let’s create a style.css in the static directory and the following code;
body{
background-color:#1e1c2a;
}
.posts{
margin-top: 150px;
}
.card{
border:none;
background-color:#252836;
color:#fff;
border-radius:12px;
}
.user-timing{
right:9px;
bottom:9px;
color:#fff;
}
.views-content{
color:#606271;
}
.views{
font-size:12px;
}
a{
text-decoration: none;
color: #fff;
}
a:hover{
text-decoration: none;
color: #999;
transition: .5s;
}
.dots{
display:flex;
height:10px;
width:10px;
background-color:green;
border-radius:50%;
margin-left:5px;
margin-bottom:6px;
}
.days-ago{
margin-top: -10px;
color: #606271;
}
.snipimage img{
height: 200px;
}
.jumbotron{
margin-top: 120px;
color: #fff;
}
.jumbo{
margin-top: 45px;
}
.img-fluid{
max-width: 70%;
}
Step 5: Create necessary views and URL Routes
Let’s go ahead and create the views to take a look at our blog properly;
Views.py;
from django.shortcuts import render, get_object_or_404
from .models import Post
def index(request):
posts = Post.objects.all()
context = {'posts': posts}
return render(request, "index.html", context)
def detail(request, slug):
post = get_object_or_404(Post, slug=slug)
context = {'post': post}
return render(request, "detail.html", context)
Now we will create a new urls.py file to create URL routes for our views, urls.py;
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name = "home"),
path('<str:slug>', views.detail, name = "detail"),
]
Now in the blog project urls.py we will include the URL routes from our blogpp and include the MEDIA_URL and MEDIA_ROOT earlier defined in our setting.py file;
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blogapp.urls')),
]
urlpatterns += static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT)
Step 6: Migrate Database, create a superuser, Populate the database, and Test
Here we will make database migrations;
python manage.py makemigrations
Migrate;
python manage.py migrate
Create a superuser that'll be able to access the Django site Administration dashboard
Python manage.py createsuperuser
To test, we will start the web server;
python manage.py runserver
Now we will populate our database with 3 posts;
Output
Conclusion
Congratulations! You've successfully learned how to build a blog in Django in less than 30 minutes. Building a blog from scratch might have seemed daunting, but now you have the knowledge and confidence to start crafting your web applications with Django.
Remember, this is a pretty basic blog and you can extend its functionality by adding other cool features such as; commenting system, tags, categories, pagination, search functionality, social sharing, user profiles, and many more.
You can find the Github Repo of the blog project here, you can also get massive support anytime you need help from the Django developers community.
Happy Coding!
Top comments (0)