Organizing Django Templates with a Base Layout and Includes for Maintainable Web Applications
This tutorial explains how to design and structure Django templates using a base.html layout, block inheritance, and reusable includes to keep the UI organized, eliminate duplicated code, and make future maintenance of a Django web project much easier.
In this tutorial we demonstrate how to design and organize Django templates so that the UI layer of a Django application remains tidy and avoids repetitive code.
Prerequisites : Python 3.6, Django 2.2, AdminLTE 3.0.5. The goal is to arrange template files in four steps.
Step 1/4 – base.html Create a base template that holds all common fragments such as the navbar, sidebar, and footer. The template loads static files and defines blocks for title, content, and sidebar. Example skeleton:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AdminLTE 3 | Starter</title>
<!-- Font Awesome Icons -->
<link rel="stylesheet" href="{% static 'plugins/fontawesome-free/css/all.min.css' %}">
<!-- Theme style -->
<link rel="stylesheet" href="{% static 'dist/css/adminlte.min.css' %}">
<!-- Google Font: Source Sans Pro -->
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700" rel="stylesheet">
</head>
<body class="hold-transition sidebar-mini">
<div class="wrapper">
<!-- Navbar -->
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
<!-- Left navbar links -->
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" data-widget="pushmenu" href="#" role="button"><i class="fas fa-bars"></i></a></li>
<li class="nav-item d-none d-sm-inline-block"><a href="index3.html" class="nav-link">Home</a></li>
<li class="nav-item d-none d-sm-inline-block"><a href="#" class="nav-link">Contact</a></li>
</ul>
<!-- SEARCH FORM -->
<form class="form-inline ml-3">
<div class="input-group input-group-sm">
<input class="form-control form-control-navbar" type="search" placeholder="Search" aria-label="Search">
<div class="input-group-append"><button class="btn btn-navbar" type="submit"><i class="fas fa-search"></i></button></div>
</div>
</form>
<!-- Right navbar links -->
<ul class="navbar-nav ml-auto">
<!-- Messages Dropdown Menu -->
<li class="nav-item dropdown">
<a class="nav-link" data-toggle="dropdown" href="#"><i class="far fa-comments"></i><span class="badge badge-danger navbar-badge">3</span></a>
<div class="dropdown-menu dropdown-menu-lg dropdown-menu-right">
<!-- Message items omitted for brevity -->
</div>
</li>
<!-- Notifications Dropdown Menu -->
<li class="nav-item dropdown">
<a class="nav-link" data-toggle="dropdown" href="#"><i class="far fa-bell"></i><span class="badge badge-warning navbar-badge">15</span></a>
<div class="dropdown-menu dropdown-menu-lg dropdown-menu-right">
<!-- Notification items omitted for brevity -->
</div>
</li>
<li class="nav-item"><a class="nav-link" data-widget="control-sidebar" data-slide="true" href="#" role="button"><i class="fas fa-th-large"></i></a></li>
</ul>
</nav>
<!-- /.navbar -->
<!-- Main Sidebar Container -->
<aside class="main-sidebar sidebar-dark-primary elevation-4">
<!-- Brand Logo -->
<a href="{% url 'home:landing' %}" class="brand-link">
<img src="{% static 'dist/img/AdminLTELogo.png' %}" alt="AdminLTE Logo" class="brand-image img-circle elevation-3" style="opacity: .8">
<span class="brand-text font-weight-light">AdminLTE 3</span>
</a>
<!-- Sidebar -->
<div class="sidebar">
<!-- Sidebar user panel (optional) -->
<div class="user-panel mt-3 pb-3 mb-3 d-flex">
<div class="image"><img src="{% static 'dist/img/user2-160x160.jpg' %}" class="img-circle elevation-2" alt="User Image"></div>
<div class="info"><a href="#" class="d-block">Alexander Pierce</a></div>
</div>
{% block sidebar %}{% endblock %}
</div>
<!-- /.sidebar -->
</aside>
<!-- Content Wrapper. Contains page content -->
{% block content_wrapper %}{% endblock %}
<!-- Control Sidebar -->
<aside class="control-sidebar control-sidebar-dark">
<div class="p-3"><h5>Title</h5><p>Sidebar content</p></div>
</aside>
<!-- Main Footer -->
<footer class="main-footer">
<div class="float-right d-none d-sm-inline">Anything you want</div>
<strong>Copyright © 2014-2019 <a href="https://adminlte.io">AdminLTE.io</a>.</strong> All rights reserved.
</footer>
</div>
<!-- ./wrapper -->
<!-- REQUIRED SCRIPTS -->
<script src="{% static 'plugins/jquery/jquery.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap/js/bootstrap.bundle.min.js' %}"></script>
<script src="{% static 'dist/js/adminlte.min.js' %}"></script>
</body>
</html>Step 2/4 – Remove redundant common code After creating base.html, the duplicated markup in landing.html and index.html can be deleted, leaving only page‑specific sections.
Step 3/4 – Extend base.html Each page declares the base template with {% extends 'base.html' %} and wraps its unique content inside the {% block content_wrapper %} block. This ensures the shared layout is automatically applied.
Step 4/4 – Store common content separately The large form that appears on multiple pages is moved to advanced_forms/general_elements_form.html . The pages then include it with {% include 'advanced_forms/general_elements_form.html' %} , eliminating duplication.
Finally, the project is restarted; although the UI looks unchanged, the file structure is now cleaner and more maintainable, illustrating the importance of reusable templates in both frontend and backend development.
Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.