Django Templates And Best Practices For Template Structure.

Introduction

In this post, we explain what Django templates are, how they work and how to organize the template structure in our Django Projects. In this article we cover:

1. Overview - Basic Introduction what templates are and how they work

2. Template folder structure - Project level templates vs App-level templates

3. Template page structure - Using template inheritance to build generic and child templates

Prerequisites

  • Basic knowledge of Django or web frameworks. 
  • Django starter project. - Optional as this post will focus on the high-level concepts.  However, you can start a blank Django project if you need to get your hands dirty and try a few things.

LEVEL - 💻 Beginner

Basic knowledge of Django and how Django templates work

Overview

A brief introduction to Django templates.

  • A template is a text file or a Python string that defines how the Html should be created. Templates are marked up using the Django Template Language (DTL). DTL uses tags and variables
  • In Django, HTML is generated dynamically. A view retrieves the requested data from the models/or some other data source, loads the relevant template and finally renders the output to a string.
A simplified overview of how Django templates work

Key concepts:

  • Loading (views.py) - Consist of finding the template for a given identified and preprocessing it.
  • Rendering (views.py) - Inject the context (data) into the template
  • Context (views.py)- Dictionary like object mapping keys to values.
  • Variables (template.html) - Outputs a value from the context and is surrounded by double curly brackets.
    • For example this template string 'Hi {{ name }}.', would render 'Hi John' from the context {'name': 'John'}
  • Tags - Logic to be used in the rendering process. This is an intentionally broad definition as text can be used as a control structure or output content. See the Django docs for more on this.

Template folder structure

Template folders can be organised in one of two ways.

App Level

├── my_proj
|   ├── config # Optional keeps config separate from apps
|   ├── apps. #  Optional all apps in one folder
|     └── my_app 
|        ├── __init__.py
│        ├── models.py
│        └── views.py
|        ├── templates
|            ├── my_app
|                ├── home.html
└── manage.py

Notes

  • Templates are saved within each app.
  • Used the app name 'my_app' in the folder to avoid namespace collisions.
  • This approach is more suitable for smaller projects.

Project Level

├── my_proj
|   └── config # Optional keeps config separate from apps
│       ├── __init__.py
│       ├── settings.py
│       ├── urls.py
│       ├── asgi.py
│       └── wsgi.py
|   └── apps # Optional all apps in one folder
|      ├── my_app
│      ├── another_app
├── templates
|   ├── base.html
|   └── my_app
|      ├── detail.html
|   └── another_app
|      ├── detail.html
└── manage.py

Notes

  • Templates are saved in one main folder
  • Templates for each app are saved in a separate folder.
  • More suitable for larger projects. As the number of apps grows, it becomes more cumbersome to hunt for templates from multiple apps. (Our preferred approach.)
  • You will notice that we prefer to keep config and apps in separate folders. This is a departure from the example app shown in the Django docs. This is our preferred way of structuring large projects.

Template inheritance - Generic and child templates

Your Django templates will contain a lot of code that is common across most of your views. A powerful feature of Django's templating language is template inheritance which allows us to build a base 'skeleton' template that contains common elements and blocks that child templates can override.

Defining a generic template

It is considered best practice to define a generic template from which our other templates can inherit from. This keeps our Html templates DRY, keeps code in one place and makes it easier to modify our code.

<!--base.html-->
{% load static some_other_tag %} {# >>1 - Load tags#}
<!DOCTYPE html>
<html>
<head>
    {# Use inbuilt block tag for variables that we can override from other templates #}
    <title>{% block title %}Home{% endblock %}</title> 
    
    {# include <meta> tags including tags to make your site SEO friendly #}

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    
    {# include styling #}
    <link href="{% static "css/project.css" %}" rel="stylesheet">
    {% block head_css %}
      <link href="{% static "css/other.css" %}" rel="stylesheet">
    {% endblock %}

    {# include javascript #}
    <script defer src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v3.0.1/dist/alpine.min.js"></script>
    {% block head_js %}{% endblock %}
</head>
<body>
    {% block content %}{% endblock %}
    {% block footer_js %}{% endblock %}

</body>
</html>

Notes

  • Includes <meta> tags , page scripts, styling that are can be reused in other pages
  • Uses the block tag i.e. {% block x %} {% endblock %} which can be overridden from other templates.

Child templates

An example child template that extends the base template would look like this.

<!--base.html-->
{% load static some_other_tag %} {# >>1 - Load tags#}
<!DOCTYPE html>
<html>
<head>
    {# Use inbuilt block tag for variables that we can override from other templates #}
    <title>{% block title %}Home{% endblock %}</title> 
    
    {# include <meta> tags including tags to make your site SEO friendly #}

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    
    {# include styling #}
    <link href="{% static "css/project.css" %}" rel="stylesheet">
    {% block head_css %}
      <link href="{% static "css/other.css" %}" rel="stylesheet">
    {% endblock %}

    {# include javascript #}
    <script defer src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v3.0.1/dist/alpine.min.js"></script>
    {% block head_js %}{% endblock %}
</head>
<body>
    {% block content %}{% endblock %}
    {% block footer_js %}{% endblock %}

</body>
</html>

Notes

  • Uses the 'extends tag' which tells the templating engine to extend another template. In this case, base Html.
  • Overrides the title and content tags to inject page specific content.
  • You will notice that the head_css tag uses the {{ block.super }} variable. This is useful for adding to components of the parent block instead of completely overriding it. In this example, the contents of other.css will also be available on this page.

Are you building a SaaS product and want to launch like ASAP? Check out the Vanty Starter Kit. Our Django boilerplate has everything you need to launch your app today!

Learn more →

Conclusion

This barely scratches the surface of what is possible with Django templates but hopefully gives you some understanding of how Django templates work and how you could structure your project.

We haven't covered other interesting topics such as using modern Javascript in your Django projects or optimizing for SEO. We will dive into those in later posts.

Further reading and sources

Django Docs - A good starting point for understanding how it all works

Django unicorn - Reactive Django using normal templates

Django Tutorials on Mozilla - Excellent tutorials that cover the basics in sufficient detail.

Last Updated 25 Sep 2022