February 7, 2014

About variable overriding behavior of Jinja2

While working on a site made with flask microframework, one of the things that kept me puzzled for hours was Jinja2’s variable overriding behavior ( variable scoping, as the pedantic will call it ). To better demonstrate my case, I am going to make two jinja templates. The first one is the base or parent template, base.html -

{% set slogan = "This is an awesome site!" %}
{{ slogan }}

The child template, page.html inherits from the base.html and tries to override the slogan variable -

{% extends 'base.html' %}
{% set slogan = "This is the most awesome page!" %}
{{ slogan }}

The output of base.html is as expected -

This is an awesome site!

And the output of page.html is -

This is an awesome site!

Surprise! The slogan did not get replaced in the child template! I wont dare to comment about the intuition behind this behavior, since front-end development is not my speciality. Anyway, proper clarifications came from an old documentation of jinja2. Here’s a relevent excerpt from it:

Template Globals
================

A special threatment exists for template code outside of visible blocks in
child templates. This code will be executed **before** the layout template
code. Thus it can be used to propagate values back to the layout template or
import macros from templates for rendering.

Such code can output data but it won't appear in the final rendering. So no
additional whitespace will pollute the template.

Because this code is executed before the actual layout template code it's
possible that the layout code overrides some of those variables. Usually
this is not a problem because of different variable names but it can be
a problem if you plan to specify default values.

What is happenning here is that, the slogan is first set by page.html, then again set by base.html, notice the ordering! The way to achieve the intended behavior is to change the slogan assignment in the base.html, so that it can capture the assignment done by the page.html. Here’s a version of base.html that works -

{% set slogan = slogan | default("This is an awesome site!") %}
{{ slogan }}
Tags: Python , Programming