Building Blog CMS (Content Management System) with Django
Last Updated :
07 Jun, 2023
Django is a python based web application framework that is helpful for building a variety of web applications. Django also includes an extensible Django-Admin interface, Default SQLIte3 database, which is also extensible to PostgreSQL, MySQL databases, and a few other components to build efficient web apps.
Install and Setup Django
Create a directory for our blog, install and activate the virtual environment. Then install Django using the below commands
# creating directory for our project
mkdir gfgblog && cd gfgblog
# installing virtual environment
pip install virtualenv
python3 -m venv env
# activating virtual environment
source env/bin/activate
# installing django
pip install django
As now we have installed Django we will now create a Django project which will set up a basic Django app
django-admin startproject gfgblog
cd gfgblog
In our gfgblog Django app, we will have these files
- __init__.py  - empty file
- urls.py  - for routing our Django project
- settings.py  - has all settings for our Django project
- asgi.py, wsgi - helpful while deploying our app
We are in the Django web app directory. Now we will make some migrations for our database which will be SQLite3 which will set up some default tables to run our app in our database. Then we will create a superuser for our app.
# migrating tables
python3 manage.py makemigrations
python3 manage.py migrate
# create and enter the details for superuser
python3 manage.py createsuperuser
creating, migrating django-app
Now run the Django development server and open port 8000 in your localhost
# running python development server
python3 manage.py runserver
default django page
Now stop the server and go into gfgblog directory in our gfgblog Django app directory and open urls.py file using your code editor. You can look at the tree of our gfgblog directory from the below picture
django app directory tree structureCreate a Blog App in Django -
Now we will create our actual blog app and database for it. Go to the gfgblog project directory. You can see our SQLite3 database, gfgblog Django app. now in this directory create a new app named blog. The below command will create a new app for us.
# creating an app named blog
python3 manage.py startapp blog
The new app directory has 5 default files
- __init__.py - Â an empty file
- admin.py - Â for managing admin interface
- apps.py - Â for managing app configuration
- models.py - Â for managing database models of the app
- tests.py - Â for testing the app
- views.py - Â for managing behavior and logic of the app
As we have created the app now we have to tell the django app that there is a new app in our project. For that go to settings.py in gfgblog directory and open settings.py file. Go to installed apps section of the settings file and add the name of the app we created; in our case its blog
Python3
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog',
]
Â
Â
Creating Models for Blog CMS -Â
Now we will create Django models for the app. You can learn more about Django Models from this tutorial - Django Models
To create Django Models open in your editor and add the below code
Python3
# importing django models and users
from django.db import models
from django.contrib.auth.models import User
STATUS = (
(0,"Draft"),
(1,"Publish"),
(2, "Delete")
)
# creating an django model class
class posts(models.Model):
# title field using charfield constraint with unique constraint
title = models.CharField(max_length=200, unique=True)
# slug field auto populated using title with unique constraint
slug = models.SlugField(max_length=200, unique=True)
# author field populated using users database
author = models.ForeignKey(User, on_delete= models.CASCADE)
# and date time fields automatically populated using system time
updated_on = models.DateTimeField(auto_now= True)
created_on = models.DateTimeField()
# content field to store our post
content = models.TextField()
# meta description for SEO benefits
metades = models.CharField(max_length=300, default="new post")
# status of post
status = models.IntegerField(choices=STATUS, default=0)
# meta for the class
class Meta:
ordering = ['-created_on']
# used while managing models from terminal
def __str__(self):
return self.title
Save the file and make migration so that the fields will be created in our database using the below commands
# creates migrations for the blog app
python3 manage.py makemigrations blog
# migrates the blog app
python3 manage.py migrate blog
So far we are good with creating models and setting our database for storing our posts, but we should present them to users. Luckily Django comes with its own templating language with which we can build dynamic HTML pages to serve our users. We can also add styling using CSS and JavaScript. You can learn more about Django templates in this tutorial - Django Templates
Creating Templates for Blog CMS -
To create templates first create a template directory in the blog app (You can also create it outside the blog app but in this way, it would be easy to manage). To manage templates in an easy way for each app in the project we have to do some changes in the settings. Go to the project settings file and replace template settings with the code below
Python3
TEMPLATES_DIR = os.path.join(BASE_DIR,'templates')
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [TEMPLATES_DIR],
'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',
],
},
},
]
Now we will create templates for our blog. As Django can build pages dynamically we will build our templates according to it. Now we will create a base template that serves as a base for all the pages in our blog.
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<!-- will be replaced with meta content -->
{% block metatags %}{% endblock %}
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<style>
html, body {
height: 100%;
}
body {
font-family: sans-serif;
font-size: 18px;
background-color: #fdfdfd;
display: flex;
flex-direction: column;
}
#nofooter {
flex: 1 0 auto;
}
darkbtn {
color: #66ff99; }
.dark-mode {
background-color: black;
color: white;
}
#foot {
flex-shrink: 0;
font-family: sans-serif;
background-color: #BDFFD3; color: #00308F;
bottom: 0px;
width: 100%;
}
.card-group,.card,.card-footer,.card-body {
border: none;
}
</style>
</head>
<body><div id="bodydiv"><div id="nofooter">
<nav class="navbar navbar-expand-lg navbar-light bg-light shadow" id="topnav">
<div class="container-fluid">
<a class="navbar-brand" href="{% url 'home' %}">
<img src="/static/icons/logo.png" width="30" height="30" class="d-inline-block rounded align-top" alt="logo" loading="lazy">
GeeksForGeeks</a>
<div class="nav justify-content-end">
<div class="d-inline"><a class="nav-link text-black font-weight-bold" href="{% url 'posts'%}">Posts</a></div>
</div></div>
</nav>
<!-- will be replaced with post content -->
{% block content %}
{% endblock content %}
</div>
<footer class="footer" id="foot">
<div class="container"><h5 style="text-align:center;">Enjoy the Blog and Stay Updated</h5>
<nav class="navbar navbar-expand-lg">
<div class="container-fluid">
<p>Copyright © GeeksForGeeks</p>
<div class="nav justify-content-center" id="navfooter">
<span><a class="nav-link text-black font-weight-bold" href="{% url 'about' %}">About</a></span>
<span><a class="nav-link text-black font-weight-bold" href="{% url 'privacy' %}">Privacy</a></span>
<span><a class="nav-link text-black font-weight-bold" href="{% url 'tos' %}">Terms</a></span>
<span><a class="nav-link text-black font-weight-bold" href="#">Feed</a></span>
</div>
</div>
</nav>
</div>
</footer></div>
</body>
</html>
This will be our base template, You can block like {% block name %}{% endblock %} which will be replaced with the content assigned to them, Now we will create a home template that would be the homepage for our blog which will have the latest posts.
HTML
{% extends "base.html" %}
{% block metatags %}
<title>Home | GeeksForGeeks</title>
<meta name="description" content="A destination for Learning">
<meta property="og:title" content="GeeksForGeeks">
<meta property="og:site_name" content="GeeksForGeeks">
<meta property="og:url" content="https://GeeksForGeeks.org">
<meta property="og:description" content="A destination for Learning">
<meta property="og:type" content="website">
{% endblock %}
{% block content %}
<style type="text/css">
body {
font-family: 'Raleway', sans-serif;}
.head_text {
color: white;
}
.h1, h4 {
font-family: 'Raleway', sans-serif;
}
#mainhome {
text-align: center; }
</style>
<header class="jumbotron" style="background-color: #BDFFD3; color: #00308F;">
<div class="container">
<p class="h1"> Welcome to <strong>GeeksForGeeks</strong></p>
</div>
</header>
<div class="container">
<div class="row">
<div class="col-md-8 mt-3 left">
{% for post in posts_list %}
<div class="shadow-none card mb-4" id="newsfeed">
<div class="card-body">
<h3 class="card-title">{{ post.title }}</h3>
<p class="card-text text-muted h6"><span>{{ post.author.first_name }} {{ post.author.last_name }}</span> | {{ post.created_on}} </p>
<p class="card-text">{{post.metades }}</p>
<span><a href="{% url 'post_detail' post.slug %}" class="btn btn-outline-primary">Read More →</a></span><span><a href="{% url 'post_detail_amp' post.slug %}" class="btn btn-outline-success">AMP page</a></span>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% if is_paginated %}
<nav aria-label="Page navigation container"></nav>
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li><a href="?page={{ page_obj.previous_page_number }}" class="btn btn-outline-info" style="font-family: sans-serif;">« PREV </a></li>
{% endif %}
{% if page_obj.has_next %}
<li><a href="?page={{ page_obj.next_page_number }}" class="btn btn-outline-info" style="font-family: sans-serif;"> NEXT »</a></li>
{% endif %}
</ul>
{% endif %}
{%endblock%}
We have created our homepage with pagination. Posts will be displayed using the for a loop. Now we will add our last template to display posts individually.Â
HTML
{% extends 'base.html' %}
{% block metatags %}
<title>{{ object.title }}</title>
<meta name="description" content="{{ object.metades}}" />
<meta property="og:title" content="{{ object.title }}">
<meta property="og:site_name" content="GeeksForGeeks">
<meta property="og:url" content="{% url 'post_detail' object.slug %}">
<meta property="og:description" content="{{ object.metades }}">
<meta property="og:type" content="article">
{% endblock %}
{% block content %}
<style type="text/css">
#container img {
border-radius: 29px;
width: 100%;
height: 360px;
opacity: 0.7;
align-content: center;
}
#container img {
opacity: 1.0; }
a {text-align: center; text-decoration: none;}
</style>
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "Post",
"headline": "{{ object.title }}",
"description": "{{ object.metades }}",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "{% url 'post_detail' object.slug %}"
},
"author": {
"@type": "Person",
"name": "{{ post.author.first_name }} {{ post.author.last_name "
},
"publisher": {
"@type": "Organization",
"name": "GeeksForGeeks",
},
"datePublished": "{{ news.created_on }}",
"dateModified": "{{ news.created_on }}",
"mentions": "{{ object.source }}"
}
</script>
<div class="container">
<div class="row">
<div class="col-md-6 left">
<h1 class="card-title">{% block title %} {{ object.title }} {% endblock title %}</h1>
<p class=" text-muted">{{ object.author.first_name }} {{ object.author.last_name }} |{{ object.created_on }}</p>
<p class="card-text">{{ object.content | safe }}</p>
</div>
</div>
</div>
{% endblock content %}
Both homepage and post page will be built by extending the base template and replacing blocks with the data stored in our database. Both also have a Facebook open graphs for better sharing and social network and the post page has a structured schema for better SEO additionally. We will build AMP template incoming tutorial.
Creating Views for Blog CMS -Â
Now open views.py file in the blog directory. This file has the logic to run the blog app. We will be using class-based views for the blog app. Class Based Generic Views are an advanced set of Built-in views which are used for the implementation of selective view strategies such as Create, Retrieve, Update, Delete.Â
Python3
# importing models and libraries
from django.shortcuts import render
from .models import posts
from django.views import generic
from django.views.decorators.http import require_GET
from django.http import HttpResponse
# class based views for posts
class postslist(generic.ListView):
queryset = posts.objects.filter(status=1).order_by('-created_on')
template_name = 'home.html'
paginate_by = 4
# class based view for each post
class postdetail(generic.DetailView):
model = posts
template_name = "post.html"
Â
Â
Creating Routes for Blog CMS -Â
For Routing our blog app just go into the blog app directory and create a file urls.py to route our app. see the code belowÂ
Python3
# importing django routing libraries
from . import views
from django.urls import path, include
from .views import *
from .feeds import blogFeed
urlpatterns = [
# home page
path('', views.postslist.as_view(), name='posts'),
# route for posts
path('<slug:slug>/', views.postdetail.as_view(), name='post_detail'),
]
 Go to urls.py file in gfgblog directory and route our blog app. See the code below for reference
