title: Proper indentation in Django templates (without monkey-patching)?
body: <p>I want to generate human-readable HTML and CSS code (properly indented) preprocessed by the Django template system for my standalone application.</p>
  
  <p>I've modified the render method from the NodeList class found in the django.template.base module. My code seems to work properly, but I'm using monkey-patching to replace the old render method.</p>
  
  <p>Is there a more elegant way that does not use monkey-patching in this case? Or maybe monkey-patching is the best way here?</p>
  
  <p>My code looks like this:</p>
  
  <pre><code>'''
  This module monkey-patches Django template system to preserve
  indentation when rendering templates.
  '''
  
  import re
  
  from django.utils.encoding import force_text
  from django.utils.safestring import mark_safe
  from django.template.loader import render_to_string
  from django.template import Node, NodeList, TextNode
  from django.template.loader_tags import (BlockNode, ConstantIncludeNode,
                                           IncludeNode)
  
  
  NEWLINES = re.compile(r'(\r\n|\r|\n)')
  INDENT = re.compile(r'(?:\r\n|\r|\n)([\ \t]+)')
  
  
  def get_indent(text, i=0):
      '''
      Depending on value of `i`, returns first or last indent
      (or any other if `i` is something other than 0 or -1)
      found in `text`. Indent is any sequence of tabs or spaces
      preceded by a newline.
      '''
      try:
          return INDENT.findall(text)[i]
      except IndexError:
          pass
  
  
  def reindent(self, context):
      bits = ''
      for node in self:
          if isinstance(node, Node):
              bit = self.render_node(node, context)
          else:
              bit = node
          text = force_text(bit)
  
          # Remove one indentation level
          if isinstance(node, BlockNode):
              if INDENT.match(text):
                  indent = get_indent(text)
                  text = re.sub(r'(\r\n|\r|\n)' + indent, r'\1', text)
  
          # Add one indentation level
          if isinstance(node, (BlockNode, ConstantIncludeNode, IncludeNode)):
              text = text.strip()
              if '\r' in text or '\n' in text:
                  indent = get_indent(bits, -1)
                  if indent:
                      text = NEWLINES.sub(r'\1' + indent, text)
  
          bits += text
  
      return mark_safe(bits)
  
  
  # Monkey-patching Django class
  NodeList.render = reindent
  </code></pre>