Django templates and best practices for template structure
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:
-
Overview - Basic Introduction what templates are and how they work
-
Template folder structure - Project-level templates vs. App-level templates
-
Template page structure - Using template inheritance to build generic and child templates
-
Advanced concepts - template tags
Prerequisites
-
Basic knowledge of Django or web frameworks.
-
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 ) - This consists 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 organized 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
-
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
-
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 or base templates
It is considered best practice to define a generic template from which our other templates can inherit. 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>
-
Includes
<meta>
tags, page scripts, and styling that can be reused on 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>
-
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.
Template tags
Default template tags
Template tags provide arbitrary logic in the rendering process. This logic can be anything from iterating over a Python list to outputting content, performing conditional operations, and so on. Django comes with a variety of built-in template tags that you can use directly in your templates.
Here's a brief overview of some of the most commonly used Django template tags:
1.
{% for %}
and
{% endfor %}
: These tags are used for looping over a sequence. For example:
<ul>
{% for item in item_list %}
<li>{{ item }}</li>
{% endfor %}
</ul>
2.
{% if %}
,
{% elif %}
,
{% else %}
and
{% endif %}
: These tags are used for conditional statements. For example:
{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}!</p>
{% else %}
<p>Please log in.</p>
{% endif %}
3.
{% extends "base.html" %}
: This tag is used for template inheritance. It tells Django that the current template "extends" another template.
4.
{% block content %}
and
{% endblock %}
: These tags define a block in the template. This block can be filled with content in templates that extend the current template.
5.
{% load %}
: This tag is used to load a custom template tag set. For example,
{% load static %}
loads the static template tag library.
6.
{% include "template.html" %}
: This tag allows you to include the contents of another template file within the current template.
7.
{% url 'view_name' %}
: This tag is used to generate a URL based on a named URL pattern.
8.
{% csrf_token %}
: This tag is used for cross-site request forgery protection. It should be used inside every HTML form that uses the POST method.
9.
{% static 'path/to/static/file' %}
: This tag is used to output the URL of a static file.
10.
{% comment %}
and
{% endcomment %}
: These tags are used to comment out part of a template.
Custom Template Tags
In addition to the built-in template tags, Django allows you to create your own custom template tags. Custom template tags can be very useful when you need to create reusable bits of logic that aren't covered by the built-in tags.
To create a custom template tag, you need to define a Python function that takes one or more arguments, performs some processing, and returns a result. This function is then registered as a template tag using the
@register.simple_tag
decorator.
Here's an example of a custom template tag that calculates the sum of two numbers:
from django import template
register = template.Library()
@register.simple_tag
def add(a, b):
return a + b
This is how you can use this template in the file.
{% load custom_tags %}
<p>The sum of 5 and 3 is {% add 5 3 %}.</p>
Django's template tags provide a powerful way to add dynamic content and logic to your templates. Whether you're using the built-in tags or creating your own, template tags can help you create more flexible and reusable templates.
Conclusion
Alright, let's wrap this up. We've talked about Django templates, how they work, and how to organize them in your projects. We've seen how templates can be set up at the app level or the project level, and how template inheritance helps us avoid repeating ourselves.
We've also looked at how template tags, both built-in and custom ones, can add some dynamic content and logic to our templates.
Next up, try applying these concepts in your own Django projects. And don't forget to check out the Django documentation and tutorials for more detailed information.
Further reading and sources
Django Docs - A good starting point for understanding how it all works
Django Tutorials on Mozilla - Excellent tutorials that cover the basics in sufficient detail.