Templates and Presentation Layer

Template Inheritance

Template inheritance is one of Django's most powerful features, allowing you to build a base "skeleton" template that contains common elements and define blocks that child templates can override. This promotes code reuse, maintainability, and consistent design across your application.

Template Inheritance

Template inheritance is one of Django's most powerful features, allowing you to build a base "skeleton" template that contains common elements and define blocks that child templates can override. This promotes code reuse, maintainability, and consistent design across your application.

Understanding Template Inheritance

The Concept

Template inheritance works like class inheritance in object-oriented programming:

  • Base templates define the overall structure and common elements
  • Child templates extend base templates and override specific blocks
  • Multiple inheritance levels allow for sophisticated template hierarchies

Benefits

  • DRY Principle - Don't repeat common HTML structure
  • Consistency - Maintain uniform design across pages
  • Maintainability - Change common elements in one place
  • Flexibility - Override specific sections as needed
  • Scalability - Easy to add new pages with consistent structure

Basic Template Inheritance

Creating a Base Template

<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}My Website{% endblock %}</title>
    
    <!-- Common CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    {% load static %}
    <link rel="stylesheet" href="{% static 'css/main.css' %}">
    
    <!-- Additional CSS block -->
    {% block extra_css %}{% endblock %}
</head>
<body>
    <!-- Header -->
    <header class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-brand" href="{% url 'home' %}">
                {% block site_name %}My Website{% endblock %}
            </a>
            
            <nav class="navbar-nav">
                {% block navigation %}
                    <a class="nav-link" href="{% url 'home' %}">Home</a>
                    <a class="nav-link" href="{% url 'about' %}">About</a>
                    <a class="nav-link" href="{% url 'contact' %}">Contact</a>
                {% endblock %}
            </nav>
            
            <div class="navbar-nav ms-auto">
                {% block user_nav %}
                    {% if user.is_authenticated %}
                        <span class="nav-link">Hello, {{ user.username }}!</span>
                        <a class="nav-link" href="{% url 'logout' %}">Logout</a>
                    {% else %}
                        <a class="nav-link" href="{% url 'login' %}">Login</a>
                        <a class="nav-link" href="{% url 'signup' %}">Sign Up</a>
                    {% endif %}
                {% endblock %}
            </div>
        </div>
    </header>

    <!-- Messages -->
    {% if messages %}
        <div class="container mt-3">
            {% for message in messages %}
                <div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
                    {{ message }}
                    <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
                </div>
            {% endfor %}
        </div>
    {% endif %}

    <!-- Main Content -->
    <main class="container my-4">
        {% block content %}
            <h1>Welcome to My Website</h1>
            <p>This is the default content.</p>
        {% endblock %}
    </main>

    <!-- Footer -->
    <footer class="bg-dark text-light py-4 mt-5">
        <div class="container">
            {% block footer %}
                <div class="row">
                    <div class="col-md-6">
                        <p>&copy; 2024 My Website. All rights reserved.</p>
                    </div>
                    <div class="col-md-6 text-end">
                        <p>Built with Django</p>
                    </div>
                </div>
            {% endblock %}
        </div>
    </footer>

    <!-- JavaScript -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    {% block extra_js %}{% endblock %}
</body>
</html>

Extending the Base Template

<!-- blog/templates/blog/post_list.html -->
{% extends 'base.html' %}
{% load static %}

{% block title %}Blog Posts - {{ block.super }}{% endblock %}

{% block extra_css %}
    <link rel="stylesheet" href="{% static 'blog/css/blog.css' %}">
{% endblock %}

