I’ve been thinking a lot about template engines in Python recently. Partly because sourceforge.net’s new python code needed to choose a template language, and there were some questions about why we would choose one over the others.
I figure all this promiscuous template library usage means that I should put my thoughts down somewhere. There are advantages and disadvantages of all these libraries, but I think that the choices are pretty clear once you know your constraints.
I’m not going to commit to covering them all in depth, but I’m going to try to put my thoughts about them down over the next few days.
For today let’s talk about the pros and cons of Django Templates. This is another post that has been developed over the last year or so, where typed stuff up while working on fossfor.us.
Django made making fossfor.us easy in lots of ways. Want threaded comments? Add the existing app in a couple hours — Done! Want OpenID? Again add an app — Done!
But it also had frustrations, and one of the biggest for me was the template language.
First the good
Django templates operate in a sandboxed environment. This is good because it limits the ways that designers can blow-up your web-app. In fact this seems to be an underlying design decision of the Django framework. It’s the reason accessing non-existent variables just returns empty strings, because as the Djanogo book tells me “you wouldn’t want a designer to be able to take down your website with a typo.”
I can respect that, and I definitely see the benefits of sandboxing template code, particularly in places where end-users might be allowed to edit templates.
Easy to learn, and easy to use
They also provide a very simple syntax, that’s very well documented. This makes it easy for designers to figure out.
All of this is good.
But there are some negatives
Django developers seem to love them, but I feel like there are some significant drawbacks to using django templates.
Not trivial to use outside of django.
You can do it, but it’s not easy, and you end up with a lot of Django loaded that you don’t need. Fortunately there’s Jinja which is somewhat Django template like, but which isn’t at all dependent on Django itself. So, if you’re not using Django, I think it’s sane to recommend that you use Jinja templates. And tomorrow I’ll blog more about why I think that Jinja is just a better version of Django templates.
Won’t let you follow the fail early philosophy of development
When you use Django Templates in Django, I have two major issues. THe first is the decision to always treat undefined variables as variables that contain an empty string. Generally “fail fast” is a good engineering principle, because it’s helps you find problems sooner, and makes debugging much easier.
Of course the Django guys are smart, and the knew about this so they planned for it.
Well sort-of, apparently it’s easy enough to make invalid variables return something other than an empty string by setting the
TEMPLATE_STRING_IF_INVALID config variable to something else, which gets displayed if you access an undefined variable in a template (say you make a typo. And this does makes it easier to find template errorsmore quickly.
But it’s not really “failing fast.”
I want an all out failure, not a small difference in the rendered page.
With that said, this as faar as it goes, I think it is good.
TEMPLATE_STRING_IF_INVALID should be a configuraiton option, (though perhaps with a better name) and people should turn it on in development and off in production. Pylons does this with it’s
c variable, and I think it’s very helpful.
But what I want is the exception, not just some random other text inserted, so then I looked into what it would take to force django template rendering to stop silently suppressing exceptions, and it’s not that hard on first glance.
The template renderer depends on a special attribute
silent_variable_failure being set to
True on the exception, so all we would have to do is just disable this checking when the
fail_fast config value was set to
True and we’d be good to go.
Seemed like a good idea, but in reality — not so good.
Django’s admin will totally break if you do such a thing, and it turns out that even setting the TEMPLATE_STRING_IF_INVALID parameter makes these pages render quite badly.
I ultimately found this tidbit in the Django Template Docs:
While TEMPLATE_STRING_IF_INVALID can be a useful debugging tool, it is a bad idea to turn it on as a ‘development default’.
Many templates, including those in the Admin site, rely upon the silence of the template system when a non-existent variable is encountered. If you assign a value other than ” to TEMPLATE_STRING_IF_INVALID, you will experience rendering problems with these templates and sites.
Generally, TEMPLATE_STRING_IF_INVALID should only be enabled in order to debug a specific template problem, then cleared once debugging is complete
Oops, that’s exactly what I don’t want. In practice this was not as bad as I thought it would be, but it was not good, and it made my life harder because the time between making a typo in a template and finding the problem increased, which also increased the dificulty of finding the cause of the problem.
My other frustration with Django Templates
Beyond that, the fact that Django template sandboxing excludes all expressions pushes lots and lots of work into filters and up into the “views” which other frameworks call “controllers” and means that django views mix presentation and action logic even more than is common in other MVC like web frameworks (Rails, Pylons, TurboGears, etc.).
Having a full featured experssion language like python in templates has been very useful to me in capturing presentation logic in the template.
And I really want to be able to have code-reuse in templates without jumping through hoops. That’s part of what a template language is for, so I really don’t like giving it up.
With all of that said, if the benifits of Django (reusable apps mainly) are important to your project, you should use Django, and if you are using Django you absolutely should use Dango Templates. They aren’t bad really. It’s just that I’m used to the other options that do it better.
So, ultimately if you’re not already using Django, I think you should consider one of the other options, as I think they all do better on template code reuse, and allowing fail fast behavior.