Create a Blog Application with Django

In this post, we are going to create our blog using Django. Firstly, Python and Django must be installed on the computer. You can download it from Django official website, also you can learn how to install it on your computer.

I will use Windows operating system when I will make this application. So let's create our project at first:

C:\Users\0xcoder\Desktop>django-admin startproject mysite
After this command, There will be a folder called mysite on the desktop. In this folder, there is a manage.py folder for running our website on the local server and other necessary things as operations. Also there is a folder named mysite again. In this folder, there are project files called, __init__.py, asgi.py, settings.py, urls.py, wsgi.py. Currently, urls.py and settings.py are important for us. settings.py file contains settings about project and urls.py contains a list like redirect to this view if the user requests the following url. You can get much information from this page. Let's get inside this mysite folder:
C:\Users\0xcoder\Desktop>C:\Users\0xcoder\Desktop>cd mysite

C:\Users\0xcoder\Desktop\mysite>dir
 Volume in drive C has no label.
 Volume Serial Number is 3C09-3D8E

 Directory of C:\Users\0xcoder\Desktop\mysite

08/21/2021  01:26 PM    <DIR>          .
08/21/2021  01:26 PM    <DIR>          ..
08/21/2021  01:26 PM               684 manage.py
08/21/2021  01:26 PM    <DIR>          mysite
               1 File(s)            684 bytes
               3 Dir(s)  88,329,146,368 bytes free
Let's test it our website with this command:
C:\Users\0xcoder\Desktop\mysite> python manage.py runserver
Our website should be worked on the local server(http://127.0.0.1:8000/). 

There are some important information we need to know about Django:
  • The software design pattern of Django is MVT (Model - View - Template). Model is a structure representing the table. View is the part that creates the logical structure. The structure that provides the relationship between the model and the template. Template is a static file like html file.
  • Django has ORM Architecture (Object-Relational Mapping). This is about Models in Django. We don't need to type database tables with SQL. We can just use Python to create class as a model that will be a table on the database. 
So, we want to make a blog. Therefore, we need to model called Post. This post is a model but it's actually a table on the database. But firstly we have to create an application called myblog:
C:\Users\0xcoder\Desktop\mysite> django-admin startapp myblog
We created first application in the project. Everything should be piecemeal in Django. This is a very useful type of usage. Also you can use this application the Django projects. We actually created our Django module. Let's check out the files of this application.
Django Application Files
In admin.py, we are going to add our model to admin panel also we can update view of these models. This apps.py contains application configurations. models.py contains the application models like post, user, comment, etc.. tests.py is for testing our Django project. views.py contains the applications view functions and classes. You can get more information from this page. 

I'm going to use Visual Studio Code as code editor. I will open this project like this:
C:\Users\0xcoder\Desktop\mysite> code .
Let's create a model called post in models.py:
from django.db import models
from django.contrib.auth.models import User

# Create your models here.
class Category(models.Model):
    name = models.CharField(max_length=100, unique=True)
    def __str__(self):
        return self.name

class Post(models.Model):
    title = models.CharField(max_length=400)
    content = models.TextField()
    pub_date = models.DateTimeField(auto_now_add=True)
    categories = models.ManyToManyField(Category)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
We created our models which are Category and Post. Post model contains five fields. We know what are the first three fields already. Each post can have category and more categories. Also it should be a model as parameter to use it our Post model. So, I created Category model. Also each post must have one author. I did not create a model for user, because Django already provides this model as default. But, you can create your user model. We created these models but these models are not known by database. We have to save this models as table on the database. However Django does not know we have an application in project. Therefore we need to install this application at first. Open settings.py in the project folder:
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myblog.apps.MyblogConfig',
]
Now, we should understand why we need to apps.py in application. Okay, now we can save our models as migration at first:
C:\Users\0xcoder\Desktop\mysite>python manage.py makemigrations
Migrations for 'myblog':
  myblog\migrations\0001_initial.py
    - Create model Category
    - Create model Post
After this command, we need to save this migrations as table in database:
C:\Users\0xcoder\Desktop\mysite>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, myblog, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying myblog.0001_initial... OK
  Applying sessions.0001_initial... OK
We saved our models in database. I want to see my tables that I created on the admin panel. But, I have to make some operation for this. Firstly, I have to add this models to see them in admin page. Let's open admin.py files in myblog application:
from django.contrib import admin
from .models import Category, Post

# Register your models here.
admin.site.register(Post)
admin.site.register(Category)
In order to enter the admin panel, we need to create a user:
C:\Users\0xcoder\Desktop\mysite>python manage.py createsuperuser
Username (leave blank to use '0xcoder'): 0xcoder
Email address:
Password:
Password (again):
Superuser created successfully.
After creation of Superuser, run the server and open this url http://127.0.0.1:8000/admin. Login with your superuser username and password to admin panel. You should see these models on your admin panel:
Models in Admin Panel