{% block content %}
    <div class="row">
        <div class="col-lg-8">
            <h1>Latest Blog Posts</h1>
            
            {% for post in posts %}
                <article class="card mb-4">
                    {% if post.featured_image %}
                        <img src="{{ post.featured_image.url }}" class="card-img-top" alt="{{ post.title }}">
                    {% endif %}
                    
                    <div class="card-body">
                        <h2 class="card-title">
                            <a href="{{ post.get_absolute_url }}" class="text-decoration-none">
                                {{ post.title }}
                            </a>
                        </h2>
                        
                        <p class="card-text text-muted">
                            <small>
                                By {{ post.author.get_full_name|default:post.author.username }}
                                on {{ post.created_at|date:"F d, Y" }}
                                in {{ post.category.name }}
                            </small>
                        </p>
                        
                        <p class="card-text">{{ post.excerpt|default:post.content|truncatewords:30 }}</p>
                        
                        <a href="{{ post.get_absolute_url }}" class="btn btn-primary">Read More</a>
                    </div>
                </article>
            {% empty %}
                <div class="alert alert-info">
                    <h4>No posts yet</h4>
                    <p>Check back later for new content!</p>
                </div>
            {% endfor %}
            
            <!-- Pagination -->
            {% if is_paginated %}
                <nav aria-label="Blog pagination">
                    <ul class="pagination justify-content-center">
                        {% if page_obj.has_previous %}
                            <li class="page-item">
                                <a class="page-link" href="?page=1">&laquo; First</a>
                            </li>
                            <li class="page-item">
                                <a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a>
                            </li>
                        {% endif %}
                        
                        <li class="page-item active">
                            <span class="page-link">
                                Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
                            </span>
                        </li>
                        
                        {% if page_obj.has_next %}
                            <li class="page-item">
                                <a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a>
                            </li>
                            <li class="page-item">
                                <a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">Last &raquo;</a>
                            </li>
                        {% endif %}
                    </ul>
                </nav>
            {% endif %}
        </div>
        
        <div class="col-lg-4">
            {% include 'blog/includes/sidebar.html' %}
        </div>
    </div>
{% endblock %}

{% block extra_js %}
    <script src="{% static 'blog/js/blog.js' %}"></script>
{% endblock %}

Advanced Inheritance Patterns

Multi-Level Inheritance

<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}My Site{% endblock %}</title>
    {% block extra_css %}{% endblock %}
</head>
<body>
    <header>{% block header %}{% endblock %}</header>
    <main>{% block content %}{% endblock %}</main>
    <footer>{% block footer %}{% endblock %}</footer>
    {% block extra_js %}{% endblock %}
</body>
</html>

<!-- templates/blog/base_blog.html -->
{% extends 'base.html' %}
{% load static %}

{% block title %}Blog - {{ block.super }}{% endblock %}

{% block extra_css %}
    {{ block.super }}
    <link rel="stylesheet" href="{% static 'blog/css/blog.css' %}">
{% endblock %}

{% block header %}
    <nav class="blog-nav">
        <a href="{% url 'blog:home' %}">Blog Home</a>
        <a href="{% url 'blog:categories' %}">Categories</a>
        <a href="{% url 'blog:archive' %}">Archive</a>
    </nav>
{% endblock %}

{% block content %}
    <div class="blog-container">
        <aside class="sidebar">
            {% block sidebar %}
                {% include 'blog/includes/sidebar.html' %}
            {% endblock %}
        </aside>
        
        <section class="main-content">
            {% block blog_content %}{% endblock %}
        </section>
    </div>
{% endblock %}

<!-- blog/templates/blog/post_detail.html -->
{% extends 'blog/base_blog.html' %}

{% block title %}{{ post.title }} - {{ block.super }}{% endblock %}

{% block blog_content %}
    <article class="post-detail">
        <header>
            <h1>{{ post.title }}</h1>
            <div class="post-meta">
                <span>By {{ post.author.get_full_name }}</span>
                <time datetime="{{ post.created_at|date:'c' }}">
                    {{ post.created_at|date:"F d, Y" }}
                </time>
            </div>
        </header>
        
        <div class="post-content">
            {{ post.content|linebreaks }}
        </div>
        
        <footer class="post-footer">
            {% include 'blog/includes/post_tags.html' %}
            {% include 'blog/includes/share_buttons.html' %}
        </footer>
    </article>
    
    {% include 'blog/includes/comments.html' %}
{% endblock %}

Conditional Inheritance

