Django templates help you manage your spider web application'southward HTML. Templates use a mini-language with variables, tags, and filters. You can conditionally include blocks, create loops, and modify variables earlier they're shown. Django comes with many congenital-in tags and filters, but what if they're not enough? In that case, write your ain! This tutorial covers the ins and outs of writing your ain Django template custom tags and filters.

In this tutorial, you'll learn how to:

  • Write and register a function as a custom filter
  • Understand how autoescaping works in custom tags and filters
  • Use @simple_tag to write a custom template tag
  • Apply @inclusion_tag to render a tag based on a subtemplate
  • Write a complex template tag with a parser and renderer

By the end of the tutorial, you'll be able to write custom filters to modify data in your templates and custom tags that give yous access to the full ability of Python within your templates.

Getting Started

To play around with your ain Django template custom tags and filters, you're going to need a Django projection. You'll build dinosoar, a small website with all sorts of dinosaur info. Although the proper name implies that you'll merely include flying dinos, that's only for marketing spin. All your favorite heavyweights will be there as well.

If you've never fix upwards a Django project before or if you demand a refresher, you may want to read Get Started With Django Function 1: Build a Portfolio App start.

Django is a third-party library, so it should be installed in a virtual surroundings. If you lot're new to virtual environments, check out Python Virtual Environments: A Primer. Create and activate a new virtual environment for yourself so run the following commands:

                                                              ane                $                python -m pip install                django                ==                3.2.5                                  2                $                django-admin startproject dinosoar                                  iii                $                                cd                dinosoar                                  4                $                python manage.py startapp dinofacts                                  5                $                python manage.py drift                          

These commands perform the following actions:

  • Line 1 runs the pip command to install Django.
  • Line 2 creates your new Django project.
  • Line 3 changes the electric current working directory to the dinosoar project.
  • Line iv uses the manage.py command to create a Django app called dinofacts, where your main view will live.
  • Line 5 migrates any database changes. Even if you aren't creating models, this line is necessary because the Django admin is agile by default.

With the project created, it'due south fourth dimension to make some configuration changes and write a quick view to aid you test your custom tags and filters.

Setting Upwards a Django Project

Yous demand to make some changes to your project's settings to make Django enlightened of your newly created app and to configure your templates. Edit dinosoar/dinosoar/settings.py and add dinofacts to the INSTALLED_APPS list:

                                                  34                  # dinosoar/dinosoar/settings.py                  35                  36                  INSTALLED_APPS                  =                  [                  37                  "django.contrib.admin"                  ,                  38                  "django.contrib.auth"                  ,                  39                  "django.contrib.contenttypes"                  ,                  40                  "django.contrib.sessions"                  ,                  41                  "django.contrib.messages"                  ,                  42                                      "django.contrib.staticfiles"                    ,                                    43                  "dinofacts"                  ,                  44                  ]                              

Within the same file, you lot'll need to update the DIR value in the TEMPLATES aspect. This tells Django where to await for your template files:

                                                  57                  # dinosoar/dinosoar/settings.py                  58                  59                  TEMPLATES                  =                  [                  sixty                  {                  61                  "BACKEND"                  :                  "django.template.backends.django.DjangoTemplates"                  ,                  62                                      "DIRS"                    :                    [                                    63                  BASE_DIR                  /                  "templates"                  ,                  64                  ],                  65                  "APP_DIRS"                  :                  True                  ,                  66                  "OPTIONS"                  :                  {                  67                  "context_processors"                  :                  [                  68                  "django.template.context_processors.debug"                  ,                  69                  "django.template.context_processors.request"                  ,                  70                  "django.contrib.auth.context_processors.auth"                  ,                  71                  "django.contrib.messages.context_processors.messages"                  ,                  72                  ],                  73                  },                              

Starting with Django 3.1, the BASE_DIR value that specifies where the projection lives is a pathlib object. The alter to the DIRS value in a higher place tells Django to wait in a templates/ subdirectory within your project directory.

With the settings changed, don't forget to create the templates/ directory within your project:

                                                  $                                    pwd                  /home/realpython/dinosoar                  $                  mkdir templates                              

It'south fourth dimension to start writing some lawmaking. To exam your custom template tags and filters, yous'll need a view. Edit dinosoar/dinofacts/views.py as follows:

                                                                      one                  # dinosoar/dinofacts/views.py                                      2                                      three                  from                  datetime                  import                  datetime                                      4                  from                  django.shortcuts                  import                  return                                      5                                      six                  def                  show_dino                  (                  request                  ,                  name                  ):                                      7                  data                  =                  {                                      8                  "dinosaurs"                  :                  [                                      9                  "Tyrannosaurus"                  ,                  10                  "Stegosaurus"                  ,                  11                  "Raptor"                  ,                  12                  "Triceratops"                  ,                  xiii                  ],                  14                  "at present"                  :                  datetime                  .                  at present                  (),                  xv                  }                  16                  17                  render                  render                  (                  request                  ,                  name                  +                  ".html"                  ,                  data                  )                              

Lines 7 to 15 create a lexicon with some sample information. Yous'll apply this in your templates to exam your tags and filters. The rest of this view does something a piffling unorthodox: it takes a parameter that specifies the name of a template.

The return() office loads and renders a template. Here, the name value gets suffixed with ".html", turning information technology into the proper name of a template to load. This is not something you'd normally practice, but the remainder of this article shows you lot a lot of templates. Rather than having to write a new view for each experiment, this single view will accomplish the task.

The last step before writing a template is to register your view every bit a URL. Edit dinosoar/dinosoar/urls.py so that it looks like this:

                                                                      1                  # dinosoar/dinosoar/urls.py                                      2                                      iii                  from                  django.urls                  import                  path                                      4                                      five                  from                  dinofacts.views                  import                  show_dino                                      6                                      vii                  urlpatterns                  =                  [                                      8                  path                  (                  "show_dino/<str:name>/"                  ,                  show_dino                  ),                                      9                  ]                              

Line 8 registers the /show_dino/ URL with the show_dino view. This URL expects an argument called proper noun, which volition be turned into the name of the template to load inside the view.

Using Django Templates

