The Django Template Language: Tags and Filters

Django Templates: Built-In Tags and Filters

by Christopher Trudeau intermediate django

Django is a powerful framework for creating web applications in Python. Its features include database models, routing URLs, authentication, user management, administrative tools, and a template language. You can compose reusable HTML that changes based on the data you pass to the template language. Django templates use tags and filters to define a mini-language that’s similar to Python—but isn’t Python.

You’ll get to know Django templates through the tags and filters you use to compose reusable HTML.

In this tutorial, you’ll learn how to:

  • Write, compile, and render a Django template
  • Use the render() shortcut in views to quickly use templates
  • Use template tags for conditionals and loops in your templates
  • Create reusable templates with inheritance and inclusion
  • Modify the presentation of your data through template filters

Creating a Django Project

To experiment with Django templates, you’re going to need a project so that you can play around with the code. You’ll be building moviepalace: the world’s smallest, simplest movie website. For a more detailed example of starting a new project, you can read Get Started With Django Part 1: Build a Portfolio App.

Django isn’t part of the standard Python library, so you’ll first need to install it. When dealing with third-party libraries, you should use a virtual environment. For a refresher on virtual environments, you can read over Python Virtual Environments: A Primer.

Once you have a virtual environment, run the following commands to get going:

 1$ python -m pip install django==3.2.5
 2$ django-admin startproject moviepalace
 3$ cd moviepalace
 4$ python startapp moviefacts

Line 1 installs Django into your virtual environment using pip. On line 2, the django-admin command creates a new Django project called moviepalace. A Django project consists of apps, where your code lives. The fourth command creates an app named moviefacts.

You’re almost ready to go. The last step is to tell Django about your newly created moviefacts app. You do this by editing the moviepalace/ file and adding "moviefacts" to the list of INSTALLED_APPS:

34    "django.contrib.admin",
35    "django.contrib.auth",
36    "django.contrib.contenttypes",
37    "django.contrib.sessions",
38    "django.contrib.messages",
39    "django.contrib.staticfiles",
40    "moviefacts",

With moviefacts registered as an app, you can now write a view containing a template.

Getting Ready to Use Django Templates

Django was created at a newspaper to help build web applications quickly. One of the goals of the framework was to separate the concerns of the business logic from the presentation logic.

Web designers, rather than Python programmers, frequently did the HTML development at the paper. Because of this, the developers decided not to allow the execution of Python within the template language. This decision simplified what the designers needed to know and sandboxed their code for security reasons. The end result was a separate mini-language. This approach is in contrast to the PHP approach, where the code is directly embedded in the HTML.

Compiling and Rendering Django Templates

Django templates let you dynamically change output content within a rendering context. You can think of templates as a form letter, where the letter’s contents include places where information can be inserted. You can run the rendering process multiple times with different data and get different results each time.

Django provides the Template and Context classes to represent the string template being rendered and the data being used during generation. The Context class is a wrapper to a dict and provides key-value pairs to populate the generated content. The result of a rendered template can be any text but is frequently HTML. Django is a web framework, after all.

It’s time to build your first template. To see one in action, you’ll first need a view. Add the following code to moviefacts/

 1# moviefacts/
 2from django.http import HttpResponse
 3from django.template import Context, Template
 5def citizen_kane(request):
 6    content = """{{movie}} was released in {{year}}"""
 7    template = Template(content)
 8    context = Context({"movie": "Citizen Kane", "year": 1941})
10    result = template.render(context)
11    return HttpResponse(result)

In this view, you see some of the main concepts that make up the Django templating language:

  • Line 6 contains references to movie and year. This is similar to a Python f-string. The double braces, or mustache brackets, indicate the items that Django replaces when it renders the template.
  • Line 7 instantiates a Template object by passing in the string that specifies the template.
  • Line 8 creates a Context object by populating it with a dictionary. The Context object contains all of the data available to the template when Django renders it. The template contains two items to replace: {{movie}} with "Citizen Kane" and {{year}} with 1941.
  • Line 10 has the call to the .render() method that generates the result.
  • Line 11 returns the rendered content wrapped in an HttpResponse object.