I created a first post as example:
Form Model in Admin Panel
When you save it, you should see this output:
Title of Model Object in Admin Panel

This is normal. I already added this feature to Category model. Post title should be seen instead of "Post object (number)". To do this, lets open models.py and added __str__ function to the Post class like this:
class Post(models.Model):
    title = models.CharField(max_length=400)
    content = models.TextField()
    pub_date = models.DateTimeField(auto_now_add=True)
    categories = models.ManyToManyField(Category)
    author = models.ForeignKey(User, on_delete=models.CASCADE)

    def __str__(self):
        return self.title
After this update operation, refresh admin page:
Title of Model Object in Admin Panel
That's a good progress. Let's create our first view with template. Firstly I created a folder called templates in the application. 
Directory of the project in the Visual Studio Code

Create a new file called index.html in this folder:
<!DOCTYPE html>
<html>
    <head>
        <title>My Blog</title>
    </head>
    <body>
        <h1>My Blog</h1>
    </body>
</html>
Let's create our first view in views.py:
from django.shortcuts import render

# Create your views here.
def index(request):
    return render(request, 'index.html')
Now, we have to make URL routing for index view. Primarily, I'm creating a file named urls.py in the application:
from django.urls import path
from .views import index

urlpatterns = [
    path('/', index),
]
But, we have to state this urlpatterns list to the original urlpatterns list that is in project directory:
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('myblog.urls')),
]
When we run the project, the result should be like that:
Preview on blog
But templates folder shouldn't be there for this project. I will located it in mysite main directory like this:
  • mysite + 
    • mysite +
    • myblog +
    • templates +
    • db.sqlite3
    • manage.py
After this operation, we have to specify this templates folder in settings.py.  Open the settings.py:
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(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',
            ],
        },
    },
]
Let's run the server again. You should get same template in the webpage.

Also, we need to use CSS and JS files in our website. These files are static files. Therefore, we need to create folder called static in mysite directory:
  • mysite + 
    • mysite +
    • myblog +
    • static +
    • templates +
    • db.sqlite3
    • manage.py
Now, we need to specify this folder in settings.py as static. Open the settings.py file and type this code:
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'), ]
I need to some static files. I downloaded Bootstrap files from their official website and I added these files in static folder:
Directory of project in django

Now, we can use our static files in html file:
{% load static %}

<!DOCTYPE html>
<html>
    <head>
        <title>My Blog</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
        <script src="{% static 'js/jquery.min.js' %}"></script>
        <script src="{% static 'js/bootstrap.min.js'%}"></script>
    </head>
    <body>
        <div class="container">
            <h1>My Blog</h1>
            <p>This is a test.</p>
        </div>
    </body>
</html>
Let's check out the webpage again:
Preview on blog
That's cool. However, we will have more pages in this blog and there are common areas for these pages like header, footer, etc. We shouldn't type this same code to each html files. As a solution, we can use partials way. Let's create a file called _base_layout.html in templates folder and I cut this code from index.html to _base_layout.html:
{% load static %}

<!DOCTYPE html>
<html>
    <head>
        <title>My Blog</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="{% static 'styles.css' %}">
        <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
        <script src="{% static 'js/jquery.min.js' %}"></script>
        <script src="{% static 'js/bootstrap.min.js'%}"></script>
    </head>
    <body>
        <div class="container">
            <header>
                <div class="row">
                    <div class="col-sm-12">
                        <h1>My Blog</h1>
                        <p>This is a test.</p>
                    </div>
                </div>
            </header>
            <!-- header end -->

            {% block content %}
            {% endblock %}

            <!-- footer start -->
            <footer>
                <div class="row">
                    <div class="col-sm-12">Footer</div>
                </div> 
            </footer>

        </div>
    </body>
</html>
We created our base_layout. Now let's extend this html file in other files. I will extend it from index.html:
{% extends '_base_layout.html' %}

{% block content %}
<div class="row">
    <div class="col-sm-9">Content</div>
    <div class="col-sm-3">Sidebar</div>
</div> 
{% endblock %}
I want to add a navbar my template. Firstly, I created _navbar.html in templates folder:
<nav class="navbar navbar-expand-sm bg-light">
    <!-- Links -->
    <ul class="navbar-nav">
        <li class="nav-item">
            <a class="nav-link" href="#">Link 1</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="#">Link 2</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="#">Link 3</a>
        </li>
    </ul>
</nav>
After that, I will edit base_layout.html and I will use it include for adding navbar:
{% load static %}

