Putting breakpoints to HTML templates in Python

Python offers many different template engines for web application development to turn your view logic to HTML code on the server and then send the resulting HTML code to a web browser. When you are dealing with complex page templates, especially when using third party libraries, it often gets confusing what template context variables are available and what they have eaten.  In this case, the best method of debugging is to insert a breakpoint into your page template and inspect the variables in runtime context. This very useful “advanced” debugging method is not known to many people, especially if they come from the background where command-line debugging tools have not been the norm.

1. Python debugging

Python offers pdb command-line debugger out of the box. I recommend to use more advanced version ipdb, which comes with proper command history, colored tracebacks and tab completion / arrow key support (though there seems to be an issue using ipdb with multithreaded / autoreloading programs). There also exist more GUIful, but still terminal based, pudb. Eclipse + PyDev offer remote debugging support with full GUI.

Read some command-line debugging tutorials if you are not familiar with the concept. Especially how to inspect local variables with commands of locals(), dir(object), for i in dict.items(): print i come to hand.

2. Setting a breakpoint in Django templates

Here is an example how to create a breakpoint tag for Django templates and then go around and debug your templates. In the example, I debug the field rendering look of django-crispy-forms.

First we need to create a custom Django template tag invoking pdb, because you cannot run arbitrary Python snippets directly from Django templates. The code here is based on the example on djangosnippets.org.

templatetags/debug.py

# -*- coding: utf-8 -*-
#
# http://djangosnippets.org/snippets/1550/
#

import pdb as pdb_module

from django.template import Library, Node

register = Library()

class PdbNode(Node):

    def render(self, context):
        pdb_module.set_trace()
        return ''

@register.tag
def pdb(parser, token):
    return PdbNode()

Then we use this tag our template:

{% load crispy_forms_field %}
{% load debug %}

{% if field.is_hidden %}
    {{ field }}
{% else %}

    <div>
        <div>
            {{ field.long_help }}
            {% pdb %}
        </div>
    </div>
{% endif %}

We run Django normally with manage.py runserver and encounter this breakpoint when rendering a template.

Screen Shot 2013-05-16 at 9.36.06 AM

Now we can go around and see what variables are avaialble in the template and what they have eaten.

(Pdb) locals().keys()
['self', 'context']
(Pdb) context.__class__
<class 'django.template.context.RequestContext'>

Ok, so we have available template variables in a local variable called context (makes sense, we are now in our templatetag.py code).

(Pdb) for i in dir(context): print i
...
_reset_dicts
autoescape
current_app
dicts
get
has_key
new
pop
push
render_context
update
use_l10n
use_tz

There seems to be a way to access all template variables:

(Pdb) for d in context.dicts: print d.keys()
[]
['csrf_token']
['request']
['perms', 'user']
['debug', 'sql_queries']
['LANGUAGES', 'LANGUAGE_BIDI', 'LANGUAGE_CODE']
['MEDIA_URL']
['STATIC_URL']
['TIME_ZONE']
['messages']
['downtime']
['block']
['flat_attrs', 'inputs', 'csrf_token', 'form_id', 'form_error_title', 
'form_action', 'error_text_inline', 'html5_required', 'help_text_inline', 
'formset_error_title', 'form_style', 'form_method', 'form_show_errors', 
'is_formset', 'form_tag', 'attrs', 'form_attrs', 'form_class']

Now we can see what come through to our template as the field and widget in the field rendering loop.

(Pdb) context["field"]
<django.forms.forms.BoundField object at 0x1038ae590>

(Pdb) for i in context["field"].__dict__.items(): print i
('html_initial_name', u'initial-ad-xxx_type')
('form', <xxx.Form object at 0x103064c10>)
('html_name', 'ad-xxx_type')
('html_initial_id', u'initial-ad-id_ad-xxx_type')
('label', u'Foobar')
('field', <django.forms.fields.TypedChoiceField object at 0x103062e50>)
('help_text', '')
('name', 'xxx_type')

And after you see this, you can figure out if your data is coming through the templating stack and is it coming through correctly and what you need to do in order to bend it to your will.

3. Other templating engines

See also an example for Chameleon templates. Unfortunately Plone, out of the box, cannot support this debugging method due to the security.

 

 

 

\"\" Subscribe to RSS feed Follow me on Twitter Follow me on Facebook Follow me Google+

3 thoughts on “Putting breakpoints to HTML templates in Python

  1. This is wonderful thanks! I’ve recently switched to vim from a full fledged Python IDE and one of the things I was really missing was django template debugging.

Leave a Reply

Your email address will not be published. Required fields are marked *