How to Architect Django Templates
Summary
In this article, I am going to present my approach to design Django Templates in the Django application development. The objective is to keep the UI part of Django application better organized and avoid redundant coding. Django has provided various mechanisms in template engine to help us achieve such goal. In this tutorial, I will walk you through how to make templates easy to maintain as much as possible by using Django built-in template tag block, extends and include.
Dependencies:
- Python 3.6
- Django 2.2
- AdminLTE 3.0.5 (You can check my previous tutorial on this theming library here.)
Preview: Bad architecture of templates
I’ve uploaded a project here to illustrate how messy the templates are. We are going to start from there. Let’s take a look at these two pages, https://127.0.0.1:8000/home/ and https://127.0.0.1:8000/polls/. Their respective html templates are located at home/templates/home/landing.html and polls/templates/polls/index.html. In short, I will call them landing.html and index.html. Each template repeats the sidebar menu, css and javascript libraries. On top of that, there is a giant form in “General Elements” box within that the content is identical. With the growth of apps and urls, this is way out of control.
My Approach
Here is my approach to avoid the “spaghetti code” of Django UI.
Step 1/4: base.html
Breaking down the template into multiple pieces, we know except menu and content, everything else is repeatable. We are going to make a base template to hold those common pieces.
Create a folder templates in project folder. Within it, create a base.html. Add all common pieces into it. You can just copy and paste the followings which is nothing but a fraction of codes shared by both landing.html and index.html.
{% load static %} <!DOCTYPE html> <!-- This is a starter template page. Use this page to start your new project from scratch. This page gets rid of all links and provides the needed markup only. --> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta http-equiv="x-ua-compatible" content="ie=edge"> <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 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"> <a href="#" class="dropdown-item"> <!-- Message Start --> <div class="media"> <img src="{% static 'dist/img/user1-128x128.jpg' %}" alt="User Avatar" class="img-size-50 mr-3 img-circle"> <div class="media-body"> <h3 class="dropdown-item-title"> Brad Diesel <span class="float-right text-sm text-danger"><i class="fas fa-star"></i></span> </h3> <p class="text-sm">Call me whenever you can...</p> <p class="text-sm text-muted"><i class="far fa-clock mr-1"></i> 4 Hours Ago</p> </div> </div> <!-- Message End --> </a> <div class="dropdown-divider"></div> <a href="#" class="dropdown-item"> <!-- Message Start --> <div class="media"> <img src="{% static 'dist/img/user8-128x128.jpg' %}" alt="User Avatar" class="img-size-50 img-circle mr-3"> <div class="media-body"> <h3 class="dropdown-item-title"> John Pierce <span class="float-right text-sm text-muted"><i class="fas fa-star"></i></span> </h3> <p class="text-sm">I got your message bro</p> <p class="text-sm text-muted"><i class="far fa-clock mr-1"></i> 4 Hours Ago</p> </div> </div> <!-- Message End --> </a> <div class="dropdown-divider"></div> <a href="#" class="dropdown-item"> <!-- Message Start --> <div class="media"> <img src="{% static 'dist/img/user3-128x128.jpg' %}" alt="User Avatar" class="img-size-50 img-circle mr-3"> <div class="media-body"> <h3 class="dropdown-item-title"> Nora Silvester <span class="float-right text-sm text-warning"><i class="fas fa-star"></i></span> </h3> <p class="text-sm">The subject goes here</p> <p class="text-sm text-muted"><i class="far fa-clock mr-1"></i> 4 Hours Ago</p> </div> </div> <!-- Message End --> </a> <div class="dropdown-divider"></div> <a href="#" class="dropdown-item dropdown-footer">See All Messages</a> </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"> <span class="dropdown-header">15 Notifications</span> <div class="dropdown-divider"></div> <a href="#" class="dropdown-item"> <i class="fas fa-envelope mr-2"></i> 4 new messages <span class="float-right text-muted text-sm">3 mins</span> </a> <div class="dropdown-divider"></div> <a href="#" class="dropdown-item"> <i class="fas fa-users mr-2"></i> 8 friend requests <span class="float-right text-muted text-sm">12 hours</span> </a> <div class="dropdown-divider"></div> <a href="#" class="dropdown-item"> <i class="fas fa-file mr-2"></i> 3 new reports <span class="float-right text-muted text-sm">2 days</span> </a> <div class="dropdown-divider"></div> <a href="#" class="dropdown-item dropdown-footer">See All Notifications</a> </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> <!-- Sidebar Menu --> {% block sidebar %}{% endblock %} <!-- /.sidebar-menu --> </div> <!-- /.sidebar --> </aside> <!-- Content Wrapper. Contains page content --> {% block content_wrapper %}{% endblock %} <!-- /.content-wrapper --> <!-- Control Sidebar --> <aside class="control-sidebar control-sidebar-dark"> <!-- Control sidebar content goes here --> <div class="p-3"> <h5>Title</h5> <p>Sidebar content</p> </div> </aside> <!-- /.control-sidebar --> <!-- Main Footer --> <footer class="main-footer"> <!-- To the right --> <div class="float-right d-none d-sm-inline"> Anything you want </div> <!-- Default to the left --> <strong>Copyright © 2014-2019 <a >AdminLTE.io</a>.</strong> All rights reserved. </footer> </div> <!-- ./wrapper --> <!-- REQUIRED SCRIPTS --> <!-- jQuery --> <script src="{% static 'plugins/jquery/jquery.min.js' %}"></script> <!-- Bootstrap 4 --> <script src="{% static 'plugins/bootstrap/js/bootstrap.bundle.min.js' %}"></script> <!-- AdminLTE App --> <script src="{% static 'dist/js/adminlte.min.js' %}"></script> </body> </html>
Please be aware that block content_wrapper is used to render custom content for each page.
Step 2/4: Remove redundant common codes
Since we created a base.html in the previous step, there is no need to keep the common codes in landing.html and home.html anymore. We should have results as below.
In landing.html:
<div class="content-wrapper"> <!-- Content Header (Page header) --> <div class="content-header"> <div class="container-fluid"> <div class="row mb-2"> <div class="col-sm-6"> <h1 class="m-0 text-dark">Home Landing Page</h1> </div><!-- /.col --> <div class="col-sm-6"> <ol class="breadcrumb float-sm-right"> <li class="breadcrumb-item"><a href="#">Landing Page</a></li> </ol> </div><!-- /.col --> </div><!-- /.row --> </div><!-- /.container-fluid --> </div> <!-- /.content-header --> <!-- Main content --> <div class="content"> <div class="container-fluid"> <div class="row"> <div class="col-lg-6"> <div class="card"> <div class="card-body"> <h5 class="card-title">Card title</h5> <p class="card-text"> Some quick example text to build on the card title and make up the bulk of the card's content. </p> <a href="#" class="card-link">Card link</a> <a href="#" class="card-link">Another link</a> </div> </div> <div class="card card-primary card-outline"> <div class="card-body"> <h5 class="card-title">Card title</h5> <p class="card-text"> Some quick example text to build on the card title and make up the bulk of the card's content. </p> <a href="#" class="card-link">Card link</a> <a href="#" class="card-link">Another link</a> </div> </div><!-- /.card --> </div> <div class="col-md-6"> <!-- general form elements disabled --> <div class="card card-warning"> <div class="card-header"> <h3 class="card-title">General Elements</h3> </div> <!-- /.card-header --> <div class="card-body"> <form role="form"> <div class="row"> <div class="col-sm-6"> <!-- text input --> <div class="form-group"> <label>Text</label> <input type="text" class="form-control" placeholder="Enter ..."> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Text Disabled</label> <input type="text" class="form-control" placeholder="Enter ..." disabled> </div> </div> </div> <div class="row"> <div class="col-sm-6"> <!-- textarea --> <div class="form-group"> <label>Textarea</label> <textarea class="form-control" rows="3" placeholder="Enter ..."></textarea> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Textarea Disabled</label> <textarea class="form-control" rows="3" placeholder="Enter ..." disabled></textarea> </div> </div> </div> <!-- input states --> <div class="form-group"> <label class="col-form-label" for="inputSuccess"><i class="fas fa-check"></i> Input with success</label> <input type="text" class="form-control is-valid" id="inputSuccess" placeholder="Enter ..."> </div> <div class="form-group"> <label class="col-form-label" for="inputWarning"><i class="far fa-bell"></i> Input with warning</label> <input type="text" class="form-control is-warning" id="inputWarning" placeholder="Enter ..."> </div> <div class="form-group"> <label class="col-form-label" for="inputError"><i class="far fa-times-circle"></i> Input with error</label> <input type="text" class="form-control is-invalid" id="inputError" placeholder="Enter ..."> </div> <div class="row"> <div class="col-sm-6"> <!-- checkbox --> <div class="form-group"> <div class="form-check"> <input class="form-check-input" type="checkbox"> <label class="form-check-label">Checkbox</label> </div> <div class="form-check"> <input class="form-check-input" type="checkbox" checked> <label class="form-check-label">Checkbox checked</label> </div> <div class="form-check"> <input class="form-check-input" type="checkbox" disabled> <label class="form-check-label">Checkbox disabled</label> </div> </div> </div> <div class="col-sm-6"> <!-- radio --> <div class="form-group"> <div class="form-check"> <input class="form-check-input" type="radio" name="radio1"> <label class="form-check-label">Radio</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="radio1" checked> <label class="form-check-label">Radio checked</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" disabled> <label class="form-check-label">Radio disabled</label> </div> </div> </div> </div> <div class="row"> <div class="col-sm-6"> <!-- select --> <div class="form-group"> <label>Select</label> <select class="form-control"> <option>option 1</option> <option>option 2</option> <option>option 3</option> <option>option 4</option> <option>option 5</option> </select> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Select Disabled</label> <select class="form-control" disabled> <option>option 1</option> <option>option 2</option> <option>option 3</option> <option>option 4</option> <option>option 5</option> </select> </div> </div> </div> <div class="row"> <div class="col-sm-6"> <!-- Select multiple--> <div class="form-group"> <label>Select Multiple</label> <select multiple class="form-control"> <option>option 1</option> <option>option 2</option> <option>option 3</option> <option>option 4</option> <option>option 5</option> </select> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Select Multiple Disabled</label> <select multiple class="form-control" disabled> <option>option 1</option> <option>option 2</option> <option>option 3</option> <option>option 4</option> <option>option 5</option> </select> </div> </div> </div> </form> </div> <!-- /.card-body --> </div> <!-- /.card --> </div> </div> </div><!-- /.container-fluid --> </div> <!-- /.content --> </div>
In index.html:
<div class="content-wrapper"> <!-- Content Header (Page header) --> <div class="content-header"> <div class="container-fluid"> <div class="row mb-2"> <div class="col-sm-6"> <h1 class="m-0 text-dark">Polls Index Page</h1> </div><!-- /.col --> <div class="col-sm-6"> <ol class="breadcrumb float-sm-right"> <li class="breadcrumb-item"><a href="#">Home</a></li> <li class="breadcrumb-item active">Polls</li> </ol> </div><!-- /.col --> </div><!-- /.row --> </div><!-- /.container-fluid --> </div> <!-- /.content-header --> <!-- Main content --> <div class="content"> <div class="container-fluid"> <div class="row"> <div class="col-lg-6"> <div class="card"> <div class="card-body"> <h5 class="card-title">Card title</h5> <p class="card-text"> Some quick example text to build on the card title and make up the bulk of the card's content. </p> <a href="#" class="card-link">Card link</a> <a href="#" class="card-link">Another link</a> </div> </div> <div class="card card-primary card-outline"> <div class="card-body"> <h5 class="card-title">Card title</h5> <p class="card-text"> Some quick example text to build on the card title and make up the bulk of the card's content. </p> <a href="#" class="card-link">Card link</a> <a href="#" class="card-link">Another link</a> </div> </div><!-- /.card --> <!-- general form elements disabled --> <div class="card card-primary card-outline"> <div class="card-header"> <h3 class="card-title">General Elements</h3> </div> <!-- /.card-header --> <div class="card-body"> <form role="form"> <div class="row"> <div class="col-sm-6"> <!-- text input --> <div class="form-group"> <label>Text</label> <input type="text" class="form-control" placeholder="Enter ..."> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Text Disabled</label> <input type="text" class="form-control" placeholder="Enter ..." disabled> </div> </div> </div> <div class="row"> <div class="col-sm-6"> <!-- textarea --> <div class="form-group"> <label>Textarea</label> <textarea class="form-control" rows="3" placeholder="Enter ..."></textarea> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Textarea Disabled</label> <textarea class="form-control" rows="3" placeholder="Enter ..." disabled></textarea> </div> </div> </div> <!-- input states --> <div class="form-group"> <label class="col-form-label" for="inputSuccess"><i class="fas fa-check"></i> Input with success</label> <input type="text" class="form-control is-valid" id="inputSuccess" placeholder="Enter ..."> </div> <div class="form-group"> <label class="col-form-label" for="inputWarning"><i class="far fa-bell"></i> Input with warning</label> <input type="text" class="form-control is-warning" id="inputWarning" placeholder="Enter ..."> </div> <div class="form-group"> <label class="col-form-label" for="inputError"><i class="far fa-times-circle"></i> Input with error</label> <input type="text" class="form-control is-invalid" id="inputError" placeholder="Enter ..."> </div> <div class="row"> <div class="col-sm-6"> <!-- checkbox --> <div class="form-group"> <div class="form-check"> <input class="form-check-input" type="checkbox"> <label class="form-check-label">Checkbox</label> </div> <div class="form-check"> <input class="form-check-input" type="checkbox" checked> <label class="form-check-label">Checkbox checked</label> </div> <div class="form-check"> <input class="form-check-input" type="checkbox" disabled> <label class="form-check-label">Checkbox disabled</label> </div> </div> </div> <div class="col-sm-6"> <!-- radio --> <div class="form-group"> <div class="form-check"> <input class="form-check-input" type="radio" name="radio1"> <label class="form-check-label">Radio</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="radio1" checked> <label class="form-check-label">Radio checked</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" disabled> <label class="form-check-label">Radio disabled</label> </div> </div> </div> </div> <div class="row"> <div class="col-sm-6"> <!-- select --> <div class="form-group"> <label>Select</label> <select class="form-control"> <option>option 1</option> <option>option 2</option> <option>option 3</option> <option>option 4</option> <option>option 5</option> </select> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Select Disabled</label> <select class="form-control" disabled> <option>option 1</option> <option>option 2</option> <option>option 3</option> <option>option 4</option> <option>option 5</option> </select> </div> </div> </div> <div class="row"> <div class="col-sm-6"> <!-- Select multiple--> <div class="form-group"> <label>Select Multiple</label> <select multiple class="form-control"> <option>option 1</option> <option>option 2</option> <option>option 3</option> <option>option 4</option> <option>option 5</option> </select> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Select Multiple Disabled</label> <select multiple class="form-control" disabled> <option>option 1</option> <option>option 2</option> <option>option 3</option> <option>option 4</option> <option>option 5</option> </select> </div> </div> </div> </form> </div> <!-- /.card-body --> </div> <!-- /.card --> </div> <!-- /.col-md-6 --> <div class="col-lg-6"> <div class="card"> <div class="card-header"> <h5 class="m-0">Featured</h5> </div> <div class="card-body"> <h6 class="card-title">Special title treatment</h6> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> </div> <div class="card card-primary card-outline"> <div class="card-header"> <h5 class="m-0">Featured</h5> </div> <div class="card-body"> <h6 class="card-title">Special title treatment</h6> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> </div> </div> <!-- /.col-md-6 --> </div> <!-- /.row --> </div><!-- /.container-fluid --> </div> <!-- /.content --> </div>
Step 3/4: Inherit base.html
In order to use base.html as a base template for each page, we need to declare base.html as a “parent” template by using {% extends ‘base.html’ %} at the beginning of the template. On top of that, don’t forget content_wrapper block. Wrap the entire content into that block. We should get result as below.
In landing.html:
{% extends 'base.html' %} {% load static %} {% block content_wrapper %} <div class="content-wrapper"> . . . </div> {% endblock %}
In index.html:
{% extends 'base.html' %} {% load static %} {% block content_wrapper %} <div class="content-wrapper"> . . . </div> {% endblock %}
I suggest you start your project and make sure it looks exactly the same as it used to be. And it should be!
Step 4/4: DRY common small pieces
You may be aware that a same giant form exists in both templates. Almost half of the codes is it. Since this form is reused in two templates, let’s maintain it in one single place where any template can include it.
Create a folder advanced_forms in templates folder. Within advanced_forms folder, create a general_elements_form.html as below.
<form role="form"> <div class="row"> <div class="col-sm-6"> <!-- text input --> <div class="form-group"> <label>Text</label> <input type="text" class="form-control" placeholder="Enter ..."> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Text Disabled</label> <input type="text" class="form-control" placeholder="Enter ..." disabled> </div> </div> </div> <div class="row"> <div class="col-sm-6"> <!-- textarea --> <div class="form-group"> <label>Textarea</label> <textarea class="form-control" rows="3" placeholder="Enter ..."></textarea> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Textarea Disabled</label> <textarea class="form-control" rows="3" placeholder="Enter ..." disabled></textarea> </div> </div> </div> <!-- input states --> <div class="form-group"> <label class="col-form-label" for="inputSuccess"><i class="fas fa-check"></i> Input with success</label> <input type="text" class="form-control is-valid" id="inputSuccess" placeholder="Enter ..."> </div> <div class="form-group"> <label class="col-form-label" for="inputWarning"><i class="far fa-bell"></i> Input with warning</label> <input type="text" class="form-control is-warning" id="inputWarning" placeholder="Enter ..."> </div> <div class="form-group"> <label class="col-form-label" for="inputError"><i class="far fa-times-circle"></i> Input with error</label> <input type="text" class="form-control is-invalid" id="inputError" placeholder="Enter ..."> </div> <div class="row"> <div class="col-sm-6"> <!-- checkbox --> <div class="form-group"> <div class="form-check"> <input class="form-check-input" type="checkbox"> <label class="form-check-label">Checkbox</label> </div> <div class="form-check"> <input class="form-check-input" type="checkbox" checked> <label class="form-check-label">Checkbox checked</label> </div> <div class="form-check"> <input class="form-check-input" type="checkbox" disabled> <label class="form-check-label">Checkbox disabled</label> </div> </div> </div> <div class="col-sm-6"> <!-- radio --> <div class="form-group"> <div class="form-check"> <input class="form-check-input" type="radio" name="radio1"> <label class="form-check-label">Radio</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="radio1" checked> <label class="form-check-label">Radio checked</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" disabled> <label class="form-check-label">Radio disabled</label> </div> </div> </div> </div> <div class="row"> <div class="col-sm-6"> <!-- select --> <div class="form-group"> <label>Select</label> <select class="form-control"> <option>option 1</option> <option>option 2</option> <option>option 3</option> <option>option 4</option> <option>option 5</option> </select> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Select Disabled</label> <select class="form-control" disabled> <option>option 1</option> <option>option 2</option> <option>option 3</option> <option>option 4</option> <option>option 5</option> </select> </div> </div> </div> <div class="row"> <div class="col-sm-6"> <!-- Select multiple--> <div class="form-group"> <label>Select Multiple</label> <select multiple class="form-control"> <option>option 1</option> <option>option 2</option> <option>option 3</option> <option>option 4</option> <option>option 5</option> </select> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Select Multiple Disabled</label> <select multiple class="form-control" disabled> <option>option 1</option> <option>option 2</option> <option>option 3</option> <option>option 4</option> <option>option 5</option> </select> </div> </div> </div> </form>
Remove redundant form codes in landing.html and index.html. Include form by using {% include 'advanced_forms/general_elements_form.html' %}. With it set up, here is the final result.
In landing.html:
{% extends 'base.html' %} {% load static %} {% block content_wrapper %} <div class="content-wrapper"> <!-- Content Header (Page header) --> <div class="content-header"> <div class="container-fluid"> <div class="row mb-2"> <div class="col-sm-6"> <h1 class="m-0 text-dark">Home Landing Page</h1> </div><!-- /.col --> <div class="col-sm-6"> <ol class="breadcrumb float-sm-right"> <li class="breadcrumb-item"><a href="#">Landing Page</a></li> </ol> </div><!-- /.col --> </div><!-- /.row --> </div><!-- /.container-fluid --> </div> <!-- /.content-header --> <!-- Main content --> <div class="content"> <div class="container-fluid"> <div class="row"> <div class="col-lg-6"> <div class="card"> <div class="card-body"> <h5 class="card-title">Card title</h5> <p class="card-text"> Some quick example text to build on the card title and make up the bulk of the card's content. </p> <a href="#" class="card-link">Card link</a> <a href="#" class="card-link">Another link</a> </div> </div> <div class="card card-primary card-outline"> <div class="card-body"> <h5 class="card-title">Card title</h5> <p class="card-text"> Some quick example text to build on the card title and make up the bulk of the card's content. </p> <a href="#" class="card-link">Card link</a> <a href="#" class="card-link">Another link</a> </div> </div><!-- /.card --> </div> <div class="col-md-6"> <!-- general form elements disabled --> <div class="card card-warning"> <div class="card-header"> <h3 class="card-title">General Elements</h3> </div> <!-- /.card-header --> <div class="card-body"> {% include 'advanced_forms/general_elements_form.html' %} </div> <!-- /.card-body --> </div> <!-- /.card --> </div> </div> </div><!-- /.container-fluid --> </div> <!-- /.content --> </div> {% endblock %}
In index.html:
{% extends 'base.html' %} {% load static %} {% block content_wrapper %} <div class="content-wrapper"> <!-- Content Header (Page header) --> <div class="content-header"> <div class="container-fluid"> <div class="row mb-2"> <div class="col-sm-6"> <h1 class="m-0 text-dark">Polls Index Page</h1> </div><!-- /.col --> <div class="col-sm-6"> <ol class="breadcrumb float-sm-right"> <li class="breadcrumb-item"><a href="#">Home</a></li> <li class="breadcrumb-item active">Polls</li> </ol> </div><!-- /.col --> </div><!-- /.row --> </div><!-- /.container-fluid --> </div> <!-- /.content-header --> <!-- Main content --> <div class="content"> <div class="container-fluid"> <div class="row"> <div class="col-lg-6"> <div class="card"> <div class="card-body"> <h5 class="card-title">Card title</h5> <p class="card-text"> Some quick example text to build on the card title and make up the bulk of the card's content. </p> <a href="#" class="card-link">Card link</a> <a href="#" class="card-link">Another link</a> </div> </div> <div class="card card-primary card-outline"> <div class="card-body"> <h5 class="card-title">Card title</h5> <p class="card-text"> Some quick example text to build on the card title and make up the bulk of the card's content. </p> <a href="#" class="card-link">Card link</a> <a href="#" class="card-link">Another link</a> </div> </div><!-- /.card --> <!-- general form elements disabled --> <div class="card card-primary card-outline"> <div class="card-header"> <h3 class="card-title">General Elements</h3> </div> <!-- /.card-header --> <div class="card-body"> {% include 'advanced_forms/general_elements_form.html' %} </div> <!-- /.card-body --> </div> <!-- /.card --> </div> <!-- /.col-md-6 --> <div class="col-lg-6"> <div class="card"> <div class="card-header"> <h5 class="m-0">Featured</h5> </div> <div class="card-body"> <h6 class="card-title">Special title treatment</h6> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> </div> <div class="card card-primary card-outline"> <div class="card-header"> <h5 class="m-0">Featured</h5> </div> <div class="card-body"> <h6 class="card-title">Special title treatment</h6> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> </div> </div> <!-- /.col-md-6 --> </div> <!-- /.row --> </div><!-- /.container-fluid --> </div> <!-- /.content --> </div> {% endblock %}
Now, let’s restart project again. Nothing changes on screen. But back the scene, project becomes easier to maintain. Effort will worth down the road.
Here is the snapshot of file structure after we complete the enhancement. I’ve highlighted the templates in yellow.
I’ve uploaded the final project here. https://github.com/slow999/DjangoAndTemplates .
The messy project I used in this tutorial is uploaded here. https://github.com/slow999/DjangoAndBadTemplatesDesign .
I also made a video. Check it out below.
Conclusion
Template is one of the core pieces in web application. Don’t repeat yourself (DRY). Having been working on and leading a great number of projects, I believe that this principle applies on both front-end and back-end developments so that we can make a scalable application. Hope this tutorial answers your question and help your project grow. Stay tuned.