With the view in place, you're all set to render some HTML. The side by side step is to create a template. Almost HTML pages are rather repetitive, containing boilerplate header information, meta-info about the page, and navigation tools. Django templates apply the ability of inheritance to minimize the repetition. To see this in action, create dinosoar/templates/base.html:

                                                                      one                  <!-- dinosoar/templates/base.html -->                                      ii                                      3                  <                  html                  >                                      4                  <                  body                  >                                      v                  {%                  cake                  content                  %}                                      6                  <                  h1                  >Dino Soar</                  h1                  >                                      7                  {%                  endblock                  content                  %}                                      eight                  </                  body                  >                                      9                  </                  html                  >                              

By defining blocks and extending parent templates, you lot can avert a lot of the cookie-cutter duplication that often happens in HTML files across a site. The template above is a base for all time to come templates in this tutorial.

Many of the examples in this tutorial volition be in their own template files. You'll be creating each ane, and each will inherit from the base of operations file above. The first case child file you need to create is dinosoar/templates/eoraptor.html:

                                                                      i                  <!-- dinosoar/templates/eoraptor.html -->                                      2                                      3                  {%                  extends                  "base.html"                  %}                                      four                                      5                  {%                  block                  content                  %}                                      half dozen                                      7                  <                  h1                  >Eoraptor</                  h1                  >                                      8                                      9                  <                  p                  >                  10                  Eoraptor was ane of the earliest dinosaurs and its name                  xi                  is based on the Greek word for "dawn".                  12                  13                  {%                  comment                  %}                                      Add something about tiptop here                                    {%                  endcomment                  %}                  14                  fifteen                  </                  p                  >                  16                  17                  <                  p                  >                  18                  Some other popular dinosaurs were:                  19                  20                  <                  ul                  >                  21                  {%                  for                  dino                  in                  dinosaurs                  %}                  22                  <                  li                  >                  {{                  dino                  |                  lower                  }}                  </                  li                  >                  23                  {%                  endfor                  %}                  24                  </                  ul                  >                  25                  </                  p                  >                  26                  27                  {%                  endblock                  content                  %}                              

This template uses a few common built-in template tags and filters:

  • Line iii declares that this template inherits from "base.html" by using the {% extends %} tag.
  • Lines five and 27 declare a block chosen content. During the rendering of eoraptor.html, the template engine looks for a cake with the same name in its parent. The engine replaces the corresponding block in base.html.
  • Line 13 uses the {% comment %} tag to write a annotate. The rendered output volition not include the contents of this tag.
  • Lines 21 and 23 define a {% for %} block tag. This works like a for loop in Python. Information technology iterates over the value dinosaurs and renders the line within the block for each member.
  • Line 22 is rendered in one case for each particular in the dinosaurs list. Each value gets put inside HTML <li> tags. Note the lower filter used with the value. Filters get practical to a value through the pipe (|) symbol. Filters use a value so modify the rendered result. The lower filter is like to the .lower() method in Python, rendering the value as lowercase.

Everything is in place now. Run the Django development server to see the result:

                                                  $                  python manage.py runserver                              

To encounter the view, visit http://127.0.0.ane:8000/show_dino/eoraptor/. Your outcome should wait like this:

Result of a example template with a comment tag

The annotate has disappeared, and the list of dinosaurs is in lowercase.

You can find more data past going over the Django documentation about built-in template tags and filters or by checking out Django Templates: Built-In Tags and Filters.

Loading Modules and Third-Political party Tag Libraries

There are over seventy-five tags and filters built into Django and several modules on top of that, merely that still might non exist plenty for your use instance. But before writing your ain custom tags and filters, you should first practise some enquiry to see if another package meets your needs.

To utilize a tag or filter that comes with Django merely isn't in the standard set, you need to:

  1. Register the package's Django app
  2. Load the template library into your template

Many tertiary-party packages are bachelor as well. Using a third-party package is no different, except that y'all'd need to install the package first using pip.

A popular parcel that comes with Django but isn't function of the congenital-in library is humanize. This library has filters that change numeric data into more readable forms. Because it ships as office of the contrib module in Django, there'south no additional installation step.

To register the app, update your INSTALLED_APPS list in dinosoar/dinosoar/settings.py:

                                                  32                  # dinosoar/dinosoar/settings.py                  33                  34                  INSTALLED_APPS                  =                  [                  35                  "django.contrib.admin"                  ,                  36                  "django.contrib.auth"                  ,                  37                  "django.contrib.contenttypes"                  ,                  38                  "django.contrib.sessions"                  ,                  39                  "django.contrib.messages"                  ,                  40                  "django.contrib.staticfiles"                  ,                  41                                      "dinofacts"                    ,                                    42                  "django.contrib.humanize"                  ,                  43                  ]                              

Create a new template called dinosoar/templates/iggy.html:

                                                                      ane                  <!-- dinosoar/templates/iggy.html -->                                      two                                      3                  {%                  extends                  "base.html"                  %}                                      iv                  {%                  load                  humanize                  %}                                      5                                      half-dozen                  {%                  block                  content                  %}                                      7                                      8                  <                  h1                  >Iguanodon</                  h1                  >                                      nine                  10                  <                  p                  >                  11                  Iguanodon (iguana-tooth) were large herbivores. They weighed                  12                  {{                  3080                  |                  intcomma                  }}kg ({{                  6800                  |                  intcomma                  }}lbs).                  13                  Wow,                  {{                  3080000                  |                  intword                  }}                  grams is a lot!                  14                  15                  </                  p                  >                  16                  17                  {%                  endblock                  content                  %}                              

The key to using a tag or filter that isn't congenital-in is the {% load %} tag you utilise on line 4. This is the equivalent of an import statement in your Python lawmaking.

The iggy.html template uses two filters from humanize:

  1. intcomma converts a number into a cord with a comma every three digits.
  2. intword converts large numbers into their English equivalent.

Visit http://127.0.0.1:8000/show_dino/iggy/ to encounter the effect:

Result of template using django.contrib.humanize

In that location are a lot of tags in Django, both built-in and included in the contrib module. There are fifty-fifty more than tertiary-party libraries out there. But what if y'all tin't discover something that solves your trouble? It's time to practice it yourself by writing some Django custom tags and filters.

