In my previous post about creating a blog with Pyramid, I mentioned that I really didn't like Chameleon or Mako (To be fair I never really tried Mako; I just read through the documentation trying to understand how to perform inheritance) templates. A commenter(thanks Matt) suggested I give Jinja2 a try. Some of my gripes (or my lack of understanding) about the those template engines were:

  • I couldn't find an easy way to break my templates into various pieces like header, footer, sidebar etc. Then have a layout template that pulls them in, and lastly inserts my individual view on top of that portion of the template.
  • The syntax should be easy and I believe template portions of your markup should stand out from the static markup.
  • I don't have any concern over speed or jython compatibility and this seems to be a major focus for chameleon.

Chameleon uses a macro language called METAL and it adds a bunch of extra markup to your document and making it less clear over which parts were part of the template and which were static. This became apparent when I was retrofitting my templates to Jinja2 and ripping out the chameleon parts, I kept finding remains of the chameleon templates after I had made the switch. With Chameleon, I was able to create a structure layout and then use macros to create the portions for each view, for instance my homepage template looked like this:


<div metal:fill-slot="main_content">
  <div tal:repeat="entry entries" class="hero-unit">
            <h3><a href="/post/${structure: entry.url}">${structure: entry.title}</a></h3>
            <h4>${structure: entry.date}</h4>
            <p tal:content="structure: entry.blog_body"></p>

            <p><a class="btn btn-primary" href="/post/${structure: entry.url}">Read Post »</a></p>
            <p></p><h4><a href="/post/${structure: entry.url}#disqus_thread"></a></h4><p></p>
            <i class="icon-tags icon-large"></i> <span tal:repeat="tag entry.tags"><a href="/tag/${structure: tag}" class="label label-success">${structure: tag</a>
</span>
          </div>
  </div>
</metal:main>

This allowed me to create a main skeleton that included my header and footer, but what if I wanted another page that used a left nav layout instead of a right one? Then I would have to create another structure template and copy/paste the header, footer, etc into the new template, thus breaking DRY principles. Also notice the metal: and tal: attributes in my markup. It is easy to see that the template parts that are wrapped in curly braces but the template system bleeds out to my markup. Jinja made this whole process much easier and flexible. I was able to install Jinja2 into my project by following these steps.

I was then able to pull out my parts and place them into their own .jinja2 files like my header.jinja2 template here:

        <div class="navbar navbar-fixed-top">
              <div class="navbar-inner">
                <div class="container">
                  <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                  </a>
                  <div class="span6 offset1">
                    <div class="logo"><img src="/static/img/bio48.png" /></div>
                    <a class="brand" href="/">{{static_info.blog_title}}</a>
                    <p class='tag-line'>{{static_info.site_motto}}</p>
                  </div>
                  <div class="nav-collapse">
                    <ul class="nav">
                      <li  class="{% if cur_page == 'home' %} active {% endif %}"><a href="/"><i class="icon-home icon-large"></i>Home</a></li>
                      <li class="{% if cur_page == 'about' %} active {% endif %}"><a href="/page/about"><i class="icon-user icon-large"></i>About</a></li>
                      </ul>
                  </div><!--/.nav-collapse -->
                </div>
              </div>
            </div>
    
Then I was able to create a layout_right.jinja2 template that pulled in all the pieces using includes like so: {% include 'header.jinja2' %}. This was exactly what I was looking for, now on my individual views, like my homepage I could tell it which layout to use and all the pieces come with it. My new home template looks like this:
        {% extends "layout_right.jinja2" %}
        {% block title %}Welcome To Brett's Blog{% endblock %}
        {% block head %}
            {{ super() }}
        {% endblock %}
        {% block content %}
            <div>
              {% for entry in entries %}
              <div class="hero-unit">
                <h3><a href="/post/{{entry.url}}">{{entry.title}}</a></h3>
                <h4>{{entry.date}}</h4>
                <p>{{entry.blog_body | safe}}</p>

                <p><a class="btn btn-primary" href="/post/{{entry.url}}">Read Post »</a></p>
                <h4><a href="/post/{{entry.url}}#disqus_thread"></a></h4>
                <p><i class="icon-tags icon-large"></i> {% for tag in entry.tags %} <a href="/tag/{{tag}}" class="label label-success">{{tag}}</a>{% endfor %}</p>
              </div>
              {% endfor %}
          </div>
        {% endblock %}
    
As you can see here, all the parts of the template are wrapped including the for statements, which makes it much easier to identify the static markup from the dynamic template pieces. Jinja2 was exactly the template system I was looking for. I updated my github source to use Jinja2, you can find it here.
comments powered by Disqus