<!-- Dynamic base template selection -->
{% if request.user.is_staff %}
    {% extends 'admin_base.html' %}
{% else %}
    {% extends 'base.html' %}
{% endif %}

{% block content %}
    <!-- Content that works with both base templates -->
{% endblock %}
# views.py - Dynamic template selection
def post_detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    
    # Choose template based on user or post type
    if request.user.is_staff:
        template_name = 'blog/admin_post_detail.html'
    elif post.is_premium:
        template_name = 'blog/premium_post_detail.html'
    else:
        template_name = 'blog/post_detail.html'
    
    return render(request, template_name, {'post': post})

Block Types and Patterns

Content Blocks

<!-- Base template with various block types -->
{% block page_header %}
    <h1>Default Page Header</h1>
{% endblock %}

{% block breadcrumbs %}
    <nav aria-label="breadcrumb">
        <ol class="breadcrumb">
            <li class="breadcrumb-item"><a href="{% url 'home' %}">Home</a></li>
            {% block breadcrumb_items %}{% endblock %}
        </ol>
    </nav>
{% endblock %}

{% block main_content %}
    <div class="row">
        <div class="col-md-8">
            {% block primary_content %}{% endblock %}
        </div>
        <div class="col-md-4">
            {% block sidebar_content %}
                {% include 'includes/default_sidebar.html' %}
            {% endblock %}
        </div>
    </div>
{% endblock %}

Asset Blocks

<!-- CSS blocks for different loading strategies -->
{% block css %}
    <!-- Critical CSS -->
    <style>
        {% block critical_css %}
            body { font-family: Arial, sans-serif; }
        {% endblock %}
    </style>
    
    <!-- External CSS -->
    {% block external_css %}
        <link rel="stylesheet" href="{% static 'css/main.css' %}">
    {% endblock %}
    
    <!-- Page-specific CSS -->
    {% block page_css %}{% endblock %}
{% endblock %}

<!-- JavaScript blocks -->
{% block js %}
    <!-- Core JavaScript -->
    {% block core_js %}
        <script src="{% static 'js/main.js' %}"></script>
    {% endblock %}
    
    <!-- Page-specific JavaScript -->
    {% block page_js %}{% endblock %}
    
    <!-- Inline JavaScript -->
    {% block inline_js %}{% endblock %}
{% endblock %}

Meta and SEO Blocks

<!-- SEO-friendly base template -->
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
    <!-- Title and description -->
    <title>{% block title %}{% endblock %}{% block title_suffix %} - My Website{% endblock %}</title>
    <meta name="description" content="{% block meta_description %}Default site description{% endblock %}">
    
    <!-- Open Graph tags -->
    <meta property="og:title" content="{% block og_title %}{% block title %}{% endblock %}{% endblock %}">
    <meta property="og:description" content="{% block og_description %}{% block meta_description %}{% endblock %}{% endblock %}">
    <meta property="og:image" content="{% block og_image %}{% static 'images/default-og.jpg' %}{% endblock %}">
    <meta property="og:url" content="{% block og_url %}{{ request.build_absolute_uri }}{% endblock %}">
    
    <!-- Twitter Card tags -->
    <meta name="twitter:card" content="{% block twitter_card %}summary{% endblock %}">
    <meta name="twitter:title" content="{% block twitter_title %}{% block og_title %}{% endblock %}{% endblock %}">
    <meta name="twitter:description" content="{% block twitter_description %}{% block og_description %}{% endblock %}{% endblock %}">
    
    <!-- Canonical URL -->
    <link rel="canonical" href="{% block canonical_url %}{{ request.build_absolute_uri }}{% endblock %}">
    
    <!-- Additional meta tags -->
    {% block extra_meta %}{% endblock %}
</head>

Using SEO Blocks in Child Templates

<!-- blog/post_detail.html -->
{% extends 'base.html' %}

{% block title %}{{ post.title }}{% endblock %}
{% block meta_description %}{{ post.excerpt|default:post.content|striptags|truncatewords:25 }}{% endblock %}

