I am interested in rendering a template in multiple steps or keeping the tags for the undefined variables in Jinja2. I believe this would mean not only creating the 'UndefinedSilent" class (so the template won't crash on missing data) but also keeping the tags with the appropriate variable names if they are missing.
Example: Let's say we have the name = "Test" included in the context, but quantity is missing.
Givent the following template:
<p>{{name}} has {{quantity}}</p>
After rendering, I need the template to become:
<p>test has {{quantity}}</p>
Does anyone know if this is achievable?
Providing DebugUndefined
to named parameter undefined in the env, apparently does the trick. The rendered template preserves the {{<undefined variable}}
.
Like here:
from jinja2 import Environment, BaseLoader, DebugUndefined
rtemplate = Environment(loader=BaseLoader,undefined=DebugUndefined).from_string("{{ a }} is defined, but {{ b}} is undefined")
print(rtemplate.render({"a":"a"}))
The result is:
a is defined, but {{ b }} is undefined
It is achievable using the default built-in filter.
<p>{{name|default('{{name}}')}} has {{quantity|default('{{quantity}}')}}</p>
The drawback is that the code becomes uglier and the variable names are duplicated, thus reducing maintainability.
Here is another approach that preserves undefined double curly expressions after rendering, including those that contain "multilevel" (dot-notated) references as well as any others.
The answer provided by Willy Pregliasco does not support preservation of undefined list types, eg {{ some_list[4] }}
which is something I required. The below solution addresses this, as well as all possible schema types.
The idea is to parse the input template and try to resolve each expression with the provided context. Any that can not be resolved, we replace with a double curly expression that simply resolves to the original expression as a string.
Pass your template and context through the below preserve_undefineds_in_template
function before calling render:
from jinja2 import Template, StrictUndefined, UndefinedError
import re
def preserve_undefineds_in_template(template, context):
patt = re.compile(r'(\{\{[^\{]*\}\})')
j2_expressions = patt.findall(template)
for j2_expression in set(j2_expressions):
try:
Template(j2_expression, undefined=StrictUndefined).render(context)
except UndefinedError:
template = template.replace(j2_expression, f"{{% raw %}}{j2_expression}{{% endraw %}}")
return template
Example:
template = """hello {{ name }}, {{ preserve_me }} {{ preserve.me[2] }}"""
context = { "name": "Alice" }
# pass it through the preserver function
template = preserve_undefineds_in_template(template, context)
# template is now:
# hello {{ name }}, {% raw %}{{ preserve.me }}{% endraw %} {% raw %}{{ preserve.me.too[0] }}{% endraw %}
# render the new template as normal
result = Template(template).render(context)
print(result)
The output is:
hello Alice, {{ preserve_me }} {{ preserve.me[2] }}
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