Python3
from django.contrib import admin
from django.urls import path, include, re_path
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
# urls handling admin route
path('admin/', admin.site.urls),
# urls handling blog routes
path('', include('blog.urls')),
]
Done, Let's Now run the server usingÂ
Python manage.py runserver
Blog CMS Screenshots -
HomePage - Â
homepage with latest's postsA sample post page -Â
sample post pagePosts management with Django Admin
posts managementUser management via Django Admin
User management
Similar Reads
Create Task Management System using Django
Task management systems are essential tools for individuals and organizations to organize, track, and prioritize tasks efficiently. In this article, we'll explore how to build a task management system using django in Python.Task Management System using DjangoBelow, are the implementations of the Tas
15+ min read
College Management System using Django - Python Project
In this article, we are going to build College Management System using Django and will be using dbsqlite database. In the times of covid, when education has totally become digital, there comes a need for a system that can connect teachers, students, and HOD and that was the motivation behind buildin
15+ min read
Custom Django Management Commands
Prerequisites:Â Django Introduction and Installation Manage.py in Django is a command-line utility that works similar to the django-admin command. The difference is that it points towards the project's settings.py file. This manage.py utility provides various commands that you must have while workin
4 min read
Event Management System Using Python Django
In today's fast-paced world, businesses, schools, and groups need to organize events well. An Event Management System (EMS) makes planning and managing events easier. This article will explain the structure, important parts, and how to build a helpful Event Management System with Django.Event Manage
13 min read
Building Web App with Django and FastAPI
Django is a powerful and popular web framework for building robust web applications with Python. It comes with a lot of built-in features, including an ORM, an authentication system, and a powerful admin interface. However, there are scenarios where you might want to integrate Django with FastAPI, a
4 min read
Building APIs using FastAPI with Django
Combining FastAPI and Django can leverage the strengths of both frameworks: FastAPI's high performance for building APIs and Django's powerful ORM and admin interface. In this guide, we'll outline how to integrate FastAPI into a Django project to build high-performance APIs.In this article, we will
2 min read
Django's Soft Delete: Simplifying Data Management
In Django, soft delete is a clever way to manage data removal. Instead of getting rid of records forever in the database, it takes a gentler approach. Here's the idea: When you want to delete certain data, you don't wipe it out completely. Instead, you place a special marker, like a flag, to show th
5 min read
Boutique Management System using Python-MySQL Connectivity
In this article, we are going to make a simple project on a boutique management system using Python MySql connectivity. Introduction This is a boutique management system made using MySQL connectivity with Python. It uses a MySQL database to store data in the form of tables and to maintain a proper r
15+ min read
Online Auction System Using Django
In this article, I am Going to Build an Online auction system Using Django. Physical auctions are limited by location but online auctions are accessible globally. It opens a broader range of buyers. It Can Connect the Seller, Buyer, and admin. Tools & Technologies Used in this Project : Python D
9 min read
How to Pass Additional Context into a Class Based View (Django)?
Passing context into your templates from class-based views is easy once you know what to look out for. There are two ways to do it - one involves get_context_data, the other is by modifying the extra_context variable. Let see how to use both the methods one by one. Explanation: Illustration of How t
2 min read