Django Spaceless With Preserved Pre Formatting

I’m using Django’s spaceless template-tag a lot, but after adding some code inside a pre tag I recognised that everything left is a squeezed string. I never came up with that problem before. The builtin spaceless tag is doing just fine. So after a little bit of searching I quickly found some resources about that topic:


  • not possible with builtin methods
  • not coming with future versions

It was my fault to expect something different from a tag which does exactly what it should.

But this doesn’t matter - let’s build a new template tag that does the trick! :)

"""Copyright (c) 2013-2014 Stephan Groß, under MIT license."""
from __future__ import unicode_literals

import re

from django import template
from django.template import Node
from django.utils import six
from django.utils.encoding import force_text
from django.utils.functional import allow_lazy

register = template.Library()

def strip_spaces_between_tags_except_pre(value):
    def replacement(count, matches, match):
        matches.append([1:-1])  # save the whole match without leading "<" and trailing ">"
        count[0] += 1
        return '<}>'.format(count[0])  # add "<" and ">" to preserve space stripping
    count = [-1]
    matches = []
    value = re.sub(r'<pre(\s.*)?>(.*?)</pre>', lambda match: replacement(count, matches, match), force_text(value), flags=re.S | re.M | re.I)
    value = re.sub(r'>\s+<', '><', force_text(value))
    return value.format(*matches)
strip_spaces_between_tags_except_pre = allow_lazy(strip_spaces_between_tags_except_pre, six.text_type)

class SpacelessExceptPreNode(Node):
    def __init__(self, nodelist):
        self.nodelist = nodelist

    def render(self, context):
        return strip_spaces_between_tags_except_pre(self.nodelist.render(context).strip())

def spaceless_except_pre(parser, token):
    """Remove whitespace between HTML tags, including tab and newline characters except content between <pre>"""
    nodelist = parser.parse(('endspaceless_except_pre',))
    return SpacelessExceptPreNode(nodelist)

also available as Gist.

Just put this snippet in a new file (like inside your templatetags folder.

Now you can load and apply this tag inside your template like:

{% load spaceless_except_pre %}{% spaceless_except_pre %}
    <div class="codehilite">
            <code><span class="k">def</span> <span class="nf">hello</span><span class="p">():</span>
    <span class="k">print</span> <span class="s">&quot;world&quot;</span>
{% endspaceless_except_pre %}

which will result in:

<html><body><div class="codehilite"><pre><code><span class="k">def</span> <span class="nf">hello</span><span class="p">():</span>
    <span class="k">print</span> <span class="s">&quot;world&quot;</span>

and frontend:

def hello():
    print "world"

You could also take a look at the source of this post for a bigger example.

For those of you who like digging deeper, I simply matched all <pre>..</pre> blocks and call a replacement method. Inside that method I:

  • append the original content to a list.
  • increment a counter
  • replace the matched pre block content with a placeholder

The trick is that the replaced content with the individual id fits the python string format method syntax. So after stripping out all whitespaces between the tags I call format and pass my filled matched list.

And that’s it. Thank you for reading.

Updates (Jan. 1, 2014):

  • Improved script to save complete original expression and ignore cases.
  • Fix example to use highlighted code (which causes the troubles)

P.S.: Plain text is doing just fine with the default spaceless tag cause it isn’t affected by the strip regex.

Posted in Programming with : Django, Django Template Tags, Python