{% block og_title %}{{ post.title }}{% endblock %}
{% block og_description %}{{ post.excerpt|striptags }}{% endblock %}
{% block og_image %}
    {% if post.featured_image %}
        {{ post.featured_image.url }}
    {% else %}
        {{ block.super }}
    {% endif %}
{% endblock %}

{% block extra_meta %}
    <meta name="author" content="{{ post.author.get_full_name }}">
    <meta name="article:published_time" content="{{ post.created_at|date:'c' }}">
    <meta name="article:modified_time" content="{{ post.updated_at|date:'c' }}">
    {% for tag in post.tags.all %}
        <meta name="article:tag" content="{{ tag.name }}">
    {% endfor %}
{% endblock %}

Block Manipulation Techniques

Using block.super

<!-- Base template -->
{% block navigation %}
    <a href="{% url 'home' %}">Home</a>
    <a href="{% url 'about' %}">About</a>
{% endblock %}

<!-- Child template - Add to existing content -->
{% block navigation %}
    {{ block.super }}
    <a href="{% url 'blog:home' %}">Blog</a>
    <a href="{% url 'contact' %}">Contact</a>
{% endblock %}

<!-- Result: Home | About | Blog | Contact -->

Conditional Block Content

<!-- Base template with conditional blocks -->
{% block sidebar %}
    {% if show_sidebar %}
        <aside class="sidebar">
            {% block sidebar_content %}
                <h3>Default Sidebar</h3>
                <p>Default sidebar content</p>
            {% endblock %}
        </aside>
    {% endif %}
{% endblock %}

<!-- Child template -->
{% block sidebar %}
    {% with show_sidebar=True %}
        {{ block.super }}
    {% endwith %}
{% endblock %}

{% block sidebar_content %}
    <h3>Custom Sidebar</h3>
    <ul>
        <li><a href="{% url 'blog:recent' %}">Recent Posts</a></li>
        <li><a href="{% url 'blog:popular' %}">Popular Posts</a></li>
    </ul>
{% endblock %}

Empty Blocks and Defaults

<!-- Base template with optional blocks -->
{% block alert %}{% endblock %}

{% block content %}
    {% block page_title %}
        <h1>{% block title %}Default Title{% endblock %}</h1>
    {% endblock %}
    
    {% block main_content %}
        <p>Default content goes here.</p>
    {% endblock %}
{% endblock %}

<!-- Child template -->
{% block alert %}
    <div class="alert alert-info">
        <strong>Info:</strong> This is important information.
    </div>
{% endblock %}

{% block title %}Custom Page Title{% endblock %}

{% block main_content %}
    <p>This is custom content that replaces the default.</p>
{% endblock %}

Template Organization Strategies

App-Specific Base Templates

templates/
├── base.html                    # Site-wide base
├── base_admin.html             # Admin base
├── base_public.html            # Public site base
├── blog/
│   ├── base_blog.html          # Blog section base
│   ├── post_list.html
│   ├── post_detail.html
│   └── includes/
│       ├── post_card.html
│       └── sidebar.html
├── shop/
│   ├── base_shop.html          # Shop section base
│   ├── product_list.html
│   └── product_detail.html
└── includes/
    ├── header.html
    ├── footer.html
    └── navigation.html

Feature-Based Templates

<!-- templates/features/comments_base.html -->
{% extends 'base.html' %}

{% block extra_css %}
    {{ block.super }}
    <link rel="stylesheet" href="{% static 'css/comments.css' %}">
{% endblock %}

{% block content %}
    {{ block.super }}
    
    {% block comments_section %}
        {% if object.comments_enabled %}
            <section class="comments">
                <h3>Comments</h3>
                {% include 'features/comments_list.html' %}
                {% include 'features/comment_form.html' %}
            </section>
        {% endif %}
    {% endblock %}
{% endblock %}

<!-- Any template that needs comments -->
{% extends 'features/comments_base.html' %}

{% block content %}
    <!-- Main content -->
    <article>{{ post.content }}</article>
    
    <!-- Comments will be automatically included -->
{% endblock %}

