Is it possible to render a Jinja2 template inside another template given by a string? For example, I want the string
{{ s1 }}
to be rendered to
Hello world
given the following dictionary as a param for Template.render
:
{ 's1': 'Hello {{ s2 }}', 's2': 'world' }
I know the similar process can be done with include
tag separating the content of s1
to the another file, but here I don't want to follow that way.
To reuse a Jinja template you use the Jinja built-in {% extends %} tag. The {% extends %} tag uses the syntax {% extends <name> %} to reuse the layout of another template. This means that in order to reuse the layout in listing 4-5 defined in a file base.
The default Jinja delimiters are configured as follows: {% ... %} for Statements. {{ ... }} for Expressions to print to the template output.
With Jinja imported, you can go on to load and render your first template: >>> import jinja2 >>> environment = jinja2.Environment() >>> template = environment.from_string("Hello, { { name }}!") >>> template.render(name="World") 'Hello, World!' The core component of Jinja is the Environment () class.
We import the Template object from the jinja2 module. Template is the central template object. It represents a compiled template and is used to evaluate it. In our template, we have the { { }} syntax which is used to print the variable. The variable is passed in the render () method. With the render () method, we generate the final output.
Jinja can generate any text-based format (HTML, XML, CSV, LaTeX, etc.). A Jinja template doesn’t need to have a specific extension: .html, .xml, or any other extension is just fine. A template contains variables and/or expressions, which get replaced with values when a template is rendered; and tags, which control the logic of the template.
The template engine is similar to the Python format () method; but template engines are more powerful and have many more features. We import the Template object from the jinja2 module. Template is the central template object. It represents a compiled template and is used to evaluate it.
I do not have an environment to easily test these ideas, but am exploring something similar within airflow's use of jinja templates.
From what I can find the best way to do this is do explicitly render the inner template string within the outer template. To do this you may need to pass or import the Template constructor in the param dictionary.
Here is some (untested) code:
from jinja2 import Template
template_string = '{{ Template(s1).render(s2=s2) }}'
outer_template = Template(template_string)
outer_template.render(
s1='Hello {{ s2 }}',
s2='world',
Template=Template
)
This is not nearly as clean as you were hoping for, so we may be able to take things further by creating a custom filter so we can use it like this:
{{ s1|inner_render({"s2":s2}) }}
Here is a custom filter I think will do the job:
from jinja2 import Template
def inner_render(value, context):
return Template(value).render(context)
Now let's assume we want the same context as the outer template, and - what the heck - lets render an arbitrary number of levels deep, N
. Hopefully some example usages will look like:
{{ s1|recursive_render }}
{{ s3|recursive_render(2) }}
An easy way to get the context from our custom filter is to use the contextfilter decorator
from jinja2 import Template
from jinja2 import contextfilter
@contextfilter
def recursive_render(context, value, N=1):
if N == 1:
val_to_render = value
else:
val_to_render = recursive_render(context, value, N-1)
return Template(value).render(context)
Now you can do something like s3 = '{{ s1 }}!!!'
and {{ s3|recursive_render(2) }}
should render to Hello world!!!
. I suppose you could go even deeper and detect how many levels to render by counting brackets.
Having gone through all this I would like to explicitly point out that this is very confusing.
Although I do believe I have found a need for 2 levels of rendering within my very specific airflow usage, I cannot imagine a need for more levels than that.
If you are reading this thinking "this is just what I need": Whatever you are trying to do can probably be done more eloquently. Take a step back, consider that you may have an xy problem, and re-read jinja's docs to be sure there isn't a better way.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With