Lecture: Adding Posts and Tags to your Blog Structure

July 7, 2026, 4 p.m. (America/Los_Angeles)

In this lecture we add the structure to our site to display and work with two new data-types: posts and tags. We go through the changes required in each file. We also add some helper functions to make managing your site easier. Finally we test our work by creating posts and tags to see how it displays.

Lecture Preview

Content

Coding up Your Initial Markdown Site

Now that you have created an initial working project and app, it's time to add the contents that will allow you to work with and display markdown content in Django. This lecture will walk you through what to change and add, while the next few lectures will explain what we did and why.

Django Relationships

flowchart TB A[models.py] B[views.py] C[urls.py -- app] G[urls.py -- project] E[html templates] D[web request] F[web response] D-->G G-->C A-->B C-->B E-->B B-->F

What are Models?

  • Starting point for adding new functionality
  • Define the essential structure of your database
  • ...but declared in a "Pythonic" way using classes, inheritance, etc
  • Best of both worlds?

Adding our First Models

We will now update the models file, adding two new model types: Tags and Posts. Open up homepage/blog/models.py. It should look something like this:

from django.db import models

Replace that text with the following:

from django.db import models
from django.urls import reverse 
from django.conf import settings
from django.contrib.auth.models import User

class Tag(models.Model):

    name  = models.CharField(max_length=200, unique=True)

    def __str__(self):
        return str(self.name)

class Post(models.Model):
    title = models.CharField(max_length=200,unique=True)
    author = models.ForeignKey(User, null=True, on_delete= models.SET_NULL)
    tags = models.ManyToManyField(Tag,blank=True)
    content = models.TextField(blank=True,null=True)

    class Meta:
        ordering = ["title"]

    def __str__(self):
        return str(self.title)

    def get_absolute_url(self):
        return reverse('post', args=[str(self.pk)])

Adding administration

go to homepage/blog/admin.py and replace the existing text with the following:

from django.contrib import admin
from django.http import HttpResponseRedirect
from django.urls import path
from django.conf import settings

from .models import Post
from .models import Tag

class TagAdmin(admin.ModelAdmin):
    list_display = ['name'] 
admin.site.register(Tag,TagAdmin)

class PostAdmin(admin.ModelAdmin):
    pass
admin.site.register(Post, PostAdmin)

Implementing render functionality in views.py

So let's go to homepage/blog/views.py and replace the text with:

from django.views import generic
from .models import Post
from django.template.response import TemplateResponse


def index(request, *args, **kwargs):
    context = {}
    return TemplateResponse(request, "index.html", context)


class PostListView(generic.ListView):
    model = Post
    ordering = ["title"]
    # template_name = "blog/post_list.html"

class PostDetailView(generic.DetailView):
    model = Post
    # template_name = "blog/post_detail.html"

Implementing url mappings

Our next task will be to connect the data and rendering functionality contained in our post model and tag model to the ways we will be interacting with that data through the external website and the admin site.

First, let's go to homepage/blog/urls.py and look at the few lines that were already there.

Replace that with the following:

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
    path('posts/', views.PostListView.as_view(), name='posts'),    
    path('post/<int:pk>', views.PostDetailView.as_view(), name='post'),
    ]

Adding templates

We need to add a link to our new list of posts

to homepage/blog/templates/base_generic.html add the following line right below the line with <body> in it (and above <main class="container">)

<a href="{% url 'posts' %}">Posts</a>

Create the folder homepage/blog/templates/blog/ as well as a child file homepage/blog/templates/blog/post_list.html and paste in:

{% extends "base_generic.html" %}

{% block content %}
  <h1>Posts</h1>
  {% if post_list %}
    <ul>
    {% for post in post_list %}
      <li><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></li>
    {% endfor %}
    </ul>
  {% else %}
    <p>There are no posts in the blog.</p>
  {% endif %}
{% endblock %}

Create homepage/blog/templates/blog/post_detail.html and paste in:

{% extends "base_generic.html" %}

{% block title %}
  {% if post.title %}
    <title>{{post.title}} | DanAukes.com</title>
  {% else %}
    <title>DanAukes.com</title>
  {% endif %}
{% endblock %}

{% block content %}
  <article>
    <h1>{{post.title}}</h1>
    <hr/>
    <div class="markdown">

      {{ post.content | safe}}

    </div>
  </article>
{% endblock %}

Helper Commands

Next we're going to define a couple scripts that help us. In the terminal, navigate to the homepage/ directory and create a new file called startup (no file extension).

cd homepage
nano startup

Paste in the following (including the first, commented out line), then save and close your file editor:

#!/usr/bin/bash
python3 manage.py makemigrations
python3 manage.py migrate
python3 manage.py migrate --run-syncdb
python manage.py createsuperuser

Back in the terminal, make the script executable:

chmod +x startup

Repeat the same for a new file named migrate, pasting in the following code:

#!/usr/bin/bash
python3 manage.py makemigrations
python3 manage.py migrate

don't forget to add executible permission:

chmod +x migrate

Finally, repeat the same for a new filenamed run, pasting in the following code:

#!/usr/bin/bash
python3 manage.py runserver 0.0.0.0:8000

don't forget to add executible permission:

chmod +x run

With those three files you can initialize your database, migrate, and then run your server. from terminal, navigate to homepage and then run the startup script

cd homepage
./startup

Follow the directions to create an initial superuser, or website administrator. With your databases initialized, you can run Django's test server:

./run

Finally, navigate to your website, which will be located at http://localhost:8000. You should see:

home page

Clicking on the posts link should get you to:

empty post list

Create your first Post

This Content is Locked

The rest of the text is available but you have to be signed in to view it. Please create an account or sign in to continue...