14

Sep

Filed in Code, Django, Jinja |

As an advocate of Jinja2 I figured it was about time I got on board the train. Up until now I had been using Jinja1 at work, and in most projects. The main reasoning behind this was there was no good connector. Now many might say that you don’t need a connector, you can just throw in a couple of functions and it works. While that might be true, there’s a lot more that goes into rendering templates than simply parsing standard HTML.

So as I began the conversion, I found a project called Coffin. The project was hosted over at LaunchPad, which personally, I wish would disappear from the internet. Among the problem of getting bzr working on my Mac, and servers, so that I could check out the project, I also found that the project wasn’t very stable.

Quickly I noticed it didn’t have support for globals, or tests. We don’t really use tests, but we have many global functions. Things such as the URL template tag in Django, we had written as a Jinja Global ({{ url() }} vs {% url %}). So I quickly began moving a copy of the repository over to GitHub, so I could get these additions in. While doing this I had also found some other areas which were needing improvements (mostly caching optimizations).

By the end of the day, I had gotten a discussion going w/ the original author (Chris Leary) of Coffin to find out that it was kind of a dead project. After a few back and forth I decided to take over the project. So we’ve now got it up at GitHub, and it includes several optimizations, support for globals and tests, and a critical threading fix. What we have now is a fairly stable (as of tonight) binding between Jinja2 and Django.

Now let’s talk a bit about how you can use this with your Django project. It’s pretty compicated so try to keep up!

First up, installation:

# As of the time of writing the current version is 0.3
easy_install Coffin==0.3

Load up your settings.py and add it to your installed apps (this step is not required):

INSTALLED_APPS = (
    'coffin',
    ...
)

Go into your urls.py and switch out your import line:

from coffin.conf.urls.defaults import *

Now the next part we do in our own project, simply because we dont use Context instances:

from coffin import shortcuts
from django.template import RequestContext
 
def render_to_string(template, context, request=None):
    if request:
        context_instance = RequestContext(request)
    else:
        context_instance = None
    return shortcuts.render_to_string(template, context, context_instance)
 
def render_to_response(template, context={}, request=None, mimetype="text/html"):
    response = render_to_string(template, context, request)
    return HttpResponse(response, mimetype=mimetype)

Now you’re ready to start rendering those amazingly fast Jinja2 templates!

from myshortcuts import render_to_response
 
def myview(request):
    return render_to_response('template/path.html', {'title': 'Hello World'}, request)

Pretty painful, we know :)

So let’s move on to a bit more of the advanced Jinja2 bits, registering your own extensions. In Coffin this is fairly easy:

from coffin import template
from jinja2 import Markup
register = template.Library()
 
@register.filter(jinja2_only=True)
def mark_safe(value):
    """ Marks the value as HTML-safe, and disables auto-escaping it. """
    return Markup(value)
 
@register.object()
def hello():
    """ Outputs 'world' """
    return "world"
 
@register.test(is_a_string, 'string')
def is_a_string(value):
    return isinstance(value, basestring)
 
class UselessExtension(Extension):
    """ Outputs the given value ."""
 
    tags = ['useless']
 
    def parse(self, parser):
        lineno = parser.stream.next().lineno
        body = parser.parse_statements(['name:endspaceless'], drop_needle=True)
        return nodes.CallBlock(
            self.call_method('_do_nothing', [], [], None, None),
            [], [], body
        ).set_lineno(lineno)
 
    def _do_nothing(self, caller=None):
        return caller()
register.tag(UselessExtension)

Please check out the README for more information. Enjoy!

  • Alberto
    I having problems with i18n. I'm in the same situation of dmlance. I get this error when I try to render a template with "trans" tags:

    'gettext' is undefined

    This is the code of my template:

    {{ _("Hello") }}

    I have added the i18n extension in settings.py but it doesn't work. Any solution?
  • koobz
    Looking at the filter/extension examples above, how does done access the django context variable that's available to you when you write custom django template tags?

    The reason I ask is because I'm trying to migrate this snippet here: http://www.djangosnippets.org/snippets/361/ to a jinja template.
  • dmlance
    Coffin have problems with i18n (trans tags and related stuff), looks like they broke jinja2 gettext init.
    Tested with latest coffin, django, jinja2 and py2.6
    djajinja works without any problems.
  • Lars
    I am using Jinja2 + Django on every project and I wanted to use it for external apps too.

    Using this patch http://code.djangoproject.com/ticket/6691 and a custom template loader + a small wrapper around Jinja templates that looks in a directory for Jinja2 templates and falls back to Django templates if none is available. There are so many projects/methods out there doing exactly the same thing (at least four and I didn't even know about djanjinja).

    It would be very nice to consolidate this work into one project and perhaps submit the necessary patches to Django to make the integration even easier.

    On a related note: Jinja 2.2 has been released a few days ago.
  • I wrote http://bitbucket.org/zacharyvoase/djanjinja to scratch exactly the same itch. It looks like the two projects are relatively similar in their goals.
  • David
    FUD would imply something illegitimate. I had loads of issues getting it compiled through Fink.
  • Marco
    what does it means "the problem of getting bzr working on my Mac" ???
    It's not more complicated than get git or hg or svn ... use macports and you'll get bzr working like a charm. Moreover there is a .pkg installer (that I personally don't like but it works too without a problem)
    Just to stop the FUD
  • Hi,
    I use this connector to make Jinja2 work with Django. Super easy and working everywhere even on AppEngine:
    http://bitbucket.org/trevor/chouwa/

    Redliner
blog comments powered by Disqus