A Simple Blog With Comments on Django: Development and Deployment for the Smallest Ones

This article is intended for beginner web programmers and covers the development of a blog on Django using Twitter Bootstrap and its deployment on the free hosting provider PythonAnywhere. I tried to write this to be as transparent and straightforward as possible. For more experienced users, this article will not tell you anything new, and some techniques may seem ineffective.

I assume that the reader is already familiar with Python syntax, has a minimal understanding of Django (it’s a good idea to start with tutorials at http://codeacademy.com on the appropriate topic and read a tutorial on Django), and also knows how to work on the command line.

So, let’s start by organizing the working environment on a local computer. In principle, any operating system that you feel comfortable in will work for our purposes. Here, I describe the process for GNU / Linux, for other systems the steps may differ slightly. The system must have virtualenv installed, a utility for creating an isolated working environment (so that the libraries we use do not interfere with other programs and projects).

Create and activate an environment:

mkdir ~/projects
cd ~/projects
virtualenv env
source env/bin/activate 

In Windows, the last command should be like this:

env\Scripts\activate

Install Django using the Python PIP package Manager.

pip install django

Create a new project. Let’s call it something original — for example, mysite.

django-admin.py startproject mysite && cd mysite

The script will work and create a mysite directory with another mysite directory and several *. py files inside. Use the script manage.py to create a django app named blog.

python manage.py startapp blog

Edit settings in the file mysite/settings.py (note: I mean ~/projects/mysite/mysite/settings.py) adding the following:

# coding: utf-8
import os

BASE_DIR = os.path.dirname(os.path.dirname(__file__))

In the first line, we specify the encoding in which we work, to avoid confusion and glitches, I suggest specifying it in all modified *. py files, changing them to be in UTF-8. BASE_DIR will store the full path to our project so that you can use relative paths for further configuration.

Let’s set up a database, in our project it is quite possible to use SQLite

DATABASES = { 'default':
    {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

Configure the time zone and language:

TIME_ZONE = 'Europe/Moscow'
LANGUAGE_CODE = 'ru-ru'

In order for Django to find out about the created app, add ‘blog’ to the INSTALLED_APPS tuple, and uncomment the ‘django’ string.contrib.admin’ to enable the built-in admin panel:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.admin',
    'blog',
)

To make the admin panel work, edit mysite/urls.py

from django.conf.urls import patterns, include, url

from django.contrib import admin
admin.autodiscover()  #function that automatically discovers admin.py files in our apps

urlpatterns = patterns('',
    url(r'^admin/', include(admin.site.urls)), #URL of admin http://site_name/admin/
)

Create a model in blog/models.py

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=255) # the title of the post
    datetime = models.DateTimeField(u'Date of Publication') # date of publication
    content = models.TextField(max_length=10000) # the text of the post

    def __unicode__(self):
        return self.title

    def get_absolute_url(self):
        return "/blog/%i/" % self.id

Based on this model, Django will automatically create tables in the database.

Register it in the admin panel blog/admin.py

from django.contrib import admin
from blog.models import Post # our model from blog/models.py

admin.site.register(Post)

Create tables with the command:

python manage.py syncdb

When you first call this command, Django will ask to create a superuser, you should do that.

Start the debug server that Django provides:

python manage.py runserver

Now enter the url in the browser

http://localhost:8000/admin/

If everything went well, we should see this:

Go to the admin panel with the previously created username/password — now we can add and delete posts (buttons to the right of Posts)

Let’s create some posts for debugging.

Now let’s create a frontend. We need only two template pages — one with a list of all posts, the second – the content of the post.

Edit blog/views.py

from blog.models import Post 
from django.views.generic import ListView, DetailView

class PostsListView(ListView): # list presentation
    model = Post               # model for representation

class PostDetailView(DetailView): # detailed view of the model
    model = Post

Add this line to urlpatterns mysite/urls.py

url(r'^blog/', include('blog.urls')),

For all URLs starting with /blog/ to be processed using urls.py from the blog module, and create the file itself urls.py in the blog module with the following content:

#coding: utf-8
from django.conf.urls import patterns, url

from blog.views import PostsListView, PostDetailView 

urlpatterns = patterns('',
url(r'^$', PostsListView.as_view(), name='list'), # that is, with URL http://site_name/blog/
                                                  # a list of posts will be displayed
url(r'^(?P<pk>\d+)/$', PostDetailView.as_view()), # and with URL http://site_name/blog/number/
                                                  # a post with a specific number will be displayed

)

Now you need to create page templates. By default, for the PostListView class, Django will search for a template in blog/templates/blog/post_list.html (such a long and strange path is associated with the logic of the framework, the developer can change this behavior, but in this article, I won’t touch on this)

Let’s create this file:

{% block content %}
    {% for post in object_list %}
        <p>{{ post.datetime }}</p>
        <h2><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h2>
        <p>{{ post.content }}</p>
    {% empty %}
    <p>No Posts</p>
    {% endfor %}

{% endblock %}

Ok, let’s try how it works by going to the URL at http://localhost:8000/blog/. If there are no errors, we will see a list of posts where the title of each post is a link. For now these links lead nowhere, we need to fix that. By default, for the PostDetailView class, the template is located in blog\templates\blog\post_detail.html.

Let’s create it:

{% block content %}
    <p>{{ post.datetime }}</p>
    <h2>{{ post.title }}</h2>
    <p>{{ post.content }}</p>
{% endblock %}

And again check: http://localhost:8000/blog/1/

We will add the ability to comment on our post. for this purpose, we will use the DISQUS services, which we will install using pip

pip install django-disqus 

This module provides comments functionality with anti-spam protection, avatars, etc., and also takes care of comment storage:

Add to post_detail.html before {% endblock %}

<p>
    {% load disqus_tags %}
    {% disqus_dev %}
    {% disqus_show_comments %}
</p>

In INSTALLED_APPS, in settings.py add ‘disqus’

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.admin',
    'blog',
    'disqus',
)

