Building a Blog Using Django

In this tutorial, we will create a basic blog using Django. In coming series, we will add new functionalities to the blog.

Prerequisite

  • Python 3.8 or greater check python version
python3 -V
Python 3.9.12

if python version is not 3.8 or greater, run below command to installed Python in Mac

brew install python3

Creating project directory

We will create a project directory which will be used for storing all project code and python dependencies.

mkdir django-blog

Creating virtual environment

We will create a virtual environment to isolate python version and dependencies from system python. Creating a virtual environemnt is recommended for almost all python projects.


cd django-blog
python3 -m venv venv

After running above commands, below folder structure is created


django-blog
└── venv 

In venv directory we will installed our local version of python3. This local python will be used for our proejct development.

Activating virtual environment

Before using a virtual environment, we have to activate the environemnt. For activating the virtual environment, run below command.

source venv/bin/activate

Once the virtual environment is activated, the python installed in venv directory will be used. To verify which python is being used, run below command after activating the virtual environemnt.

which python

/Users/vinodpandey/Projects/django-blog/venv/bin/python

In my case, it shows the /venv/bin/python inside django-blog. /Users/vinodpandey/Projects is where is created this directory. This path will be different, but django-blog/venv/bin/python will be same.

Installing Django in virtual environment

Make sure you are in django-blog directory and run below command


pip install Django==4.0.5

since we are using virtual environment, Django will be installed in venv directory we created earlier.

If we run below command, it will show Django 4.0.5 installed directories


ls venv/lib/python3.9/site-packages/

Django-4.0.5.dist-info       pip-22.0.4.dist-info
_distutils_hack              pkg_resources
asgiref                      setuptools
asgiref-3.5.2.dist-info      setuptools-60.10.0.dist-info
distutils-precedence.pth     sqlparse
django                       sqlparse-0.4.2.dist-info
pip


Creating Django project

We will use django-admin command to create project. This command was installed when we installed Django.

To view the django-admin command location, run below command

ls venv/bin/

Activate.ps1  activate.fish pip3          python3
activate      django-admin  pip3.9        python3.9
activate.csh  pip           python        sqlformat

Run below command to create the project

django-admin startproject project .

After running the project, below folder structure ic created


django-blog
├── project
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
└── venv

If we don't use the . in above command and run django-admin startproject project, it creates below structure with project directory inside project directory and we don't want that. So, we use django-admin startproject project . to create simple directory strucure.

Below is the structure when we don't use .

 
django-blog
.
├── project
│   ├── project
│   └── manage.py
└── venv

Setting Sqlite as database for Django blog

Run below command to create Django related database tables


python manage.py migrate

By default Django uses sqlite database because of the below default configuration in project/settings.py


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


We will keep the setting as-is. In later series, we will use MySQL instead of Sqlite database.

The database file in created in django-blog directory itself. To view the file, run below command in django-blog directory.

ls

db.sqlite3 manage.py  project    venv

db.sqlite3 is the database file where all our blog related data will be stored.

At this stage, we can run bleow command to verify that the server is running fine.

python manage.py runserver

Below is the output of above command


Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
June 13, 2022 - 12:14:35
Django version 4.0.5, using settings 'project.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Open browser and access http://127.0.0.1:8000/

To quit the server use CONTROL-C

Setting up admin account and access admin section

Quit the server using CONTROL-C and run below command to create admin user

python manage.py createsuperuser

Username (leave blank to use 'vinodpandey'): admin
Email address: admin@admin.com
Password: 
Password (again): 
The password is too similar to the username.
This password is too short. It must contain at least 8 characters.
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

We are using username: admin and password: admin during development. Never use this combination in production.

Run the server using python manage.py runserver and access http://127.0.0.1:8000/admin/. It will show the admin section.

Creating blog application

Stop the server if it is already running. Make sure you are in django-blog directory and virtual environment is activated.

Quit the server with CONTROL-C (if it is already running) and run below command to create the blog app.

python manage.py startapp blog

This command will create a blog directory inside django-blog directory will bunch of other files.

