Django spotlight: humanize template filters

I frequently consult Django's set of default template tags and filters and there's one part I often forget about, because it's not enabled by default: django.contrib.humanize.

The quick and short of it

The humanize app adds a set of template filters for "adding a human touch to data". Meaning: turning numbers and temporal data into clear, comprehensible text, while taking the active locale into account.

Like ordinal and intcomma:

>>> from django.contrib.humanize.templatetags import humanize
>>> # ordinal
>>> humanize.ordinal(1)
'1st'
>>> humanize.ordinal(5)
'5th'
>>>
>>> # intcomma
>>> humanize.intcomma(14000500)
'14.000.500'

Or naturalday

>>> # naturalday
>>> from django.utils import timezone
>>> from datetime import timedelta
>>> now = timezone.now()
>>> humanize.naturalday(now)
'today'
>>> humanize.naturalday(now - timedelta(days=1))
'yesterday'

Let's switch to French:

>>> from django.utils import translation
>>> with translation.override("fr"):
...     humanize.naturalday(now)
...
"aujourd'hui"

Don't forget to:

  1. Load the library in your templates using {% load humanize %}.
  2. Add django.contrib.humanize to your INSTALLED_APPS.

Pop quiz, hotshot

It might be a little bit puzzling these six template filters have to be explicitly enabled, while the default filters include such things as phone2numeric, get_digits, linenumbers and unordered_list.

Do you know what these do? Have you ever used them?

phone2numeric converts a phone number which might contain letters into all digits. The example in the docs: 800-COLLECT is converted into 800-2655328. This seems quite outdated and pretty US-centric.

get_digit expects a whole number and grabs a digit, starting from the back. Meaning, with value set to 123456789, {{ value|get_digit:"4" }} will output 6. Out of sheer curiosity I searched GitHub for usages of this filter. That resulted in a lot more matches than I expected, but from a quick glance they seemed to be contrived examples, used incorrectly or better served with a custom template filter.

unordered_list will output each item of a list as <li>{{ item }}</li>, adding another unordered list if the current item is a list. I'm not claiming this can't be useful. I do question whether this is a more common use case than outputting an ordinal.

linenumbers will split the value into lines and output each line, prefixed with the line number. That's it. There's no markup involved. No <ol>. I wonder why this didn't just result in a far more useful split filter. For example:

{% for line in value|split %} {{ forloop.count }}. {{ line }} {% endfor %}

Don't forget

Django doesn't just pack auth, admin and messages as part of the django.contrib module. There's plenty of other useful modules you can rely on.