Building Tags and Filters

Custom tags and filters live in your Django app in a templatetags/ directory. Yous can import any files in this directory into a template using the {% load %} tag. The proper noun of the module you lot create will exist the name you apply to load the tag library.

For this project, the structure volition await like this:

                                dinosoar/dinofacts/templatetags/ ├── __init__.py └── dinotags.py                              

This directory is a module like any other Python code, and then information technology requires the __init__.py file. The dinotags.py file volition contain all the lawmaking for your custom tags and filters.

As you lot'll before long explore more, filters are functions. Tags tin can be either functions or classes, depending on their complexity. Just writing the functions and classes isn't sufficient—you also need to register the code with Django.

Registration requires an example of a Library class, which you tin can then apply equally a decorator to wrap your tags and filters. The following code is a simplified version of the built-in filter lower:

                                                                      1                  from                  django                  import                  template                                      2                                      3                  register                  =                  template                  .                  Library                  ()                                      4                                      5                  @register                  .                  filter                                      6                  def                  lower                  (                  value                  ):                                      7                  return                  value                  .                  lower                  ()                              

Think about how this example works:

  • Line 1 imports Django's template module.
  • Line 3 creates an case of Library used for registration.
  • Line 5 uses the Library case's filter method every bit a decorator. This tells Django that the lower() role is a filter.
  • Lines half dozen to 7 ascertain the role implementing the filter. The filtered value is the first statement to the function. This implementation assumes the value is a cord. The string's .lower() method gets called and the effect returned. What yous return from your filter function gets rendered in the template.

The Library object provides methods for registering tags and filters. You may call these methods directly, but the better fashion is to utilise them as decorators. Decorating the function makes it clear to other programmers that it'southward registered as a tag or filter.

Writing Django Template Custom Filters

You lot prepare your project and wrote a view to use for testing. Then, you used congenital-in tags and filters as well as loaded tags from a library. In the previous section, y'all learned how to annals tags and filters. In this section, you're all set to write your first Django custom filter!

Filters as Functions

As mentioned previously, filters are Python functions. The most basic filters have a single statement: the value to filter. The upshot of a filter function gets rendered past the template engine.

To get started, you'll write a filter that renders a cord composed of the first letter of the alphabet of each particular in a list. If you haven't already, you'll need to gear up your template tag file:

                                                  $                                    pwd                  /home/realpython/dinosoar                  $                  mkdir dinofacts/templatetags                  $                  touch dinofacts/templatetags/__init__.py                              

With the construction in place, either create or edit the template tag file called dinosoar/dinofacts/templatetags/dinotags.py:

                                                                      1                  # dinosoar/dinofacts/templatetags/dinotags.py                                      two                                      3                  from                  django                  import                  template                                      four                                      5                  register                  =                  template                  .                  Library                  ()                                      6                                      7                  @register                  .                  filter                                      8                  def                  first_letters                  (                  iterable                  ):                                      9                  event                  =                  ""                  10                  for                  particular                  in                  iterable                  :                  11                  result                  +=                  item                  [                  0                  ]                  12                  xiii                  return                  result                              

The code above registers a filter called first_letters. The function expects an iterable, similar a list. Information technology iterates over the list and builds the effect cord. If the value to filter is a list of strings, then result is the start alphabetic character of each of those strings.

To use this filter in action, create dinosoar/templates/raptor.html:

                                                                      one                  <!-- dinosoar/templates/raptor.html -->                                      2                                      3                  {%                  extends                  "base.html"                  %}                                      4                  {%                  load                  dinotags                  %}                                      5                                      half-dozen                  {%                  block                  content                  %}                                      vii                                      eight                  <                  h1                  >Velociraptor</                  h1                  >                                      9                  10                  <                  p                  >                  11                  The Velociraptor (swift seizer) was made famous by their appearance                  12                  in the motion-picture show                  <                  i                  >Jurassic Park</                  i                  >. Different in the film, these                  13                  dinosaurs were smaller, about the size of a turkey. They shared                  xiv                  something else with turkeys: they likely had feathers.                  15                  16                  </                  p                  >                  17                  <                  p                  >                  18                  The first letters of our dinosaur variable are                  {{                  dinosaurs                  |                  first_letters                  }}.                  xix                  20                  </                  p                  >                  21                  22                  {%                  endblock                  content                  %}                              

With your template in place, visit http://127.0.0.1:8000/show_dino/raptor/ to meet the result:

Result of template containing first_letter filter

Remember from dinofacts/views.py that the dinosaurs value is a list containing "Tyrannosaurus", "Stegosaurus", "Raptor", and "Triceratops". The issue above is the offset letter of each of these mighty reptiles: "TSRT".

Filters tin can also take arguments. Now you'll augment the ability of first_letters by writing a filter that returns the north-thursday letter of each item in an iterable. Add this function to dinotags.py:

                                                  23                  # dinosoar/dinofacts/templatetags/dinotags.py                  24                  25                  @register                  .                  filter                  (                  name                  =                  "nth_letters"                  ,                  is_safe                  =                  True                  )                  26                  def                  other_letters                  (                  iterable                  ,                  num                  ):                  27                  result                  =                  ""                  28                  for                  particular                  in                  iterable                  :                  29                  if                  len                  (                  detail                  )                  <=                  num                  or                  not                  item                  [                  num                  -                  1                  ]                  .                  isalpha                  ():                  30                  upshot                  +=                  " "                  31                  else                  :                  32                  event                  +=                  item                  [                  num                  -                  1                  ]                  33                  34                  render                  result                              

In that location are a few new things going on here:

  • Line 25 adds the name argument to the @register.filter() decorator. This makes the filter'southward name in the template different from the implementing part. Here, the filter gets named nth_letters fifty-fifty though the role that implements it is other_letters(). Notation that is_safe=Truthful indicates to Django that the output of this filter doesn't contain characters that will break HTML. You'll explore more almost this below.
  • Line 26 defines the function. The value to filter is the first argument, and the filter'southward parameter is the second.
  • Lines 28 to 32 iterate over the value and build the resulting cord.
  • Line 29 is a safety check. If yous're looking for the tenth index in an 8-alphabetic character string, it'll use a infinite (" ") instead. Likewise, if the north-thursday grapheme isn't a letter, y'all use a space to avoid accidentally returning characters that intermission HTML.
  • Line 34 returns the result string to be rendered.