And also add to settings.py

DISQUS_API_KEY = '***'
DISQUS_WEBSITE_SHORTNAME = '***'

The last two values are obtained by registering on http://disqus.com.

Test the project in the browser. Great, the functionality of our app is impressive, but we need to do something about the design. The easiest, and at the same time modern, option is to use Twitter Bootstrap.

Download the archive http://twitter.github.io/bootstrap/assets/bootstrap.zip and unzip it to the static directory of our project (I mean ~/projects/mysite/static – create it)

Edit settings.py so that Django knows where to look for static pages.

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

Create a blog/templates/blog/base.html with the following content

<!DOCTYPE html>
<html lang="ru">
    <head>
        <meta charset="utf-8" />
        <title>MyBlog</title>
        <link href="{{STATIC_URL}}bootstrap/css/bootstrap.css" rel="stylesheet">
        <style>
            body {
                padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
            }
        </style>
        <link href="{{STATIC_URL}}bootstrap/css/bootstrap-responsive.css" rel="stylesheet">
        <!--[if lt IE 9]>
        <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
        <![endif]-->
        <script src="{{STATIC_URL}}bootstrap/js/bootstrap.js" type="text/javascript"></script>
        {% block extrahead %}
        {% endblock %}
        <script type="text/javascript">
        $(function(){
        {% block jquery %}
        {% endblock %}
        });
        </script>
    </head>
<body>

<div class="navbar navbar-inverse navbar-fixed-top">
    <div class="navbar-inner">
        <div class="container">
            <div class="brand">My Blog</div>
            <ul class="nav">
                <li><a href="{% url 'list' %}" class="">List of posts</a></li>
            </ul>
        </div>
    </div>

</div>

<div class="container">
     {% block content %}Empty page{% endblock %}
</div> <!-- container -->

</body>
</html>

This is the basic template for our pages, include it in our post_list.html and post_detail.html by adding this first line into them

{% extends 'blog/base.html' %}

Check that everything works. Now that the design is set, you can start deploying the app on a free cloud hosting service.

Register a free N00b account on PythonAnywhere. I like this service for ease of installation of Django. Everything happens almost the same as on the local computer.

Let’s say we created a user in PythonAnywhere with the name djangotest, then our application will be located at djangotest.pythonanywhere.com. Note: replace ‘djangotest’ with your PythonAnywhere username everywhere in the text below.

Change in settings.py

DEBUG = False

and add

ALLOWED_HOSTS = ['djangotest.pythonanywhere.com']

Upload files to the host in any of the possible ways.

In my opinion, for an inexperienced user, the easiest way is to archive the project folder, upload the archive to the server (in the Files->Upload a file section) and unzip it on the server using the command in the bash shell (in the Consoles -> bash Section):

For example, if we upload mysite.tar.gz, run this in the PythonAnywhere console

tar -zxvf mysite.tar.gz

Now we configure the working environment on the server, run this in the PythonAnywhere console:

virtualenv env

source env/bin/activate

pip install django django-disqus

Configure static pages in the Web -> Static files section:

The first line — the place where bootstrap is, in the second – static files of the built-in Django admin panel.

Configure WSGI (Web -> It is configured via a WSGI file stored at: …):

activate_this = '/home/djangotest/env/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))

import os
import sys

path = '/home/djangotest/mysite'
if path not in sys.path:
    sys.path.append(path)
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'

import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

Click the button Web -> Reload djangotest.pythonanywhere.com

Go to djangotest.pythonanywere.com/blog / – congratulations, it wasn’t easy, but You did it. Now You have your own cozy blog, developed with your own hands on the most modern web technologies!


Written by Станислав Фатеев, translated from https://habr.com/en/post/181556/