synic
synic

Reputation: 26668

jinja2: overload how the templates load all variables

Is there a way to overload how jinja evaluates all variable expressions?

I want to change how the templating system evaluates all variables, in {{ variable }} expressions, and in {% if variable %} type statements as well (I'd like to make it load them from a specific location in a specific way, by name).

For instance, if someone puts {{ this.is.a.test }} in a template, I want to use jmespath (https://github.com/jmespath/jmespath.py) to load the variable from a known dictionary in the context, instead of the normal route. It would pick "hello" from this dictionary:

{
  "this": {
    "is": {
      "a": {
        "test": "hello"
      }
    }
  }
}

This would also evaluate to true: {% if this.is.a.test == "hello" %}

Is this something that is possible?

Upvotes: 0

Views: 215

Answers (1)

Waylan
Waylan

Reputation: 42497

Just pass your dict in as the context and it will work out of the box.

>>> from jinja2 import Environment
>>> env = Environment()
>>> template = env.from_string('{{ this.is.a.test }}')
>>> context = {
...   "this": {
...     "is": {
...       "a": {
...         "test": "hello"
...       }
...     }
...   }
... }
>>> print(template.render(**context))
hello

There are two factors which make this work. First, as Jinja's documentation explains:

You can use a dot (.) to access attributes of a variable in addition to the standard Python __getitem__ “subscript” syntax ([]).

The following lines do the same thing:

{{ foo.bar }}
{{ foo['bar'] }}

The second is the use of ** when passing in a dictionary expands the first level to keywords and values. In other words, the following two lines are exactly the same:

foo(**{'bar': 'baz'})
foo(bar='baz')

Of course, Jinja's Template.render method simply accepts keywords as root level variables, so that gives you what you want.

Upvotes: 1

Related Questions