To test this out, you’ll need to make this view available in the browser, so you’ll need to add a route. Modify moviepalace/ as follows:

# moviepalace/
from django.urls import path
from moviefacts import views

urlpatterns = [
    path("citizen_kane/", views.citizen_kane),

Make sure you’re using the virtual environment where you installed Django, then run the Django development server to see the result:

$ python runserver

Run your view by visiting

The HttpResponse object is returning this content as HTML, but as the string doesn’t contain any tags, your browser will treat this as if it’s text inside of a <body> tag. It returns badly formed HTML, but it’s good enough to play with for now. If everything went well, you should see your template rendered with the data from your context:

Citizen Kane was released in 1941

Your template has been compiled, and Django replaced the movie and year variables with Citizen Kane and 1941.

Configuring Django to Load File Templates

In a web application, your most likely use of templates will be to output HTML—lots and lots of HTML. The Django template language was built to simplify this process. Unlike the previous example, you don’t typically use template strings in your views. Instead, you load templates from other files.

To load a template from disk, you first need to tell Django where to find it. Inside of moviepalace/, modify the "DIRS" value in TEMPLATES:

 2    {
 3        "BACKEND": "django.template.backends.django.DjangoTemplates",
 4        "DIRS": [
 5            BASE_DIR / "templates",
 6        ],
 7        "APP_DIRS": True,
 8        "OPTIONS": {
 9            "context_processors": [
10                "django.template.context_processors.debug",
11                "django.template.context_processors.request",
12                "django.contrib.auth.context_processors.auth",
13                "django.contrib.messages.context_processors.messages",
14            ],
15        },

In the default file that the django-admin command created, the DIRS list is empty. Django will now look for templates in a directory named moviepalace/templates. Note that Django uses a double-folder structure for its own configuration. For example, is found in moviepalace/moviepalace. The directory for templates should be in the project root, not in the configuration directory.

When APP_DIRS on line 7 is True, Django will also look for templates in the app subdirectories. Django expects app templates to be inside a directory named templates under the app’s folder.

In Django 3.1, the BASE_DIR parameter in the file changed from using os.path to pathlib:

        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [os.path.join(BASE_DIR, "templates"), ],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [

If you’re using Django 3.0 or earlier, the DIRS value will need to use os.path instead.

How do you decide where to store your templates? If your app is going to be reusable in other projects and has templates that are specific to it, then keep the templates with the app. Otherwise, keep your templates together in the project template directory. See the lesson on Django’s double folder structure in the course Getting Started With Django for more information about how to structure your Django projects.

After making the change to the moviepalace/ file, don’t forget to create the templates directory:

$ pwd
$ mkdir templates

With the configuration done and the directory created, you’re now ready to load a template from a file.

Loading Django Templates From Files

Let’s rebuild the Citizen Kane experience using a file. Create templates/simple.txt and add the template string used in your citizen_kane() view:

{{movie}} was released in {{year}}

You could write code to load the file as a string, build a Template object, and do the same thing you did in the citizen_kane() view, or you can use the render() shortcut, which does all of that for you. Add the following to your moviefacts/ file:

# moviefacts/

from django.shortcuts import render

def casablanca(request):
    return render(
        request, "simple.txt", {"movie": "Casablanca", "year": 1942}

With the new view in place, don’t forget to add a route to moviepalace/

# moviepalace/
from django.urls import path
from moviefacts import views

urlpatterns = [
    path("citizen_kane/", views.citizen_kane),
    path("casablanca/", views.casablanca),

Visiting should produce similar results to the Citizen Kane example:

Casablanca was released in 1942

The render() shortcut is the usual way of rendering templates. Using the Template object directly is only done in rare cases, like when you want to give the power of templates to your users.

An example of using Template directly could be when allowing your user to input a form letter. A form letter might contain variables like the recipient of the letter. By allowing the user to use a Django template, you can take advantage of the built-in variable substitution mechanism for replacing the recipient’s name.

Choosing a Template Language

Django supports multiple template engines. It ships with two:

  • The Django template language: the original Django templating language and the one you’re learning about in this tutorial
  • Jinja2: formerly a third-party-only library that is now included in Django but is outside the scope of this tutorial

You can change which template engine is used by editing the TEMPLATES value in moviepalace/

 2    {
 3        "BACKEND": "django.template.backends.django.DjangoTemplates",
 4        "DIRS": [
 5            BASE_DIR / "templates",
 6        ],
 7        "APP_DIRS": True,
 8        "OPTIONS": {
 9            "context_processors": [
10                "django.template.context_processors.debug",
11                "django.template.context_processors.request",
12                "django.contrib.auth.context_processors.auth",
13                "django.contrib.messages.context_processors.messages",
14            ],
15        },

The BACKEND setting on line 3 is where you specify your rendering engine. You can choose either the Django template engine or the Jinja2 engine by changing BACKEND to the dotted-path module name of the engine:

  • django.template.backends.django.DjangoTemplates
  • django.template.backends.jinja2.Jinja2

Third-party template engines are also available. Using them requires installing the library through pip and changing the BACKEND value to the dotted-path name of the engine.

The rest of this tutorial will focus solely on the original Django template backend to give you a solid foundation for creating HTML in your Django projects.

Learning About Django Templates, Tags, and Filters

So far, you’ve seen templates that contain simple variable substitution. The Django template language goes much deeper than that. You get access to many of the structures and controls that you’re used to in Python, except within its own mini-language.

Django Template Tags and Filters

The Django template language has three ways of controlling what gets rendered: values, tags, and filters. Everything you put into a template that is not one of these three gets rendered as you have written it. In this tutorial, you’ll go over the three main parts of the template language:

  1. Interpreted data, which you note by double braces, {{ value }}
  2. Tags, which you note by braces and percent signs, {% tag_name %}
  3. Filters, which modify interpreted data and you apply with the pipe operator (|), like in {{ value | filter }}

As you saw in the previous section, when Django renders a template into text, it uses a special dictionary that’s called Context. The context is the state with which the template is rendered. In addition to containing the interpreted data to be rendered with double braces, the content of the Context object can be used to make logic decisions.

Tags are like the template language’s keywords and functions. In Python, keywords and functions provide control flow and the tools your code is built upon. Similarly, Django’s built-in tags provide inheritance, conditional operation, looping, comments, and text management. For example, the {% lorem %} tag is replaced with sample lorem ipsum text when rendered.

Django filters operate inside of the double braces and let you alter the presentation of the data you display. For example, the date filter formats a date-time object similar to how strftime() works in Python. If the Context dictionary contained a datetime object named today, {{ today | date:"Y"}} applies the date filter to today, returning the year.

The rest of this tutorial will guide you through common tags and filters, giving examples of how you use each.

Template Inheritance and Inclusion

There’s a lot of boilerplate inside of HTML. Most websites have a common look shared by every page. Every page rendered often repeats the same header and footer, which includes the same style sheets, and often includes the same JavaScript files for metrics and measuring. Manually repeating this across every page would mean that making a change would require a lot of work. Template inheritance and inclusion to the rescue!

There are two ways of composing parts of Django templates together. Inheritance works like class inheritance in Python, with templates overriding their parents’ values. Inclusion injects content into the template from another template. This is similar to the #include directive in the C programming language.

To see these in action, create templates/base.html:

 1<!-- templates/base.html -->
 4  {% block heading %}
 5    <h1>Movie Palace</h1>
 6  {% endblock heading %}
 8  {% block content %}
 9  {% endblock content %}

This declaration is like a base class in Object-Oriented Programming. You can use it on its own, or you can set up other templates to inherit from it. If this template is rendered as-is, the contents of the {% block %} tags will display as they are. This provides a useful default in the case where the child doesn’t override the block.

In base.html, there’s a heading that says <h1>Movie Palace</h1>, and there’s no content. The magic happens when another template inherits from this one. In the child template, you can optionally override any of the defined blocks. A common practice is filling in the content block with the content for a page while leaving the boilerplate HTML inside this base file. Create a template that inherits from base.html called templates/falcon.html:

 1<!-- templates/falcon.html -->
 2{% extends "base.html" %}
 4{% block heading %}
 5  {{block.super}}
 6  <h2>The stuff that dreams are made of</h2>
 7{% endblock heading %}
 9{% block content %}
10  <p>
11    {% include "simple.txt" %}
12  </p>
13{% endblock content %}

The falcon.html file shows two things: inheritance and inclusion. You inherit a parent file by using the {% extends %} tag. Here, falcon.html inherits from base.html. Django renders the base.html file in full, except when the child falcon.html overrides a block. For example, the content section in falcon.html overrides the block with the same name in base.html.

Inside of a block section, a special variable is defined: {{block.super}}. This variable contains whatever was in the parent block. On line 5, the parent block brings in the <h1>Movie Palace</h1> title, while falcon.html adds the <h2> to the block on line 6.

In addition to inheritance, you can also do inclusion. On line 11, the {% include %} tag inserts the contents of simple.txt. This allows you to reuse snippets of HTML. Here, it’s reusing the template originally defined for the casablanca() view.

To see all this in action, you’ll need a new view to render them. Create the following in moviefacts/

# moviefacts/

def maltese_falcon(request):
    return render(
        {"movie": "Maltese Falcon", "year": 1941},

This view is almost identical to casablanca(), except it renders falcon.html and passes in the appropriate movie data. Update your moviepalace/ file with a route for the maltese_falcon view and then visit the page:

Rendered result of inheritance using the extends tag

You overrode the header, you included simple.txt, and Django rendered it all with the Maltese Falcon movie data.

Inside of falcon.html, the {% extends %} tag hard-codes the name of the parent template. This is the most common case, but the parent template can be named in a context variable instead. This trick can allow you to use the same child page in both a logged-in and public state, changing the parent template based on the situation.

The falcon.html template only uses one level of inheritance, but you aren’t limited to that alone. Grandchildren can inherit from children, which in turn inherit from parents. You can have as many layers as needed to organize your output.

Exploring Common Template Tags

You can take advantage of over twenty-five built-in tags that Django 3.2.5 includes. These tags are like the keywords and functions of the template language. They allow you to do conditional logic, loops, inheritance, text manipulation, and much more.

Conditional Code

In Python, you use the if, elif, and else keywords to execute code conditionally. In Django templates, you use tags to accomplish the same thing.

To see these tags in action, you’ll need a view with a bit more data. Edit your moviefacts/ file and add the following function:

# moviefacts/

def psycho(request):
    data = {
        "movie": "Psycho",
        "year": 1960,
        "is_scary": True,
        "color": False,
        "tomato_meter": 96,
        "tomato_audience": 95,
    return render(request, "psycho.html", data)

Add a corresponding route for psycho to moviepalace/, then create templates/psycho.html:

 1<!-- templates/psycho.html -->
 2{% extends "base.html" %}
 4{% block content %}
 5  <p>
 6    {{movie}} was released in {{year}}.  It was {% if not is_scary %}not
 7    {% endif %}scary.
 8  </p>
10  <p>
11    {% if color %}
12      Color
13    {% else %}
14      Black and white 
15    {% endif %}
16  </p>
18  <p>
19    {% if THX %}
20      Sound was awesome
21    {% endif %}
22  </p>
24  <p>
25    {% if tomato_meter > tomato_audience %}
26      Critics liked it better than audience
27    {% elif tomato_meter == tomato_audience %}
28      Critics liked it the same as audience
29    {% else %}
30      The audience liked it better than the critics
31    {% endif %}
32  </p>
34  <p>Copyright <b>MoviePalace</b></p>
35{% endblock content %}

The {% if %} tag works like Python’s if keyword. If the condition tested is True, then the block is rendered.

  • Lines 6 to 7 show the use of an inline if-conditional, with the is_scary value negated with the not keyword.
  • Lines 11 to 15 show the use of a multi-line if-condition. This condition block also contains an {% else %} clause. Unlike writing if statements in Python, though, don’t forget the closing {% endif %} tag.
  • Line 19 checks the THX variable, which Django won’t find. A variable that isn’t in the context is considered False, so this block about the sound quality doesn’t get rendered.
  • Lines 25 to 31 show the use of conditions with Boolean comparison operators. You can use the same kinds of operators you’d use in Python inside of the tags.

If you run your development server and visit the view, you’ll notice that the conditional tags have worked:

Rendered example page with if, elif, else, and endif conditional tags

Conditional tags let you control the display of HTML blocks with the same power as condition sections in your Python code. Play around with the data in the psycho() view and see how that changes the output of the conditional blocks.


There’s a single Django template tag for looping: {% for %}. It uses a similar syntax to Python’s for statement and provides some built-in variables that give information about where you are in the iteration.

The movie views so far have had very little data in them with nothing to loop over. To play with loops, you’ll want to add another view to moviefacts/

# moviefacts/

def listing(request):
    data = {
        "movies": [
                "Citizen Kane",   # Movie
                1941,             # Year
    return render(request, "listing.html", data)

This view has a context variable called movies, which is a list. The template you’ll be building will use loop tags to iterate over this list. Create templates/listing.html as follows:

 1<!-- templates/listing.html -->
 2{% extends "base.html" %}
 4{% block content %}
 5  <h2> Movies</h2>
 6  <ul>
 7    {% for movie in movies %}
 8      <li>
 9        #{{forloop.counter}} {{movie.0}} was released in {{movie.1}}
10      </li>
11    {% endfor %}
12  </ul>
14  <h2> Goodies </h2>
15  <ul>
16    {% for goodie in confectionaries %}
17      <li> {{goodie.0}} &mdash; ${{goodie.1}}</li>
18    {% empty %}
19      <li> <i>There are no goodies available</i> </li>
20    {% endfor %}
21  </ul>
22{% endblock content %}

Lines 7 to 11 contain a {% for %} block, which is similar to the for keyword in Python. The block iterates over movies and assigns a local variable named movie during each loop. Inside of the block, you write HTML that takes advantage of double-brace pairs to display the content inside of <li> tags.

Lines 8 to 10 are repeated once for each iteration. The loop is using a special object called forloop. This is a local variable inserted in the template context by Django. The object has several members, each of which contains information about the current iteration of the loop:

Variable Description
forloop.counter 1-based index number of the iteration
forloop.counter0 0-based index number of the iteration
forloop.revcounter Number of iterations left in the loop (1-indexed)
forloop.revcounter0 Number of iterations left in the loop (0-indexed)
forloop.first True if this is the first iteration
forloop.last True if this is the last iteration
forloop.parentloop Contains the context of the parent loop inside nested loops

Let’s examine the line inside of the loop block some more:

 7<!-- From inside of templates/listing.html -->
 9  #{{forloop.counter}} {{movie.0}} was released in {{movie.1}}

The content of movies is a list of tuples. Each tuple contains the name and year of a movie. Django template tags don’t allow the use of subscripts. Instead of using square brackets to access a value in a tuple or list as you do in Python, you use a number as a property of the object. The values of {{movie.0}} and {{movie.1}} are the first and second values of the movie tuple.

The Django loop also supports a special tag called {% empty %}. Open up listing.html again for an example of this tag:

13<!-- From inside of templates/listing.html -->
14{% for goodie in confectionaries %}
15  <li> {{goodie.0}} &mdash; ${{goodie.1}}</li>
16{% empty %}
17  <li> <i>There are no goodies available</i> </li>
18{% endfor %}

This tag is like an else clause. If there’s nothing to iterate over in the {% for %} tag, Django runs the {% empty %} block instead.

The {% empty %} tag is more convenient than wrapping the {% for %} block in an {% if %} block to achieve the same result. In this case, there’s no confectionaries defined in the context. The Django template language treats missing values as empty, so it renders the {% empty %} portion of the block.

Add a route in moviepalace/, fire up your Django development server, and visit to see the result:

Django templates example, MoviePalace listing

There’s one more loop-like tag supported: {% cycle %}. This tag takes a series of arguments and returns each in turn every time it’s called:

{% for movie in movies %}
    <tr class="{% cycle 'row1' 'row2' %}">
{% endfor %}

When {% cycle %} runs out of arguments, it cycles back to the beginning. The most common use of this is adding stripes to tables, alternating the style value of each row:

Example striped table using the cycle tag

You could achieve the same result with a {% for %} tag, but that approach would require more code.


Although you can write comments in HTML using <!-- -->, the response downloaded to the user’s browser includes those comments. Django includes a {% comment %} tag, whose contents are stripped out entirely:

{% comment %}
    Nothing to see here!
{% endcomment %}

You can also include a string parameter to the {% comment %} tag that acts as a note. It might seem a little strange, but it’s useful if you’re commenting out code. You use the block to comment out the code, and then the note to remind yourself why you commented the code out.

Special Characters

By default, the Django template renderer automatically escapes any variables that it renders. This tries to prevent your HTML from being corrupted if a string contains a character special to HTML, like the angled brackets (<, >) used in HTML tags. The {% autoescape %} tag allows you to control this behavior:

{% autoescape off %}
    {{ content }}
{% endautoescape %}

The {% autoescape %} tag takes a single parameter of on or off, turning the escape mechanism on or off, respectively. Consider the following template:

{% autoescape on %}
    Escaped: {{ my_text }}
{% endautoescape %}
{% autoescape off %}
    Not Escaped: {{ my_text }}
{% endautoescape %}

If my_text contained "Hello <b>world</b>", the rendered content would have two versions of the string:

Example rendering of the autoescape tag

autoescape on changes the HTML special characters so that they render as they are in the string. By contrast, with autoescape off, the bold tag is passed through to the browser.

All languages that use special characters need a way of using those same special characters directly. If you want to use the characters that indicate a tag, you can wrap them in the {% verbatim %} tag:

{% verbatim %}
    Django uses mustaches braces to render variables: {{ is_scary }}.
{% endverbatim %}

Many JavaScript frameworks have their own templating languages. The double-brace format for variables or tags is quite common. If you need to include JavaScript templates inside of your Django template, wrapping them in the {% verbatim %} tag will cause Django’s template renderer to ignore them, leaving them in place for JavaScript.

Dates and Times

You often need to show date and time information on a website. The {% now %} tag in the Django template language gives you the current date and time. It takes a string parameter specifying the format of the datetime to display. For historical reasons, the format string is based on the PHP date() function rather than Python’s strftime. A listing of the possible format characters is available in the documentation.

A common use of the {% now %} tag is displaying the current year as part of a copyright statement:

<p>Copyright 2012-{% now "Y" %}</p>

The example above will render a copyright notice with years ranging from 2012 to the current one.


Django’s view and routing mechanism has a built-in way of naming your URLs. Hard-coding URLs is bad practice. If something changes, then you have to hunt down all of the times you used it in your code. Instead of resolving a URL in a view and passing it in as context, you can use the {% url %} tag:

<a href="{% url 'home_page' filter %}">Home</a>

The argument to {% url %} is any URL pattern name, the same way that Django’s reverse() works. It optionally takes positional or named arguments to the underlying view being referenced.

Exploring Key Template Filters

Like you learned earlier, Django template tags are the keywords and functions of the template language. Filters are like small functions that modify data in place before Django renders it.

You can put filters inside of double braces by adding a pipe (|) and the filter name after the variable. This is inspired by how you can pipe commands to each other in Unix shells.

Now you’ll look at some common filters used with strings, lists, and dates and times, along with examples that show how to use them.

String Filters

Filters can operate on all kinds of data. Many of them directly correspond to Python functions with the same purpose. The upper filter is the same as the str.upper() method in Python. Using it changes the contents of a variable to uppercase. Try it out by modifying templates/listing.html, adding the upper filter to the movie.0 variable:

<!-- templates/listing.html -->
{% extends "base.html" %}

{% block content %}
  <h2> Movies</h2>
       #{{forloop.counter}} {{movie.0|upper}} was released in {{movie.1}}

With the change above, each of the movie titles in the listing will be in shout-case. Not surprisingly, there’s also a corresponding lower filter.

The center filter adds padding to both sides of a string. The padding is a space character, so depending on your font and what tag wraps it, this filter may have limited effect:

<pre>{{value}} becomes *{{ value|center:"16" }}*</pre>

If value contains "Elephant", this example results in four spaces on each side:

<pre>Elephant becomes *    Elephant    *</pre>

If you need to remove characters from a string, you can use the cut filter. The argument to cut is the substring to remove:

<pre>*{{value}}* becomes *{{ value|cut:" " }}*</pre>

Take a look at what happens when you use the cut filter with a value of "Bates Motel":

<pre>*Bates Motel* becomes *BatesMotel*</pre>

Note that the argument to cut is not a list of characters you want to cut but the sequence you want to cut. If you tried to cut this value with "ae" instead, nothing would happen. There’s no "ae" substring in "Bates Motel".

List Filters

Filters can operate on list-like values. The first and last filters return the first and last items in an iterable, respectively:

{{ goodies|first }}

If goodies contains ["popcorn", "peanuts", "cola"], then this example results in "popcorn". Using the last filter would result in "cola".

In addition to using filters to access parts of a list, you can use them to get data from a list. The join filter is similar to the Python str.join() method. It returns a string composed from the parts of the list:

{{ goodies|join:", " }}

You use a colon (:) to pass parameters to a filter. Joining the goodies list with a comma as a parameter returns a single string containing a comma-separated list: "popcorn, peanuts, cola".

To find the length of a list, you use the length filter. This can be particularly useful when combined with other filters. The pluralize filter adds an "s" to the end of a word when dealing with multiple items. Try adding the following line to templates/listing.html just below the <h2>Movies</h2> heading:

<!-- templates/listing.html -->
{% extends "base.html" %}

{% block content %}
  <h2> Movies</h2>
  <p>{{movies|length}} movie{{movies|length|pluralize}} found.</p>

Django replaces the first set of double braces with the length of the movies list. The second set is doing something clever: chaining filters. The pluralize filter takes a number. If the number is 2 or larger, it returns an "s". By using length and then pluralize, you are passing the length of movies to pluralize.

What do you do if adding an "s" isn’t how you pluralize the word? The filter supports an argument that specifies the ending suffix:

You have {{num_boxes}} box{{num_boxes|pluralize:"es"}}.

In this example, instead of appending "s", pluralize appends "es", returning "boxes". For words that have different endings, you can specify both the single and plural endings:

You have {{num_cherries}} cherr{{num_cherries|pluralize:"y,ies"}}.

In the above example, pluralize adds the string specified before the comma if the result is singular and adds the string specified after the comma if the result is plural. This way, you correctly get "cherry" or "cherries".

Date and Time Filters

The presentation of a date or time should be specific to the locale of the user. Within your code, it’s recommended that you use only a Python datetime object. You should leave the formatting of this object to rendering time. There are filters to help you with this.

The date filter takes a parameter similar to the {% now %} tag that you saw earlier in the tutorial. Like that tag, the parameters for the date filter specify the presentation format of the date:

<p> {{release_date|date:"Y-m-d"}}</p>

This example would present the contents of release_date in a year-month-day ISO 8601 format. For Citizen Kane, that would be 1941-09-05 (September 5th, 1941) for its wide release. Although it’s called the date filter, it also supports times, assuming it’s working with a datetime object rather than just a date object.

If you’re only interested in the time, there’s a filter for that as well. The time filter uses a subset of the configuration parameters for date, just those having to do with time. The usage is similar:

<p> {{showing_at|time:"H:i"}}</p>

This example displays the showing time in 24-hour clock format. For a 9:30 p.m. movie, Django renders this as "21:30".

A popular format that makes time more readable is displaying the time that’s passed since an event. You see this in many webmail clients that show information like You received an email “3 days ago”. The Django timesince filter gives you this power:

<p> {{release_date|timesince}}</p>

This example shows the amount of time that’s passed since the release date of a movie. By default, this filter calculates the time that’s passed since now. Alternatively, you can add a parameter to timesince that specifies the comparison date.

There’s a companion filter to timesince called timeuntil. Instead of looking backward to a date, it looks forward. You can use this to display the countdown to a conference or a similar event.

Both timesince and timeuntil calculate the difference in time according to the server when Django renders the template. The calculation is done using the timezone information in This can create strange results if your server is not in UTC and your Django config is using the UTC default. Make sure these two configurations match, or your time difference calculation will be incorrect.

Third-Party Tag and Filter Libraries

In addition to the built-in Django template tags and filters, you can also write your own. You can find helpful third-party libraries that include custom tags and filters. One such library is django-simple-tags. Don’t confuse this with the similarly named function simple_tag() in Django.

To use a third-party library in your templates, you need to follow three steps:

  1. Install the app library
  2. Register the app with INSTALLED_APPS in
  3. Load the tags in your template

Installing a Django app library is no different than any other package. You can install it using pip:

$ python -m pip install django-simple-tags

With the package installed, update the INSTALLED_APPS portion of your file to make Django aware of the app:

34    "django.contrib.admin",
35    "django.contrib.auth",
36    "django.contrib.contenttypes",
37    "django.contrib.sessions",
38    "django.contrib.messages",
39    "django.contrib.staticfiles",
40    "moviefacts",
41    "django_simple_tags",

Now Django is aware of the app, but you can’t yet use the tags. Before you can use them, you first need to load them inside of the template. You do this with the {% load %} tag:

 1{% load django_simple_tags %}
 3Current value of DEBUG is {% get_setting "DEBUG" %}

On line 1, the {% load %} tag adds all of the tags from django-simple-tags into your template context. Note that there are no quotes around django_simple_tags. This is to be consistent with import statements. The django-simple-tags library has over twenty tags and filters. This example shows {% get_setting %} returning the value of DEBUG from Django settings.

There are many third-party libraries out there and plenty of tags that can simplify your HTML. Using them is just a pip install and a {% load %} tag away.


Django template tags and filters give you a powerful way to build HTML output in a reusable fashion. Templates are defined in their own language in order to separate the business logic from the display logic. Tags are like the keywords and functions of the language, while filters allow you to modify existing data before displaying it.

In this tutorial, you learned how to:

  • Use the Django Template and Context objects to compile a template
  • Load templates from files and return them using the render() shortcut
  • Write templates with conditional blocks and looping blocks
  • Use extends and include to write reusable template components
  • Modify data using template filters
  • Load and use custom template tags and filters from third-party libraries

You can find more general information on Django in the excellent documentation. In the same documentation, you can also find more specific information on template tags and filters.

If you want to follow along with some Django tutorials on Real Python, you can check out Get Started With Django Part 1: Build a Portfolio App, the Django for Web Development Learning Path, and the list of available Django tutorials.

And the final appropriate word from the moviepalace tutorial would have to be: Rosebud.

🐍 Python Tricks 💌

Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

About Christopher Trudeau

Christopher has a passion for the Python language and writes for Real Python. He is a consultant who helps advise organizations on how to improve their technical teams.

» More about Christopher

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Master Real-World Python Skills With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

Master Real-World Python Skills
With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Rate this article:

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students. Get tips for asking good questions and get answers to common questions in our support portal.

Looking for a real-time conversation? Visit the Real Python Community Chat or join the next “Office Hours” Live Q&A Session. Happy Pythoning!

Keep Learning

Related Topics: intermediate django