Below is the directory structure now:


django-blog
├── blog
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── db.sqlite3
├── manage.py
├── project
│   ├── __init__.py
│   ├── __pycache__
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── venv

To register blog app in Django, open project/settings.py and search for INSTALLED_APPS . In INSTALLED_APPS, add blog at the end of existing entries after django.contrib.staticfiles as mentioned below.



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

Creating model for blog

We will create a model Post which will be used for creating and storing blog posts. In blog/models.py add below content.


# Create your models here.
from django.db import models
from django.contrib.auth.models import User


STATUS = (
    (0, "Draft"),
    (1, "Published")
)

class Post(models.Model):
    title = models.CharField(max_length=250, unique=True)
    slug = models.SlugField(max_length=250, unique=True)
    created_by = models.ForeignKey(User, on_delete= models.CASCADE,related_name="%(app_label)s_%(class)s_created_by")
    content = models.TextField()
    status = models.IntegerField(choices=STATUS, default=0)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now= True)

    class Meta:
        ordering = ['-created_at']

    def __str__(self):
        return self.title
        

We have created the model Post and now we have to create the corresponding database tables in sqlite. For creating tables, run below command:


python manage.py makemigrations

Migrations for 'blog':
  blog/migrations/0001_initial.py
    - Create model Post

Above command created a migration file 0001_initial.py in blog/migrations/ directory.


ls blog/migrations
0001_initial.py

Next step is to apply the migrations and create the database tables.


python manage.py migrate

Operations to perform:
  Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
  Applying blog.0001_initial... OK
  

Enabling admin functionality for models for entering data

In blog/admin.py add below content. This will enable django admin backend for blog app.


from .models import Post

admin.site.register(Post)

Run django server using python manage.py runserver and access http://localhost:8000/admin/. It should show Django administration page.

Create a new blog post which we will use for testing.


title: Hello World
slug: hell-world
author: admin
content: Hello world
Status: Published

Creating blog views and urls

We will create views, urls and couple of other files. Stop the Django server and once all below files are created, start it again.

Creating view

In blog/views.py add below code


from django.views import generic
from .models import Post


class PostList(generic.ListView):
    queryset = Post.objects.filter(status=1).order_by('-created_at')
    template_name = 'blog/index.html'


class PostDetail(generic.DetailView):
    model = Post
    template_name = 'blog/detail.html'
    

Creating template

Create blog template directory


mkdir -p blog/templates/blog/

create file index.html in blog/templates/blog/ directory and add below content


<!DOCTYPE html>  
<html lang="en">  
<head>  
    <meta charset="UTF-8">  
    <title>Django Blog</title>  
</head>  
  
<body>  
  
    <h1>Posts:</h1>  
    {% for post in object_list %}  
        <ul>  
            <li> 
              <a href="{% url 'blog_post_detail' post.slug  %}">{{ post.title }</a>  
            </li>        
        </ul>    
    {% endfor %}  
    {% if not object_list %}  
        <p>No post available.</p>  
    {% endif %}  
  
</body>


create file detail.html in blog/templates/blog/ directory and add below content


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{  object.title }}</title>
</head>
<body>
 <p> <a href="{% url 'blog_home' %}"><< Back</a></p>
 <h1>{{ object.title }}</h1>
 <p>{{ object.content }}</p>
</body>
</html>


Creating urls

Create a new file urls.py in blog directory and add below code in blog/urls.py. You will see views.py in same directory.


from . import views
from django.urls import path

urlpatterns = [
    path('', views.PostList.as_view(), name='blog_home'),
    path('<slug:slug>/', views.PostDetail.as_view(), name='blog_post_detail'),
]


In project/urls.py append path('', include('blog.urls')), below path('admin/', admin.site.urls), as specified below. Also update the import as from django.urls import path, include. We have added include in existing import.

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

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


Accessing blog application

Now run the server again

python manage.py runserver

And access http://localhost:8000. It should show the blog post we created earlier. On clicking the link, it will open individual blog post.

In upcoming series, we will add new functionality to blog and enhance the UI.