In Part 4, you transformed a “Not Found” error into a set of live, accessible pages on your Heroku-deployed Django application. You now have a homepage, an about page, and a contact page.
While functional, your site is still a collection of disconnected pages lacking a professional look and feel. It’s time to tie everything together, add some style, and build out the core feature: the blog itself.
This guide—Part 5 of our series—walks you through three crucial enhancements:
- Creating a navigation menu to link your pages together
- Introducing Django’s template inheritance system for consistent styling
- Laying the groundwork for blog functionality using models and views
Step 1: Create a Navigation Menu and Link Your Pages
A website isn’t very useful if users can’t navigate between pages. A navigation menu is the first step toward creating a cohesive user experience. We will add a simple menu to a shared header across all pages.
The best way to do this is by creating a single, reusable template that all other templates can extend—this is known as template inheritance.
Create a Base Template
Inside your main/templates/main directory, create a new file named base.html. This file will contain the foundational HTML structure for your entire site, including the navigation menu.
main/templates/main/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Django Blog</title>
</head>
<body>
<header>
<nav>
<ul>
<li><a href="{% url 'home' %}">Home</a></li>
<li><a href="{% url 'about' %}">About</a></li>
<li><a href="{% url 'contact' %}">Contact</a></li>
<!-- We'll add the blog link later -->
</ul>
</nav>
</header>
<main>
{% block content %}
<!-- The content of other templates will go here -->
{% endblock %}
</main>
<footer>
<p>&copy; 2025 My Awesome Blog. All Rights Reserved.</p>
</footer>
</body>
</html>
Key Django Template Tags Explained
{% url 'page-name' %}
Dynamically generates a URL based on the name defined inurls.py. This avoids hardcoding paths and makes future changes easier.{% block content %}/{% endblock %}
Defines a replaceable section that child templates override. This is the core of template inheritance.
Update Existing Page Templates
Modify your existing templates so they extend the new base template.
main/templates/main/home.html
{% extends "main/base.html" %}
{% block content %}
<h1>Homepage</h1>
<p>Welcome! Your Django site is now live on Heroku.</p>
{% endblock %}
main/templates/main/about.html
{% extends "main/base.html" %}
{% block content %}
<h1>About Page</h1>
<p>This is the page that tells visitors about you or your project.</p>
{% endblock %}
main/templates/main/contact.html
{% extends "main/base.html" %}
{% block content %}
<h1>Contact Page</h1>
<p>Here's how to get in touch.</p>
{% endblock %}
After deploying, every page now shares the same header and footer.
Step 2: Style Your Pages with CSS
A plain HTML site works—but it’s not engaging. Let’s add basic CSS using Django’s staticfiles app and whitenoise.
Create the Static Directory
From the project root (next to manage.py):
myproject/
├── static/
│ └── css/
│ └── style.css
├── manage.py
Create Your Stylesheet
static/css/style.css
body {
font-family: sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
background-color: #f4f4f4;
color: #333;
}
header {
background: #333;
color: #fff;
padding: 1rem 0;
text-align: center;
}
header nav ul {
padding: 0;
list-style: none;
}
header nav ul li {
display: inline;
margin: 0 15px;
}
header a {
color: #fff;
text-decoration: none;
}
main {
padding: 20px;
max-width: 800px;
margin: 20px auto;
background: #fff;
border-radius: 5px;
}
footer {
text-align: center;
padding: 20px;
margin-top: 40px;
color: #777;
}
Link the Stylesheet in base.html
Add the following inside the <head> tag:
{% load static %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
Configure Static Files in settings.py
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
Push the changes to Heroku and run:
heroku run python manage.py collectstatic
Your site should now appear styled.
Step 3: Build Out the Blog
Now it’s time to add the blog itself.
1. Create the Blog App
python manage.py startapp blog
2. Register the App
Add 'blog' to INSTALLED_APPS in settings.py.
3. Define the Post Model
blog/models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique_for_date='publish')
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
content = models.TextField()
publish = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
objects = models.Manager()
custom_objects = models.Manager()
class Meta:
ordering = ('-publish',)
def __str__(self):
return str(self.title)
4. Run Migrations
python manage.py makemigrations
python manage.py migrate
5. Register the Model with Django Admin
blog/admin.py
from django.contrib import admin
from .models import Post
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'slug', 'author', 'publish', 'status')
list_filter = ('status', 'created', 'publish', 'author')
search_fields = ('title', 'body')
prepopulated_fields = {'slug': ('title',)}
raw_id_fields = ('author',)
date_hierarchy = 'publish'
ordering = ('status', 'publish')
6. Create Blog Views and Templates
blog/views.py
from django.shortcuts import render
from .models import Post
def post_list(request):
posts = Post.objects.all().order_by('-created')
context = {'posts': posts}
return render(request, 'blog/post_list.html', context)
blog/templates/blog/post_list.html
{% extends "main/base.html" %}
{% block content %}
<h1>Blog</h1>
{% for post in posts %}
<article>
<h2>{{ post.title }}</h2>
<p>by {{ post.author }} on {{ post.created_at|date:"F d, Y" }}</p>
<p>{{ post.body|truncatewords:50 }}</p>
</article>
<hr>
{% empty %}
<p>No blog posts have been written yet.</p>
{% endfor %}
{% endblock %}
7. Create Blog URLs
blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.post_list, name='post_list'),
]
8. Include Blog URLs in the Project
myproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')),
path('', include('main.urls')),
]
Final Touches: Deploy and Test
git add .
git commit -m "Add base template, styling, and blog app"
git push heroku main
Run migrations on Heroku if needed:
heroku run python manage.py migrate
Visit:
yourapp.herokuapp.com/blog/
Don’t forget to add the Blog link to your navigation menu.
Handling Git Push Errors Due to venv/
If a venv/ directory causes push errors:
git rm --cached -r venv/
Add to .gitignore:
venv/
Then:
git add .gitignore
git commit -m "Remove venv from tracked files and add to .gitignore"
git push heroku main
Conclusion and What’s Next
Congratulations! 🎉
You’ve built a navigable, styled Django site with dynamic content. You learned how to:
- Use template inheritance to stay DRY
- Apply CSS styling
- Build core blog functionality
Next up: Adding individual blog post detail pages so users can read full articles.






Leave a Reply