Using strings inside of HTML safely is a deep topic. HTML consists of strings with sure characters irresolute how the browser displays the page. You lot have to be conscientious about what string data you pass to the rendering engine, and doubly so if the information was user input.

Django extends raw Python strings with a class chosen SafeString. A SafeString object has additional information in it that indicates whether the template engine should escape it before rendering.

When Django renders a template, parts of the template may be in autoescape mode. These areas automatically escape the values within, then Django will turn whatever troublesome characters into the corresponding HTML entity for brandish. Sometimes the values you're rendering are supposed to contain HTML, and so they need to exist marked safe.

In the case above, the is_safe=True argument to the registration decorator tells Django that this filter promises not to output any troublesome characters. A prophylactic cord passed to a filter does not get escaped by Django. The default value for is_safe is Fake.

Notation that is_safe=True is not marking your filter result as safe. That's a separate step for which you are responsible. The call to .isalpha() above ensures that all output from this function is rubber, so there'south no need for an extra pace.

Exist careful when determining whether your filter is rubber or not, especially when removing characters. A filter that removed all semicolons would pause HTML entities that depend on semicolons, like &amp;.

To play with the nth_letters filter, create dinosoar/templates/alberto.html:

                                                                      1                  <!-- dinosoar/templates/alberto.html -->                                      2                                      3                  {%                  extends                  "base.html"                  %}                                      4                  {%                  load                  dinotags                  %}                                      v                                      6                  {%                  cake                  content                  %}                                      seven                                      eight                  <                  h1                  >Albertosaurus</                  h1                  >                                      9                  ten                  <                  p                  >                  eleven                  Albertosaurus ('Alberta lizard') is a smaller cousin of                  12                  the T-Rex. These dinosaurs were named afterwards the location                  13                  of their first discovery, Alberta, Canada.                  14                  15                  </                  p                  >                  sixteen                  17                  <                  p                  >                  18                  The nth letters of our dinosaur variable are:                  19                  <                  ul                  >                  20                  <                  li                  >                  tertiary: "{{                  dinosaurs                  |                  nth_letters                  :                  iii                  }}"</                  li                  >                  21                  <                  li                  >                  fifth: "{{                  dinosaurs                  |                  nth_letters                  :                  5                  }}"</                  li                  >                  22                  <                  li                  >                  10th: "{{                  dinosaurs                  |                  nth_letters                  :                  10                  }}"</                  li                  >                  23                  </                  ul                  >                  24                  </                  p                  >                  25                  26                  {%                  endblock                  content                  %}                              

Visit http://127.0.0.1:8000/show_dino/alberto/ to get the resulting HTML:

Result of template containing nth_letter filter

Explore the text and check that your nth_letters filter works how yous'd expect.

The Canadian lizard might non take been king, but yous probably all the same wouldn't want to meet it in a dark alley.

String Filters

The information blazon of a filter'south argument is the data blazon of the value used inside the template. The most mutual data type within HTML documents is the cord. Django provides a style to coerce the filter's input into a string, and so y'all don't accept to do this manually. Now you'll write a new filter that outputs a judgement summarizing the number of instances of a letter in a cord.

Add together the following to your dinotags.py file:

                                                  35                  # dinosoar/dinofacts/templatetags/dinotags.py                  36                  37                  from                  django.template.defaultfilters                  import                  stringfilter                  38                  from                  django.utils.html                  import                  conditional_escape                  ,                  mark_safe                  39                  40                  @register                  .                  filter                  (                  needs_autoescape                  =                  True                  )                  41                  @stringfilter                  42                  def                  letter_count                  (                  value                  ,                  letter                  ,                  autoescape                  =                  True                  ):                  43                  if                  autoescape                  :                  44                  value                  =                  conditional_escape                  (                  value                  )                  45                  46                  upshot                  =                  (                  47                  f                  "<i>                  {                  value                  }                  </i> has <b>                  {                  value                  .                  count                  (                  letter                  )                  }                  </b> "                  48                  f                  "instance(s) of the letter of the alphabet <b>                  {                  letter                  }                  </b>"                  49                  )                  50                  51                  return                  mark_safe                  (                  consequence                  )                              

The @stringfilter decorator on line 41 indicates that this filter only takes strings. Django turns the filter's value into a string before passing it into the filter. At that place are some other interesting things going on in this function:

  • Line 40 uses the needs_autoescape parameter in the registration decorator. This tells Django to add together some other argument to the filter part: autoescape. The value of this argument will bespeak whether autoescaping is on or off for the telescopic of this filter.
  • Line 42 declares the filter function and includes the autoescape argument mentioned higher up. This statement should default to True so that you're in autoescape mode if your code calls the office directly.
  • Lines 43 to 44 replace value with the outcome of conditional_escape() if autoescape is Truthful. The conditional_escape() part escapes the string only is smart enough to not escape something that has already been escaped.
  • Lines 46 to 49 build the returning string. Because the letter_count filter outputs HTML with bold and italic tags, it has to be autoescape-aware. The f-string on line 47 uses the contents of value, which got appropriately escaped in lines 43 to 44, as needed. The result string contains value in italics and the letter count in bold.
  • Line 51 calls mark_safe() on the issue variable. Considering the filter is outputting HTML that should be displayed, the office must mark the cord as safe. This tells Django not to further escape the contents so the assuming and italic tags get rendered by your browser.

To test out this filter, create the following in dinosoar/templates/mosa.html:

                                                                      1                  <!-- dinosoar/templates/mosa.html -->                                      two                                      three                  {%                  extends                  "base.html"                  %}                                      4                  {%                  load                  dinotags                  %}                                      five                                      vi                  {%                  cake                  content                  %}                                      7                                      8                  <                  h1                  >Mosasaurus</                  h1                  >                                      9                  x                  <                  p                  >                  11                  Mosasaurus ('Meuse River lizard') was an aquatic reptile that lived in                  12                  the Late Cretaceous. Estimated lengths reach upward to 17 meters                  xiii                  (56 feet)!                  {{                  "Mosasaurus"                  |                  letter_count                  :"south"                  }}                  14                  15                  </                  p                  >                  sixteen                  17                  {%                  endblock                  content                  %}                              

Fire upward your evolution server and get to http://127.0.0.ane:8000/show_dino/mosa/ to see the following results:

Result of a example template with letter_count filter

The @stringfilter decorator is a quick shortcut that ensures your filter will just have to deal with strings. The needs_autoescape argument and its corresponding autoescape argument give you fine-grained control over what the filter does and doesn't autoescape.

Date Filters

Dates and time zones can be tricky things to deal with. Dealing with them on a website has an added wrinkle: whose fourth dimension zone? The server's? The user'southward? Something else?

Django has built-in tools to help deal with this problem. Role of Django'south solution is two central settings:

  1. USE_TZ
  2. TIME_ZONE

When USE_TZ is True, Django does all date work co-ordinate to the time zone yous set in TIME_ZONE. The default setting for this is UTC.

It'due south like shooting fish in a barrel to forget that template rendering happens on the server-side. Each company is getting their own page rendered, so information technology's natural to remember of the browser as being responsible. Nevertheless, since rendering does happen on the server, the server'due south time zone is the time zone used—unless Django's settings brand information technology otherwise. Neither the server's time zone nor Django'due south settings have to correspond to the user'southward fourth dimension zone.

This makes filters and dates complicated. To aid with this, filter registration supports an argument chosen expects_localtime. Django converts datetime objects into the configured time zone when expects_localtime is True. To see how this works, add together the following lawmaking to dinotags.py:

                                                  57                  # dinosoar/dinofacts/templatetags/dinotags.py                  58                  59                  @annals                  .                  filter                  (                  expects_localtime                  =                  Truthful                  )                  threescore                  def                  bold_time                  (                  when                  ):                  61                  return                  mark_safe                  (                  f                  "<b>                  {                  when                  }                  </b>"                  )                              

This filter returns a bolded version of the passed in datetime object. There are better ways of doing this without a filter, but that wouldn't testify you lot the fourth dimension zone effects. With expects_localtime set to True in the code in a higher place, Django will render the page with a datetime object moved into the time zone specified past the TIME_ZONE setting. To play around with this, create dinosoar/templates/ptero.html:

                                                                      one                  <!-- dinosoar/templates/ptero.html -->                                      2                                      three                  {%                  extends                  "base.html"                  %}                                      4                  {%                  load                  dinotags                  %}                                      v                                      6                  {%                  block                  content                  %}                                      7                                      8                  <                  h1                  >Pterodactyl</                  h1                  >                                      9                  10                  <                  p                  >                  11                  Pterodactyl ('winged finger') is the common name for Pterodactylus,                  12                  the get-go of the genus pterosaur to exist identified as a flying                  13                  reptile. This species is thought to have gone extinct 150 1000000                  14                  years ago, which is a long fourth dimension earlier at present ({{                  now                  |                  bold_time                  }}).                  15                  16                  </                  p                  >                  17                  xviii                  {%                  endblock                  content                  %}                              

Visit the page http://127.0.0.one:8000/show_dino/ptero/ to see the filter in action:

Result of a example template with bold_time filter

To see the departure, edit dinosoar/dinosoar/settings.py and modify the values of USE_TZ or TIME_ZONE and reload the folio. Depending on your choices, the time and possibly even the date will change.

Custom filters give you fine-grained control over your HTML output. They enable you to change the appearance of your information through reusable components. Even so, as filters are data-focused, they're express. To accept consummate control over a block, you lot need custom tags.

Writing Django Template Custom Tags

Filters operate on a unmarried value modifying how they're rendered. Tags are much more than flexible than that, allowing you to inject or modify blocks of content besides as manipulate the data context.

Like filters, you piece of work with tags by:

  • Declaring them in modules within an app'south templatetags/ directory
  • Registering them using a Library instance
  • Implementing them as functions

Additionally, for more than complex tags, you can apply rendering classes instead of functions. This is necessary to implement tags that render blocks.

Using Simple Tags

To make tag writing more straightforward, Django has the @simple_tag decorator. Structurally, this is similar to a filter: yous register a function as a tag, and Django renders its return value. Dissimilar the filter, tags don't get associated with values. They're on their own. You'll start with the simplest of simple tags by editing the dinosoar/dinofacts/templatetags/dinotags.py file:

                                                  57                  # dinosoar/dinofacts/templatetags/dinotags.py                  58                  59                  @register                  .                  simple_tag                  60                  def                  mute                  (                  *                  args                  ):                  61                  render                  ""                              

Django's {% comment %} tag is a cake and requires a lot of typing. The tag above is a more basic version of the same thought: whatever gets passed into the tag as a parameter gets ignored, and Django renders the tag as an empty string. Create dinosoar/templates/rex.html to examination your mute tag:

                                                                      i                  <!-- dinosoar/templates/king.html -->                                      2                                      3                  {%                  extends                  "base.html"                  %}                                      4                  {%                  load                  dinotags                  %}                                      5                                      6                  {%                  cake                  content                  %}                                      7                                      viii                  <                  h1                  >Tyrannosaurus                  {%                  mute                  "The Rex"                  %}                  Rex</                  h1                  >                                      ix                  10                  <                  p                  >                  eleven                  Tyrannosaurus rex ('lizard-tyrant king'), or T-Rex for short, is the                  12                  largest of a genus of theropods.                  thirteen                  14                  It had very                  {%                  mute                  "chomp chomp chomp"                  %}                  big teeth.                  15                  </                  p                  >                  16                  17                  {%                  endblock                  content                  %}                              

Visit the folio http://127.0.0.1:8000/show_dino/rex/ to see the filter in activity:

Result of a example template with nop tag

There's not much to encounter in the results, and that's the point. Everything inside the mute tag has been removed.

Escaping Content

Tags, like filters, have to be concerned with whether the content they're generating is safe for HTML or not. Tags created with @simple_tag are automatically autoescaped but still need to have their content marked safety if information technology contains HTML.

Consider the following tag, which takes a listing and renders an HTML bulleted listing. Open dinotags.py and add the post-obit function:

                                                  71                  # dinosoar/dinofacts/templatetags/dinotags.py                  72                  73                  from                  django.utils.html                  import                  escape                  ,                  mark_safe                  74                  75                  @register                  .                  simple_tag                  76                  def                  make_ul                  (                  iterable                  ):                  77                  content                  =                  [                  "<ul>"                  ]                  78                  for                  detail                  in                  iterable                  :                  79                  content                  .                  append                  (                  f                  "<li>                  {                  escape                  (                  item                  )                  }                  </li>"                  )                  80                  81                  content                  .                  append                  (                  "</ul>"                  )                  82                  content                  =                  ""                  .                  join                  (                  content                  )                  83                  return                  mark_safe                  (                  content                  )                              

This part takes an iterable—such equally a list—and wraps each of its items in an HTML <li> block. Note the utilise of escape() on line 79. You don't desire to trust what's passed into the tag. The content variable is a list that starts with a <ul> tag, has each item appended, and and then ends with the corresponding closing </ul> tag. Everything gets joined together into a string, and the string gets marked condom.

Use make_ul in a template past creating dinosoar/templates/bronto.html:

                                                                      1                  <!-- dinosoar/templates/bronto.html -->                                      2                                      iii                  {%                  extends                  "base of operations.html"                  %}                                      4                  {%                  load                  dinotags                  %}                                      5                                      6                  {%                  block                  content                  %}                                      7                                      viii                  <                  h1                  >Brontosaurus</                  h1                  >                                      ix                  10                  <                  p                  >                  11                  Brontosaurus (thunder lizard) is a long necked quadruped whose being                  12                  was debated for a long fourth dimension, with original finds being found to be                  13                  equanimous of different animals. In 2015 the proper noun was resurrected subsequently                  fourteen                  an extensive study showed that there was a distinction between it and                  fifteen                  its cousin the Apatosaurus.                  xvi                  17                  </                  p                  >                  eighteen                  19                  <                  h2                  >Other Dinosaurs</                  h2                  >                  20                  21                  {%                  make_ul                  dinosaurs                  %}                  22                  23                  {%                  endblock                  content                  %}                              

Visit http://127.0.0.1:8000/show_dino/bronto/ to go the result:

Result of a example template with make_ul tag

Play around with the dinosaurs value from the show_dino() view inside dinosoar/dinofacts/views.py to see how escaping works. For example, add together bold tags to "Tyrannosaurus" to brand information technology "<b>Tyrannosaurus</b>", and you'll see the tags displayed instead of really making anything assuming.

Working With the Context

When your view renders a template, y'all can pass information to the template engine through a dictionary called Context . All values rendered in a page come up from the Context object, and you can get and ready them from within a tag. Create a new tag in dinotags.py:

                                                  87                  # dinosoar/dinofacts/templatetags/dinotags.py                  88                  89                  @register                  .                  simple_tag                  (                  takes_context                  =                  True                  )                  90                  def                  dino_list                  (                  context                  ,                  championship                  ):                  91                  output                  =                  [                  f                  "<h2>                  {                  title                  }                  </h2><ul>"                  ]                  92                  for                  dino                  in                  context                  [                  "dinosaurs"                  ]:                  93                  output                  .                  append                  (                  f                  "<li>                  {                  escape                  (                  dino                  )                  }                  </li>"                  )                  94                  95                  output                  .                  append                  (                  "</ul>"                  )                  96                  output                  =                  ""                  .                  bring together                  (                  output                  )                  97                  98                  context                  [                  "weight"                  ]                  =                  "20 tons"                  99                  render                  mark_safe                  (                  output                  )                              

This lawmaking is similar to make_ul but with a few key changes:

  • Line 89 adds the takes_context statement to the tag registration telephone call. Setting this to True tells Django to add together an argument to the tag's function phone call that contains the Context object.
  • Line 90 declares the function for the tag. Note that the context argument comes first. Tags can accept a variable number of arguments, so context has to come start.
  • Line 92 accesses the context argument as a dictionary, getting the value for dinosaurs, which is the same list of dinosaurs used in many other examples.
  • Line 98 writes the string "20 tons" to the context using the key "weight".

Create a new file called dinosoar/templates/apato.html to examination this tag:

                                                                      1                  <!-- dinosoar/templates/apato.html -->                                      ii                                      3                  {%                  extends                  "base.html"                  %}                                      iv                  {%                  load                  dinotags                  %}                                      5                                      6                  {%                  cake                  content                  %}                                      7                                      8                  <                  h1                  >Apatosaurus</                  h1                  >                                      9                  ten                  <                  p                  >                  11                  Apatosaurus (deceptive cadger) is a long necked quadruped that when                  12                  originally discovered was confused with parts of what is now called                  13                  a Brontosaurus.  Apatosaurus weighed on average                  {{                  weight                  }}.                  14                  xv                  </                  p                  >                  16                  17                  {%                  dino_list                  "Other Big Lizards"                  %}                  xviii                  19                  <                  p                  >                  twenty                  Let's endeavour this again: Apatosaurus weighed on average                  {{                  weight                  }}.                  21                  </                  p                  >                  22                  23                  {%                  endblock                  content                  %}                              

Note the use of the value weight on lines 13 and xx. The first use is before the {% dino_list %} tag, and the second use is afterward. Equally the value for weight is added as a side effect of the tag, the first instance should be undefined and therefore blank.

The scope of context changes is strictly within the rendering engine. Modifying the context dictionary doesn't affect any original value in the view. Endeavour out this template by going to http://127.0.0.1:8000/show_dino/apato/:

Result of a example template with dino_list tag

Equally promised, the sentence Apatosaurus weighed on average ends rather abruptly. The value for weight isn't assigned until the template engine renders the tag. The use of the context inside tags is a powerful tool for communicating between your tags or saving country for a tag that you run multiple times.

Writing Inclusion Tags

