django blog app construction

last updated: 02 Dec 2020

table of contents

overview

construction process

process overview

install django

pip3 install django
python3 -m django --version

create new project

django-admin startproject django_blog_app
cd django_blog_app
python virtual environment for django app
Django Development Server Launch Screen
Note the parenthesis entity at the left-end offsetting the CLI prompt

run dev-server

python3 manage.py runserver 6500
# default port is 8000
Django Development Server Launch Screen
Django Development Server Launch Screen @ localhost:6500

initialize apps

initialize the blog app
python3 manage.py startapp blog
initialize the user app
python3 manage.py startapp users
Django Blog Structure
Bird's Eye View of the Django Blog App
register both apps

setup ORM

Django Blog Structure
Django Admin Login Page
database migrations

the very first DB migration creates the DB and adds a few default tables

python3 manage.py makemigrations
python3 manage.py migrate
create superuser
resetting superuser password from CLI (if password is forgotten)
python3 manage.py shell
setup models (M of M-V-T)

Post model:

Profile model:

register with admin page
updating DB
django signals

the view-template system (V-T of M-V-T)

App Routes Needed
Routes Needed for this Blog App
(suggested order of creation in the right)
blog home page setup

routing

Bootstrap Base Template
Bootstrap Base Template
set up crispy-forms

registration page setup

form-view

routing

template

setting up flash messages

view

template

login and logout system

routing/view

template

setup login redirect
setup logout flow

routing

template

template

login/logout status in the navbar

template

user profile

routing

view

template

template

image storage system

routing

model

user-profile update system

form/view

view

template

blog pages (C-R-U-D)

blog post details

routing

view

template

create and update blog post

view

# blog/views.py

...
from django.views.generic import (
    ListView,
    DetailView,
    CreateView,
    UpdateView,
)
from djnago.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin

# Create your views here.
...

# create view
class PostCreateView(LoginRequiredMixin, CreateView):
    model = Post
    fields = [
        'title',
        'content',
        ]

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)

# update view
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
    model = Post
    fields = [
        'title',
        'content',
    ]

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)

    def test_func(self):
        post = self.get_object()
        if self.request.user == post.author:
            return True
        else: return False

routing

# blog/urls.py

...
from .views import PostListView, PostCreateView, PostDetailView, PostUpdateView

urlpatterns = [
    ...
    path('post/new/', PostCreateView.as_view(), name='post_create'),
    path('post/<int:pk>/update', PostUpdateView.as_view(), name='post-update'),
]

template

redirection

blog list view

routing

view

template

{% extends "base.html" %}

{% block content %}

    {% for post in posts %}

    <div class="card m-5">

        <div class="card-title m-3">

            <a href="{% url 'post-detail' post.id %}" > <h1> {{ post.title }}  </h1> </a>
            <p> {{ post.author }} </p>

        </div>

        <div class="card-body">

            <p> {{ post.content }} </p>

        </div>
        

    </div>
    
    {% endfor %}

{% endblock content %}
delete blog post

routing

# blog/urls.py

...
from .views import PostListView, PostCreateView, PostDetailView, PostUpdateView, PostDeleteView

urlpatterns = [
    ...
    path('post/<int:pk>/delete', PostDeleteView.as_view(), name='post-delete'),
]

view

# blog/views.py

...
from django.views.generic import (
    ListView,
    DetailView,
    CreateView,
    UpdateView,
    DeleteView,
)
...

...
# delete view 
class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
    model = Post
    success_url = '/'

    def test_func(self):
      post = self.get_object()
      if self.request.user == post.author:
          return True
      else: return False

template

pagination

add pagination navigation buttons

filtering

view

routing

# blog/urls.py

...
from .views import PostListView, ..., UserPostListView

urlpatterns = [
    ...
    path('user/<str:username>/', UserPostListView.as_view(), name='user-posts'),
]

template

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

{% block content %}

    <h1> Posts by {{view.kwargs.username}} </h1>
    <p> ({{page_obj.paginator.count }} posts by this user) </p>

    {% for post in posts %}

        <article class="media content-section">
            <img class="rounded-circle" src="{{post.author.profile.image.url}}" style="max-width: 90px; margin: 3px">
            <div class="media-body">
                
                <div class="article-metadata">
                    <a class="mr-2" href="{% url 'user-posts' post.author.username %}">{{ post.author }}</a>
                    <small class="text-muted">{{ post.date_posted | date:"Y, F d" }}</small>
                </div>

                <h2><a class="article-title" href="{% url 'post-detail' post.id %}">{{ post.title }}</a></h2>
                
                <p class="article-content">{{ post.content }}</p>

            </div>

        </article>

    {% endfor %}

    {% if is_paginated %}

        {% if page_obj.has_previous %}
            <a class="btn btn-outline-info" href="?page=1">First</a>
            <a class="btn btn-outline-info" href="?page={{page_obj.previous_page_number}}">Previous</a>
        {% endif %}

        {% for num in page_obj.paginator.page_range %}
                
            {% if page_obj.number == num %}
                <a class="btn btn-info" href="?page={{num}}"> {{num}} </a>
            {% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
                <a class="btn btn-outline-info" href="?page={{num}}"> {{num}} </a>
            {% endif %}

        {% endfor %}

        {% if page_obj.has_previous %}
            <a class="btn btn-outline-info" href="?page={{page_obj.next_page_number}}">Next</a>
            <a class="btn btn-outline-info" href="?page={{page_obj.previous_page_number}}">Last</a>
        {% endif %}

    {% endif %}

{% endblock content %}

password reset email

password reset page

routing/view

template

password reset confirm page

routing/view

template

password reset done page

routing/view

template

smtp config in settings.py

ethereal

gmail

password reset complete

routing/view

template

heroku deployment

initial setup

docker setup

create django dockerfle

vscode workflow

manual workflow

image creation from Dockerfile

container deployment from image

access the app
stop the docker container

additional docker commands

further reading

django-SQL


created: 08 Nov 2020
today's track: Virkelighetens Etterklang by Kalandra