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 }}