The template engine renders whatever your tag function returns. As you accept explored in previous examples, yous ofttimes write HTML snippets within your tags. Composing HTML inside strings can be cumbersome, and so inclusion tags requite yous another way to attain this: your tag itself can employ templates. To run across how this is done, outset past creating the subtemplate dinosoar/templates/sublist.html:

                                                                      1                  <!-- dinosoar/templates/sublist.html -->                                      2                                      3                  <                  ul                  >                                      four                  {%                  for                  particular                  in                  iterator                  %}                                      5                  <                  li                  >                  {{                  particular                  }}                  </                  li                  >                                      6                  {%                  endfor                  %}                                      7                  </                  ul                  >                              

Your tag will employ this template. Now you tin can add the following tag role to dinotags.py:

                                                  103                  # dinosoar/dinofacts/templatetags/dinotags.py                  104                  105                  @register                  .                  inclusion_tag                  (                  "sublist.html"                  )                  106                  def                  include_list                  (                  iterator                  ):                  107                  return                  {                  "iterator"                  :                  iterator                  }                              

The combination of the sublist.html template and this new tag achieves the same affair that make_ul did, but with less code. The @inclusion_tag decorator specifies which template to return with this tag, and the tag function returns a dictionary to apply as the context inside that template.

To see the results, create a test page called dinosoar/templates/brachio.html:

                                                                      1                  <!-- dinosoar/templates/brachio.html -->                                      2                                      3                  {%                  extends                  "base.html"                  %}                                      4                  {%                  load                  dinotags                  %}                                      5                                      half dozen                  {%                  block                  content                  %}                                      seven                                      eight                  <                  h1                  >Brachiosaurus</                  h1                  >                                      9                  10                  <                  p                  >                  eleven                  Brachiosaurus (arm lizard) is yet another long-necked quadruped.                  12                  thirteen                  </                  p                  >                  14                  15                  <                  h2                  >                  Using include_list                  </                  h2                  >                  16                  {%                  include_list                  dinosaurs                  %}                  17                  18                  {%                  endblock                  content                  %}                              

Go to the usual view to run across the page http://127.0.0.1:8000/show_dino/brachio/:

Result of a example template with include_list tag

If yous're writing a tag that uses a lot of HTML, using @inclusion_tag is a ameliorate style to go on the HTML separate from the code.

Creating Advanced Custom Tags

Unproblematic tags are a quick way of writing a tag for the rendering engine to supersede inline. Something you tin can't practise with a simple tag is build blocked areas. Consider how {% comment %} and {% endcomment %} are paired together, removing everything between them. In this section, you'll explore how to build advanced Django custom tags.

Parsing Content

To build a paired cake tag, you lot'll demand to implement a grade that extends django.template.Node. This class is responsible for rendering the tag. Django provides a utility for parsing the content between paired block tags, which it and then passes to your Node class for rendering.

To demonstrate paired block tags, you'll implement a Markdown rendering tag. A library chosen mistune has already done the heavy lifting for this. Use pip to install mistune:

                                                                      1                  $                  python -m pip install                  mistune                  ==                  0.viii.4                              

At the time of writing this tutorial, mistune has been undergoing an overhaul. The 2.0 version is in beta and is drastically different from the examples shown here. Make certain to install version 0.viii.iv, or exist prepared to suit the calls to the library.

Add the following code to dinosoar/dinofacts/templatetags/dinotags.py:

                                                  109                  # dinosoar/dinofacts/templatetags/dinotags.py                  110                  111                  import                  mistune                  112                  113                  @register                  .                  tag                  (                  name                  =                  "markdown"                  )                  114                  def                  do_markdown                  (                  parser                  ,                  token                  ):                  115                  nodelist                  =                  parser                  .                  parse                  ((                  "endmarkdown"                  ,))                  116                  parser                  .                  delete_first_token                  ()                  117                  render                  MarkdownNode                  (                  nodelist                  )                  118                  119                  course                  MarkdownNode                  (                  template                  .                  Node                  ):                  120                  def                  __init__                  (                  cocky                  ,                  nodelist                  ):                  121                  self                  .                  nodelist                  =                  nodelist                  122                  123                  def                  return                  (                  self                  ,                  context                  ):                  124                  content                  =                  self                  .                  nodelist                  .                  return                  (                  context                  )                  125                  result                  =                  mistune                  .                  markdown                  (                  str                  (                  content                  ))                  126                  render                  result                              

To build a cake tag, you need both a function and a class. Hither'southward how they work:

  • Line 113 registers the do_markdown() function as a tag. Note that it uses the proper noun argument to proper noun the tag and that it'due south using the .tag() decorator, non .simple_tag().
  • Line 114 declares the tag. The arguments are different from a simple tag, taking a parser and a token. The parser is a reference to the template engine'southward parser that's parsing the template. You don't use the token argument in this case, and you'll explore information technology later.
  • Line 115 uses the parser object to continue parsing the template until it sees the finish tag, in this case {% endmarkdown %}.
  • Line 116 calls .delete_first_token() to remove the opening tag. What you pass to the Node class is simply what's between the opening and closing tags.
  • Line 117 instantiates the Node course that renders the template, and the template engine passes in the tokens from the parsed tag block.
  • Lines 119 to 121 declare and initialize the Node course.
  • Lines 123 to 126 render the content. For this tag, that involves using mistune to catechumen the tag block from Markdown into HTML.
  • Line 124 calls .render() on the contents of the cake. This ensures that any embedded template content gets handled and allows you to use values and filters inside your embedded Markdown.
  • Line 125 converts the rendered content into a string and then uses mistune to turn that into HTML.
  • Line 126 returns the result to be inserted into the rendered page. Annotation that the result is not autoescaped. Django expects that y'all know how to protect your users from HTML exploits if y'all're writing an advanced tag.

Now you'll test this new tag with some Markdown content. Create dinosoar/templates/steg.html:

                                                                      ane                  <!-- dinosoar/templates/steg.html -->                                      2                                      iii                  {%                  extends                  "base of operations.html"                  %}                                      4                  {%                  load                  dinotags                  %}                                      5                                      6                  {%                  block                  content                  %}                                      7                                      8                  <                  h1                  >Stegosaurus</                  h1                  >                                      9                  ten                  <                  p                  >                  11                  {%                  markdown                  %}                  12**Stegosaurus** ('roof-lizard') is a four-legged found eater from the                  thirteen*Late Jurassic*. It had:                  14                  15* Bony back plates                  16* Big hindquarters                  17* A tail tipped with spikes                  eighteen                  {%                  endmarkdown                  %}                  19                  </                  p                  >                  xx                  21                  {%                  endblock                  content                  %}                              

Load http://127.0.0.1:8000/show_dino/steg/ into your browser to encounter the finished product:

Result of a example template with markdown tag

The Markdown from within the block tag pair gets rendered as HTML by the template engine. 1 matter to keep in mind when playing with Markdown is that indentation is meaningful. If the tag and its contents had not been left-justified, the mistune library would not have been able to catechumen it properly. This is like to using a <pre> tag in HTML. Of a sudden, spacing matters.

Rendering Content

It's time to dive deeper into block tags. To meet how the parser deals with the contents of a tag, add the post-obit to dinotags.py:

                                                  130                  # dinosoar/dinofacts/templatetags/dinotags.py                  131                  132                  @register                  .                  tag                  ()                  133                  def                  shownodes                  (                  parser                  ,                  token                  ):                  134                  nodelist                  =                  parser                  .                  parse                  ((                  "endshownodes"                  ,))                  135                  parser                  .                  delete_first_token                  ()                  136                  return                  ShowNodesNode                  (                  token                  ,                  nodelist                  )                  137                  138                  grade                  ShowNodesNode                  (                  template                  .                  Node                  ):                  139                  def                  __init__                  (                  cocky                  ,                  token                  ,                  nodelist                  ):                  140                  cocky                  .                  token                  =                  token                  141                  cocky                  .                  nodelist                  =                  nodelist                  142                  143                  def                  render                  (                  self                  ,                  context                  ):                  144                  result                  =                  [                  145                  "<ul><li>Token info:</li><ul>"                  ,                  146                  ]                  147                  148                  for                  part                  in                  self                  .                  token                  .                  split_contents                  ():                  149                  content                  =                  escape                  (                  str                  (                  function                  ))                  150                  result                  .                  append                  (                  f                  "<li>                  {                  content                  }                  </li>"                  )                  151                  152                  result                  .                  suspend                  (                  "</ul><li>Cake contents:</li><ul>"                  )                  153                  for                  node                  in                  self                  .                  nodelist                  :                  154                  content                  =                  escape                  (                  str                  (                  node                  ))                  155                  effect                  .                  append                  (                  f                  "<li>                  {                  content                  }                  </li>"                  )                  156                  157                  event                  .                  append                  (                  "</ul>"                  )                  158                  return                  ""                  .                  join                  (                  consequence                  )                              

The contents of shownodes() are quite similar to do_markdown(). The only departure is that this time, the Node course is going to take both token and the parsed content as parameters. The .render() method of ShowNodesNode does the following:

  • Lines 144 to 146 create a list that volition contain the results. The listing starts out with an HTML bulleted listing tag and a title.
  • Lines 148 to 150 iterate through the contents of the token by calling token.split_contents(). This token contains the information from the opening tag, including its arguments. The parts of the token become added to the consequence as a bulleted sublist.
  • Lines 153 to 155 do something similar, but instead of operating on the token, they operate on the contents of the cake tag. Each particular in this bulleted sublist will exist one of the tokens parsed out of the tag block.

To see how tag parsing works, create dinosoar/templates/tri.html and use {% shownodes %} every bit follows:

                                                                      1                  <!-- dinosoar/templates/tri.html -->                                      two                                      3                  {%                  extends                  "base.html"                  %}                                      4                  {%                  load                  dinotags                  %}                                      v                                      6                  {%                  block                  content                  %}                                      7                                      viii                  <                  h1                  >Triceratops</                  h1                  >                                      9                  ten                  <                  p                  >                  11                  Triceratops (3-horned face) is a plant eating quadruped from the                  12                  Late Cretaceous period.                  xiii                  14                  </                  p                  >                  15                  xvi                  {%                  shownodes                  "pointy face up"                  "stubby tail"                  %}                  17It has a big bony frill around its neck. A fully grown adult weighed                  18                  {{                  weight                  }}.                  {%                  comment                  %}                                      put more info here                                    {%                  endcomment                  %}                  19                  {%                  endshownodes                  %}                  20                  21                  {%                  endblock                  content                  %}                              

Your {% shownodes %} tag contains some text, a Context value, and a {% comment %} tag. See what your debugging tag does past visiting http://127.0.0.i:8000/show_dino/tri/:

Result of a example template with shownodes tag

This page shows you a bit more of what's going on when you write a block-level Django custom tag. The opening tag had ii arguments, "pointy face" and "stubby tail", which can be accessed through token.split_contents(). The parser divides the block content into five pieces:

  1. A TextNode object with the opening part of the sentence
  2. A VariableNode object representing the value weight that would normally be rendered
  3. Another TextNode object, ending the commencement sentence
  4. A child, CommentNode
  5. A TextNode object containing the final blank line

Usually in a block tag, you'd telephone call .render() on whatever child contents, which would resolve weight and remove the annotate in this case, simply y'all don't have to. Annihilation within your tag'due south block is nether your command.

Conclusion

Django is structured to separate business logic from presentation code through the utilize of views and templates. The template language is intentionally restrictive to aid enforce this separation.

The original developers of Django were attempting to separate the work of HTML designers from Python programmers. This doesn't hateful y'all're stuck with the built-in mechanisms, though. Django custom template tags and filters give you a valuable weapon in your tool breast.

In this tutorial, you learned:

  • Where custom template tags and filters live
  • How to write a custom filter
  • When autoescaping will modify your results and how to deal with it
  • How to write elementary tags with the @simple_tag and @inclusion_tag decorators
  • About avant-garde custom tags and parsing the contents of a cake tag

For more data about Django, get to the Django project home folio. For more information about tags and filters, check out the Django documentation, particularly the sections on born template tags and filters and custom template tags and filters.

There'south also plenty more to larn about Django on the Django for Web Development Learning Path. Dig in, chow down, have a large bite, and go a Djangosaurus Male monarch.