« Archives on August 8, 2008

A Simple Django Truncate Filter

The Problem:

The built-in Django filter, truncate_words, truncates a string after a certain number of words.  This is great, but many times I find I have very tight space restrictions in certain areas of a page, and a string that is too long would push its way into another element and subsequently into my head in the form of a headache.

The built-in truncate_words filter is no help here — it does nothing to limit the width of a string.

I.e., “One two” and “ooooooooooooooooooooooooooonnnnnnnnnnnnnnneeeeeeeeeeeeee twoooooooooooooo” are both only 2 words, yet they have extremely different widths :)

The Solution:

We need a filter that truncates not only by words, but by characters too.  It’s an extremely simple filter, and often times I wonder why it’s not included in Django.

Let’s start from the very beginning.  Every filter must live in your app’s templatetags directory.  So create a file in that directory named “truncate_filters.py” or something.  If you need any more information than that, take a look at the Django documentation on how to create a custom filter.

Here is what the filter looks like:

from django import template
register = template.Library()

@register.filter("truncate_chars")
def truncate_chars(value, max_length):
    if len(value) <= max_length:
        return value

    truncd_val = value[:max_length]
    if value[max_length] != " ":
        rightmost_space = truncd_val.rfind(" ")
        if rightmost_space != -1:
            truncd_val = truncd_val[:rightmost_space]

    return truncd_val + "..."


*update* code was changed per chris and paul’s suggestions below, I haven’t tested but assume they work :)

Here’s how it works visually on this string: “This is a sample string”
This is what happens when the filter is supplied with the argument 20:

  1. Cut down the string to 20 chars if it is greater than 20 chars in length.  The string now becomes: “This is a sample str”
  2. Find the right-most space, indicating the start of the last word in the string, and truncate again:  The string is now: “This is a sample”
  3. Add “…” and return.  “This is a sample…”

You can invoke the filter from within your template like so:

{% load truncate_filters %}
<ol>
{% for some_string in a_list_of_strings %}
    <li>{{some_string|truncate_chars:50}}</li>
{% endfor %}
</ol>

Hope someone out there finds this helpful :)