Building Your Live Site: Menus, Styles, and Blog Functionality (Part 5)

Building Your Live Site: Menus, Styles, and Blog Functionality (Part 5)

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:

  1. Creating a navigation menu to link your pages together
  2. Introducing Django’s template inheritance system for consistent styling
  3. 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>© 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 in urls.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 %}
&lt;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 %}
&lt;h1>Blog&lt;/h1>
{% for post in posts %}
&lt;article>
    &lt;h2>{{ post.title }}&lt;/h2>
    &lt;p>by {{ post.author }} on {{ post.created_at|date:"F d, Y" }}&lt;/p>
    &lt;p>{{ post.body|truncatewords:50 }}&lt;/p>
&lt;/article>
&lt;hr>
{% empty %}
&lt;p>No blog posts have been written yet.&lt;/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.


Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *