Arsalan Shahid

Building a Blog Application in Django: A Start

My dear reader, how are you? السلام عليكم

Every new beginning comes from some other beginning’s end –Seneca

This is the first post for a tutorial series on building a blog application using Django. We will refer to it as ‘Django-Blog’. In this post, we will create basic blog views, models and templates. This tutorial has been adopted from the book Django 2 by Example. I will be providing summarized details for this project step-by-step. The end goal is to have a fully functional blog application running on Django server.


Few useful links to practically follow the project:

GitHub repository — DirectMe

All other tutorials on Django-Blog — DirectMe

I am using Ubuntu 18.04 shell as a development OS.


Create a Virtual Environment and Setup Django Project

First of all, create a virtual environment, install Django and create a Django project in a virtual environment into it as shown below. We configured the Django project to use PostgreSQL as a database backend. We followed the same methodology explained in an earlier post followed by this DirectMe.

$ mkdir Django-Blog-Application
$ cd Django-Blog-Application
Django-Blog-Application$ virtualenv blogenv 
Django-Blog-Application$ source blogenv/bin/activate
(blogenv)Django-Blog-Application$ pip3 install django
(blogenv)Django-Blog-Application$ pip3 freeze > requirements.txt

# Create a Django project
(blogenv)Django-Blog-Application$ django-admin startproject config 

# Change project name
(blogenv)Django-Blog-Application$ mv config Django-Blog 
(blogenv)Django-Blog-Application/Django-Blog$ django-admin startapp blog

# list the blog application in the list of installed apps in settings.py of project as shown below

INSTALLED_APPS = [
'blog', # add this
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]

# Configure Django project to use PostgreSQL. 
# open Django-Blog/config/settings.py and update the following DATABASE variable as follows

DATABASES = {
        'default':
            {
                'ENGINE': 'django.db.backends.postgresql',
                'NAME': 'mymdb',
                'USER': 'mymdb',
                'PASSWORD': 'development',
                'HOST': '127.0.0.1',
                'PORT': '5432',
            }
}

(blogenv)Django-Blog-Application/Django-Blog$ python3 manage.py makemigrations
(blogenv)Django-Blog-Application/Django-Blog$ python3 manage.py migrate

(blogenv)Django-Blog-Application/Django-Blog$ python3 manage.py runserver

Check if development server is working by entering the following URL in browser http://127.0.0.1:8000/. It should be giving you something as shown in Figure 1. If it does, you are doing great. cheers!

A detail on individual Django project files is given in one of my earlier posts at DirectMe

Fig 1: Django Welcome Page


Create A BLOG POST MODEL

Let us first create a Blog post model as shown below

# open blog/models.py and add the following code

from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse

class Post(models.Model):
    STATUS_CHOICES = (
        ('draft', 'Draft'),
        ('published', 'Published'),
    )
    title = models.CharField(max_length=250)
    slug = models.SlugField(max_length=250,
                            unique_for_date='publish')
    author = models.ForeignKey(User,
                               on_delete=models.CASCADE,
                               related_name='blog_posts')
    body = models.TextField()
    publish = models.DateTimeField(default=timezone.now)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    status = models.CharField(max_length=10,
                              choices=STATUS_CHOICES,
                              default='draft')


    class Meta:
        ordering = ('-publish',)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('blog:post_detail',
                       args=[self.publish.year,
                             self.publish.month,
                             self.publish.day,
                             self.slug])

# Migrate the models to create SQL entities in the backend database

(blogenv)Django-Blog-Application/Django-Blog$ python3 manage.py makemigrations blog
(blogenv)Django-Blog-Application/Django-Blog$ python3 manage.py migrate


SETUP AN Admin SITE for BLOG POSTS

After this, we would be needing to register the models on the admin site but first, we will create a superuser for the admin site as shown below

(blogenv)Django-Blog-Application/Django-Blog$  python3 manage.py createsuperuser

We now add the blog model to the admin site as shown below

# open blog/admin.py and add the following code

from django.contrib import admin

from .models import Post
admin.site.register(Post) # it would register the models as default

You can open the admin site on your browser and add a post into it manually at http://127.0.0.1:8000/admin/


ADD Quert_set managers for Post MODEL

We will now add custom model managers to be able to extract only blog posts whose status is ‘published’.

# open blog/models.py and add