<!DOCTYPE html>
<html>
    <head>
        <title>My Blog</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="{% static 'styles.css' %}">
        <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
        <script src="{% static 'js/jquery.min.js' %}"></script>
        <script src="{% static 'js/bootstrap.min.js'%}"></script>
    </head>
    <body>
        <div class="container">
            <header>
                <div class="row">
                    <div class="col-sm-12">
                        <h1>My Blog</h1>
                        <p>This is a test.</p>
                    </div>
                </div>
            </header>
            <!-- header end -->

            {% include '_navbar.html' %}

            {% block content %}
            {% endblock %}

            <!-- footer start -->
            <footer>
                <div class="row">
                    <div class="col-sm-12">Footer</div>
                </div> 
            </footer>

        </div>
    </body>
</html>
And the result is like this:
Preview on blog

I want to add "about page" on my blog. I opened views.py in the application and I created a new view for about page:

from django.shortcuts import render

# Create your views here.
def index(request):
    return render(request, 'index.html')

def about(request):
    return render(request, 'about.html')
After that, I will add this view in urls.py file in the application:
from django.urls import path
from .views import * 

urlpatterns = [
    path('', index),
    path('about', about, name="about"),
]
Let's create a new html file called about.html:
{% extends '_base_layout.html' %}

{% block content %}
<div class="row">
    <div class="col-sm-12">
        <h1>About</h1>
    </div>
</div> 
{% endblock %}
Let's look at the result (Don't remember edit _navbar.html file):
Preview on blog
How should we arrange url paths in navbar:
<nav class="navbar navbar-expand-sm bg-light">
    <!-- Links -->
    <ul class="navbar-nav">
        <li class="nav-item">
            <a class="nav-link" href="{% url 'index' %}">Home</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="{% url 'about' %}">About</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="#">Link 2</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="#">Link 3</a>
        </li>
    </ul>
</nav>
That's wunderbar. Now, let's create post list as view. We will do that in the index view. Let's import models in the views.py:
from django.shortcuts import render
from .models import Post

# Create your views here.
def index(request):
    # get all posts from database
    posts = Post.objects.all()

    context = {
        'posts': posts,
    }
    
    return render(request, 'index.html', context)

def about(request):
    return render(request, 'about.html')
Let's iterate these posts in the index.html:
{% extends '_base_layout.html' %}

{% block content %}
<div class="row">
    <div class="col-sm-9">
        {% for post in posts %}
            <div class="card">
                <div class="card-body">
                    <h5 class="card-title"> {{ post.title }} </h5>
                    <p class="card-text"> {{ post.content }} </p>
                    <a href="#" class="btn btn-primary">Read more..</a>
                </div>
            </div>
        {% endfor %}
    </div>
    <div class="col-sm-3">Sidebar</div>
</div> 
{% endblock %}

Let's look at the result:

Preview on blog

Let's add some posts on the admin panel and let's look at the again:
Preview on blog

That's okay but there is a problem ordering. The last post should be top on the older posts. Open the models.py in the application folder and add this meta setting to solve this problem:
class Post(models.Model):
    title = models.CharField(max_length=400)
    content = models.TextField()
    pub_date = models.DateTimeField(auto_now_add=True)
    categories = models.ManyToManyField(Category)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    
    def __str__(self):
        return self.title
    
    class Meta:
        ordering = ['-pub_date',]
Now, we solved that problem. So, what we are going to do? When we click the post title or "read more" button, we must get detail of this post. I will add new view function as detail:
def detail(request, pk):
    post = Post.objects.get(pk=pk)

    context = {
        'post' : post,
    }
    return render(request, 'detail.html', context)
Add this view on the urls.py file in the application:
urlpatterns = [
    path('', index, name="index"),
    path('about', about, name="about"),
    path('post/<int:pk>', detail, name="detail"),
]
We have to create a template file called detail:
{% extends '_base_layout.html' %}

{% block content %}
<div class="row">
    <div class="col-sm-9">
        <h1>{{ post.title }}</h1>
        <p>{{ post.content }}</p>
    </div>
    <div class="col-sm-3">
        sidebar
    </div>
</div> 
{% endblock %}
Let's edit index.html again according to this:
{% extends '_base_layout.html' %}

{% block content %}
<div class="row">
    <div class="col-sm-9">
        {% for post in posts %}
            <div class="card">
                <div class="card-body">
                    <h5 class="card-title">
                        <a href="{% url 'detail' pk=post.pk %}">{{ post.title }}</a> 
                     </h5>
                    <p class="card-text"> {{ post.content }} </p>
                    <a href="#" class="btn btn-primary">Read more..</a>
                </div>
            </div>
        {% endfor %}
    </div>
    <div class="col-sm-3">Sidebar</div>
</div> 
{% endblock %}
Now, let's run the server:
detail of post

Finally, we made this. I think this is enough as I beginner. I learnt too much things when I type this blog post. I hope it will help you. I will keep going learn and share my experience on this blog. I will develop this project as much as I can. 

No comments:

Post a Comment