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 :)

Comments (8)

  1. 4:40 pm, August 10, 2008SmileyChris  / Reply

    So kinda like http://code.djangoproject.com/ticket/5025 ? :)

    Interesting idea about cutting it back to the last word, but if you’re that worried about truncating characters, I’d suggest it just inserts the ellipsis after truncating to length-3.

  2. 3:25 pm, February 2, 2009Chris  / Reply

    Minor error at a boundary condition. You can fix it by changing line 8 to:

    if not len(value) == max_length+1 and value[max_length+1] != ” “:

  3. 3:36 pm, May 1, 2009Graham King  / Reply

    Exactly what I needed. Thank you.

  4. 1:25 pm, May 19, 2009Daniel Karlsson  / Reply

    my django expert informed me that strings are lists and therefore we can use {{ some_list|slice:”:20″ }} or more specific {{ some_string | slice:”:20″}}

  5. 5:53 pm, October 6, 2009Chris Robinson  / Reply

    Worked beautifully thanks! No idea why truncating by characters isn’t part of the default set of django filters

  6. 2:08 pm, February 9, 2010Neum  / Reply

    Excellent! That was exactly what I was looking for. I agree, this should be in Django proper. Until then, you saved me 10 min. :) Thanks!

  7. 10:53 am, February 25, 2010Paul  / Reply

    Thanks that’s useful but I found two bugs with this:
    - it throws an error if there’s no space in the string before max_length
    - the get the character right after truncation, you should use value[max_length], not value[max_length + 1] which also solves the issue chris reported.
    Heres the fixed version:

    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 + “…”

  8. 10:56 am, February 25, 2010Paul  / Reply

    the indentation was lost in the paste. here it is:

    http://gist.github.com/314819

Leave a Reply

Allowed Tags - You may use these HTML tags and attributes in your comment.

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Pingbacks (0)

› No pingbacks yet.