class PublishedManager(models.Manager):
    def get_queryset(self):
        return super(PublishedManager,
                self).get_queryset()\
                        .filter(status='published')

class Post(models.Model):
    ...
    ...
    published =  PublishedManager() # add this

    class Meta:
        ordering = ('-publish',)
    ...
    ...

Building List and Detail Views for BLOG Posts

First, we add the views as shown below

# open blog/views.py and add

from django.shortcuts import render, get_object_or_404
from .models import Post


def post_list(request):
     posts = Post.published.all()
     return render(request,
             'blog/post/list.html',
             {'posts': posts})

def post_detail(request, year, month, day, post):
    post = get_object_or_404(Post, slug=post,
            status='published',
            publish__year=year,
            publish__month=month,
            publish__day=day)
    return render(request,
            'blog/post/detail.html',
            {'post': post})

After this, we need to add the routing information as shown below

# create urls.py file in blog app and add the following code

from django.urls import path
from . import views

app_name = 'blog'
urlpatterns = [
        path('', views.post_list, name='post_list'),
        path('<int:year>/<int:month>/<int:day>/<slug:post>/',
            views.post_detail,
            name='post_detail'),
        ]

# update urls.py in config with the following

from django.contrib import admin
from django.urls import path, include

import blog.urls

urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/', include('blog.urls', namespace='blog')),
]

Adding Templates and static files for list and detail views

Create the following folders and files in blog application.

templates/
└── blog/
├── base.html
└── post/
├── detail.html
└── list.html

# open base.html and add the following program

{% load static %}
<!DOCTYPE html>
<html>
<head>
        <title>{% block title %}{% endblock %}</title>
        <link href="{% static "css/blog.css" %}" rel="stylesheet">
</head>
<body>
        <div id="content">
                {% block content %}
                {% endblock %}
        </div>

        <div id="sidebar">
                <h2>My blog</h2>
                <p>This is my blog.</p>
        </div>

</body>
</html>

# open list.html and add the following program

{% extends "blog/base.html" %}
{% block title %}My Blog{% endblock %}

{% block content %}

<h1>My Blog</h1>

{% for post in posts %}
<h2>
<a href="{{ post.get_absolute_url }}">
{{ post.title }}
</a>
</h2>
<p class="date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|truncatewords:30|linebreaks }}
{% endfor %}
{% endblock %}

# open detail.html and add the following program

{% extends "blog/base.html" %}
{% block title %}{{ post.title }}{% endblock %}
{% block content %}
<h1>{{ post.title }}</h1>
<p class="date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|linebreaks }}
{% endblock %}

Create the following file and directory pattern in blog application for static files

static/
└── css
└── blog.css

# open blog.css and add the following program

body {
margin:0;
padding:0;
font-family:helvetica, sans-serif;
}

a {
color:#00abff;
text-decoration:none;
}

h1 {
font-weight:normal;
border-bottom:1px solid #bbb;
padding:0 0 10px 0;
}

h2 {
font-weight:normal;
margin:30px 0 0;
}

#content {
float:left;
width:60%;
padding:0 0 0 30px;
}

#sidebar {
float:right;
width:30%;
padding:10px;
background:#efefef;
height:100%;
}

p.date {
color:#ccc;
font-family: georgia, serif;
font-size: 12px;
font-style: italic;
}

/* pagination */
.pagination {
margin:40px 0;
font-weight:bold;
}

/* forms */
label {
float:left;
clear:both;
color:#333;
margin-bottom:4px;
}
input, textarea {
clear:both;
float:left;
margin:0 0 10px;
background:#ededed;
border:0;
padding:6px 10px;
font-size:12px;
}
input[type=submit] {
font-weight:bold;
background:#00abff;
color:#fff;
padding:10px 20px;
font-size:14px;
text-transform:uppercase;
}
.errorlist {
color:#cc0033;
float:left;
clear:both;
padding-left:10px;
}

/* comments */
.comment {
padding:10px;
}
.comment:nth-child(even) {
background:#efefef;
}
.comment .info {
font-weight:bold;
font-size:12px;
color:#666;
}

If you run the development server at this point in time you should be able to see the following at http://127.0.0.1:8000/blog/


I hope you find this tutorial useful. If you find any errors or feel any need for improvement, let me know in your comments below.

Signing off for today. Stay tuned and I will see you next week! Happy learning.

Exit mobile version