Performance Considerations

Template Caching with Inheritance

# settings.py - Enable template caching
TEMPLATES = [{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [BASE_DIR / 'templates'],
    'OPTIONS': {
        'loaders': [
            ('django.template.loaders.cached.Loader', [
                'django.template.loaders.filesystem.Loader',
                'django.template.loaders.app_directories.Loader',
            ]),
        ],
    },
}]

Efficient Block Structure

<!-- Efficient: Specific blocks for targeted overrides -->
{% block page_specific_css %}{% endblock %}
{% block page_specific_js %}{% endblock %}

<!-- Less efficient: Large blocks that require full override -->
{% block all_css %}
    <!-- 50 lines of CSS that child templates must duplicate -->
{% endblock %}

Lazy Loading Patterns

<!-- Base template with lazy loading -->
{% block deferred_content %}
    <div id="deferred-content" data-url="{% url 'ajax_content' %}">
        <div class="loading">Loading...</div>
    </div>
{% endblock %}

{% block extra_js %}
    <script>
        // Load deferred content via AJAX
        document.addEventListener('DOMContentLoaded', function() {
            const container = document.getElementById('deferred-content');
            if (container) {
                fetch(container.dataset.url)
                    .then(response => response.text())
                    .then(html => container.innerHTML = html);
            }
        });
    </script>
{% endblock %}

Best Practices

Block Naming Conventions

<!-- Use descriptive, hierarchical names -->
{% block page_title %}{% endblock %}
{% block page_subtitle %}{% endblock %}
{% block page_content %}{% endblock %}

{% block sidebar_primary %}{% endblock %}
{% block sidebar_secondary %}{% endblock %}

{% block footer_links %}{% endblock %}
{% block footer_copyright %}{% endblock %}

<!-- Avoid generic names -->
{% block content1 %}{% endblock %}  <!-- Bad -->
{% block stuff %}{% endblock %}     <!-- Bad -->
{% block div %}{% endblock %}       <!-- Bad -->

Documentation in Templates

<!-- Document complex inheritance structures -->
{# 
    Base template for all blog pages
    
    Blocks available for override:
    - blog_title: Page title for blog section
    - blog_content: Main blog content area
    - blog_sidebar: Blog-specific sidebar content
    - blog_meta: SEO meta tags for blog pages
#}

{% extends 'base.html' %}

{% block title %}{% block blog_title %}Blog{% endblock %} - {{ block.super }}{% endblock %}

{% block content %}
    <div class="blog-layout">
        <main class="blog-main">
            {% block blog_content %}{% endblock %}
        </main>
        
        <aside class="blog-sidebar">
            {% block blog_sidebar %}
                {% include 'blog/includes/default_sidebar.html' %}
            {% endblock %}
        </aside>
    </div>
{% endblock %}

Testing Template Inheritance

# tests.py
from django.test import TestCase
from django.template.loader import render_to_string

class TemplateInheritanceTests(TestCase):
    def test_base_template_blocks(self):
        """Test that base template renders with default content."""
        html = render_to_string('base.html', {})
        self.assertIn('<title>My Website</title>', html)
        self.assertIn('Welcome to My Website', html)
    
    def test_child_template_inheritance(self):
        """Test that child template properly extends base."""
        context = {'post': {'title': 'Test Post', 'content': 'Test content'}}
        html = render_to_string('blog/post_detail.html', context)
        
        # Should include base template elements
        self.assertIn('My Website', html)
        
        # Should include child template content
        self.assertIn('Test Post', html)
        self.assertIn('Test content', html)
    
    def test_block_super_functionality(self):
        """Test that block.super works correctly."""
        html = render_to_string('blog/post_list.html', {})
        
        # Should include both base and child navigation
        self.assertIn('Home', html)  # From base
        self.assertIn('Blog', html)  # From child

Template inheritance is a cornerstone of maintainable Django applications. By mastering these patterns and techniques, you can create flexible, reusable template structures that scale with your application's complexity while maintaining clean, DRY code.