Jinja2 and Django 4ever

By on March 15th, 2010 in Python Comments (4)

My goal is to stop hating Django. Our team has been bouncing back and forth between using Django and Pylons for the creation of the web front end for ChompStack, a restaurant-oriented iPhone app site with an admin panel for easy data management.

In the beginning, there was Django, and it was good. But gradually we began to find its paradigms counterintuitive: we disliked the idea of apps and considered the built-in template language pretty crippled. More importantly, we prefer the epically powerful SQLAlchemy to Django’s ORM of Doom, so with the evidence before us, we switched to Pylons and the haven of SQLAlchemy/Jinja, only to discover we had switched from a shitty apartment in Brooklyn to a tent in the desert. No cockroaches, true, but no running water, either. Who really wants to write a thumbnail library? (Apologies to anyone who has actually written a thumbnail library)

So now we’re back to Django, but we’re using the 1.2 development version, and we’re taking Jinja2 with us. Here’s how:

  1. Install Jinja2.
  2. sudo easy_install jinja2
    
  3. Integrate Jinja2 with Django
  4. Create a Django project if you haven’t already. The name of my test project is jinja_replace. Now create a file in a location which can be imported. For example, I created a lib directory, added the requisite __init__.py file, and then created a jinja2django.py file. You could put this file anywhere accessible to your project.
    Most of the following code is derived from this excellent Django snippet, which you should certainly check out. My version is just a little simpler.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    from django.http import HttpResponse
    from django.conf import settings
    from jinja2 import Environment, ChoiceLoader, FileSystemLoader
     
    # Setup environment
    default_mimetype = getattr(settings, 'DEFAULT_CONTENT_TYPE')
     
    # Create the Jinja2 Environment
    env = Environment(
        line_statement_prefix='%',
        line_comment_prefix='##',
        loader=ChoiceLoader([FileSystemLoader(path) for path in getattr(settings, 'TEMPLATE_DIRS', ())])
    )
     
    def render_to_string(filename, context={}):
        return env.get_template(filename).render(**context)
     
    def render_to_response(filename, context={},mimetype=default_mimetype, request = None):
        if request: context['request'] = request
        return HttpResponse(render_to_string(filename, context),mimetype=mimetype)

    Two things to note here. One, we’re really only replacing two functions: render_to_string and render_to_response, which calls render_to_string. Two, Jinja requires us to create an environment object, and we do this at the module level. The environment object is responsible for retrieving the template. We’re doing something kind of neat with the environment configuration by setting the line_statement_prefix and line_comment_prefix to something other than the default. This allows us to use Jinja2 in this fashion, replacing the usual {% block %} notation:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    % block container
    % include "header.html"
        <div id="main">
          % for message in messages:
            <div class="message success">
              <h2>Congratulations</h2>
              <p>{{ message }}</p>
            </div>
          % endfor
      </div>

    Now that we’re cool like that, we need to complete just two more steps to get this puzzle together. Go to whatever views.py file you’ve created which will be rendering your Jinja2 templates, and instead of using the standard Django render_to_response shortcut, import your custom render_to_response method. My views.py looks like this:

    1
    2
    3
    4
    
    from jinja_replace.lib.jinja2django import render_to_response
     
    def index(request):
    	return render_to_response("index.html", context={"name":"Steve"})

    Finally, create your template. I put mine in a templates directory in my main project, then went to my settings.py file and added that templates dir as such:

    import os
    ROOT_PATH = os.path.dirname(__file__)
    TEMPLATE_DIRS = (
        os.path.join(ROOT_PATH,'templates'),
    )
    

    And here’s my templates/index.html (super complex, I know…try to keep up):

    1
    
    Welcome to Hindsight Labs, {{ name }}!
  5. Test your monster.

  • http://www.rosslawley.co.uk Ross

    Hi, as you are using Django 1.2, then Jinja2 integration is a doddle with template loaders and handlers.

    I put a gist about it here: http://www.rosslawley.co.uk/2010/07/django-12-and-jinja2-integration.html. Whats really nice as once the loader and template handlers are configured you can use all the native Django methods to render the templates, without writing new render to response / render to string code!

    By namespacing your jinja2 template dir can use normal django apps that dont use jinja2 without any changes to your code!

  • Christine Meranda

    Thanks Ross, that’s super helpful!

  • http://hostomato.com/ Olav

    Wow, thanks! I just discovered Jinja2, and I just started working on a new project. That should be a good enough excuse to use Jinja2, right? ;)

  • Christine Meranda

    Jinja2 is literally my favorite templating language ever (I only know about 3). So I’m going to say yes :)