Creating custom template tags in Django

For those well-versed in Python Django, common template tags like {% load static %} , {% if %}are likely second nature, these are all part of the Django templating language but did you know you can create your own custom template tags?, In this article, I'll be showing how to do just that.

Custom template tags

Django offers a pair of utility functions designed for crafting custom template tags.

  1. simple_tag: It handles the given data and produces a string as output.

  2. inclusion_tag: returns a template after processing the given data.


For this Illustration, we will be working with the following sample models:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    age = models.IntegerField()

    def __str__(self):
        return f"{self.first_name} {self.last_name}"

class Product(models.Model):
    name = models.CharField(max_length=100)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    description = models.TextField()

    def __str__(self):
        return self.name

Simple tags:

Create your django project and app. Guide on how to get started with Django projects can be found here. Custom template tags have to live inside a Django app.

In your Django app folder, i.e the folder that contains models.py, views.py e.t.c create a new folder called templatetags.

Inside the templatetags directory, create a new file and call it app_tags.py the naming is important here because you will use this name to load the tags in your HTML e.g {% load app_tags %}

Our first custom template tag will be one that returns the total number of products.

Edit the app_tags.py file and include the following code:


from django import template
from ..models import Product

register = template.Library()

@register.simple_tag
def total_products():
    return Product.objects.count()

We have created the function called total_products and added the @register.simple_tag decorator to it, this registers it as a simple tag meaning we want the output of this tag to be just a string.

In the HTML template where you want to display the total number of products, you can load your custom template tags and then utilize them however you wish.

{% load app_tags %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Custom template tags</title>
</head>
<body>
    <h1>The total number of products is {% total_products %}</h1>
</body>
</html>

We load our custom tags using {% load app_tags %} and have used the total_products template tag to display the total number of products in a h1 tag.

The above html code would render a h1 tag as shown below:

We have just successfully created a template tag that returns the total number of products in the database. When employing the simple_tag helper function, you have the flexibility to insert any code required for processing data as needed, though it is constrained to returning a string. Django will convert any other datatype returned into a string before it is rendered in the HTML.

Inclusion tags:

When using inclusion tags, we now have the ability to render a HTML template using the context variables provided by your custom template tag.

We will be creating an inclusion tag that will always return a html unordered list containing the names of people in the database.

In your templates directory (where you keep all your html files), create a new folder, I've named mine inclusions, then in your new folder, create a new html file, I've called mine all_persons.html

Edit the templatetags/app_tags.py file and update it as below:

from django import template
from ..models import Product, Person

register = template.Library()

@register.simple_tag
def total_products():
    return Product.objects.count()

@register.inclusion_tag('inclusions/all_persons.html')
def get_names(num=5):
    people = Person.objects.all()[:num]
    return {'people': people}

We have created a new template tag called get_names and registered it as an inclusion tag using @register.inclusion_tag('inclusions/all_persons.html') notice that we passed in the path to the html file we want to render. In this function we have created a parameter num with a default value of 5 which is used to limit the number of objects that will be returned from the database, we have the option of providing a different value for num when we use this template tag.

Let's now write the code for what we want to display using all_persons.html.

Edit all_persons.html to include the code below.

<ul>
    {% for person in people %}
        <li>{{person.last_name}} {{person.first_name}}</li>
    {% endfor %}
</ul>

Update the html file where you intend to use the new template tag as shown below.

{% load app_tags %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Custom template tags</title>
</head>
<body>
    <h1>The total number of products is {% total_products %}</h1>

    <h2>Some people in the database </h2>
    {% get_names 2 %}
</body>
</html>

Our new template tag get_names is called passing in the number of names we want to display we have specified a number 2.

Populate the database of Persons to see this effect. You can do this using the Django administration site.

If setup correctly, this should be the resulting HTML

Observe that the unordered list we specified in all_persons.html is now rendered, this indicates that everything is working as expected. You can include this custom tag in any other html file where you want to display the names of people, all you have to do is ensure that you have loaded the app_tags using {% load app_tags %}

Creating custom template can come in very handy especially if reusability is something you fancy.

I hope this helps. You can share the custom template tags you are able to create with me on my twitter handle or reach out to me on Gmail at , I'll love to see the amazing functionalities you are able to implement using this feature.