DEV Community

Cover image for How to Build Your Blog in Django: Rapid Development in Under 30 Minutes!
Pwaveino Victor Clarkson
Pwaveino Victor Clarkson

Posted on • Originally published at link.Medium

How to Build Your Blog in Django: Rapid Development in Under 30 Minutes!

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

We can now create our blog project using the “Django-admin” command.

django-admin startproject blog
Enter fullscreen mode Exit fullscreen mode

We can now create our application within the new “blog” project

python manage.py startapp blogapp
Enter fullscreen mode Exit fullscreen mode

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'
]
Enter fullscreen mode Exit fullscreen mode

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})
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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',
            ],
        },
    },
]

Enter fullscreen mode Exit fullscreen mode

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'
]
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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 %}
Enter fullscreen mode Exit fullscreen mode

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 %}
Enter fullscreen mode Exit fullscreen mode

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%;
}
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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"),
]
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Step 6: Migrate Database, create a superuser, Populate the database, and Test

Here we will make database migrations;

python manage.py makemigrations
Enter fullscreen mode Exit fullscreen mode

Migrate;

python manage.py migrate
Enter fullscreen mode Exit fullscreen mode

Create a superuser that'll be able to access the Django site Administration dashboard

Python manage.py createsuperuser 
Enter fullscreen mode Exit fullscreen mode

To test, we will start the web server;

python manage.py runserver
Enter fullscreen mode Exit fullscreen mode

Now we will populate our database with 3 posts;

Image description

Output

Image description

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)