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.