保留未定义的变量
Keep undefined variables
我有兴趣在多个步骤中呈现模板或保留 Jinja2 中未定义变量的标签。我相信这不仅意味着创建“UndefinedSilent”class(因此模板不会因丢失数据而崩溃)而且还意味着在标签丢失时使用适当的变量名称。
示例:
假设我们在上下文中包含 name = "Test",但缺少 quantity。
给出以下模板:
<p>{{name}} has {{quantity}}</p>
渲染后,我需要模板变成:
<p>test has {{quantity}}</p>
有谁知道这是否可以实现?
使用 default built-in filter 可以实现。
<p>{{name|default('{{name}}')}} has {{quantity|default('{{quantity}}')}}</p>
缺点是代码变丑,变量名重复,降低可维护性
向环境中未定义的命名参数提供 DebugUndefined
,显然可以解决问题。呈现的模板保留了 {{<undefined variable}}
.
点赞:
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"}))
结果是:
a is defined, but {{ b }} is undefined
我也看到了同样的行为。库 jinja2schema 提供模板所需的变量架构。
我的解决步骤是:
- 有模板
- 获取模式结构
- 填写一些数据
- 完整的de数据用原始字符串填充缺失的项目
from jinja2 import Template
import jinja2schema
def assign(schema, data, root=''):
'''Returns a corrected data with untouched missing fields
'''
out = {}
for key in schema.keys():
if isinstance(schema[key], (str, jinja2schema.model.Scalar)):
try:
out[key] = data[key]
except:
out[key] = f'{{{{ {root+key} }}}}'
elif isinstance(schema[key], (dict, jinja2schema.model.Dictionary)):
out[key]={}
try:
data[key]
except:
data[key] = {}
out[key] = assign(schema[key], data[key], root+key+'.')
return out
# original template
template_str = '<p>{{name}} has {{quantity}}</p>'
# read schema
schema = jinja2schema.infer(template_str)
# available data
data = {'name':'test'}
# data autocompleted
data_corrected = assign(schema, data)
# render
template = Template(template_str)
print(template.render(data_corrected))
输出为
<p>test has {{ quantity }}</p>
这是预期的结果。
希望对您有所帮助。该解决方案不适用于列表,但我认为可以扩展该解决方案。如果需要,您还可以获得缺失字段的列表。
这是 Template
而不是 Environment
的版本:
from jinja2 import Template, DebugUndefined
template = Template("<p>{{name}} has {{quantity}}</p>", undefined=DebugUndefined)
new_template = Template(template.render(name="Test"))
感谢@giwyni
另一个小技巧,如果你只有几个变量:
<p>{{name}} has {{ "{{quantity}}" }}</p>
第二个替换将替换为 {{quantity}}
所以一切都很好 ;)
这是另一种在渲染后保留未定义的双卷曲表达式的方法,包括那些包含“多级”(dot-notated) 引用以及任何其他引用的表达式。
Willy Pregliasco 提供的答案不支持保留未定义的列表类型,例如 {{ some_list[4] }}
这是我需要的。下面的解决方案解决了这个问题,以及所有可能的模式类型。
想法是解析输入模板并尝试使用提供的上下文解析每个表达式。任何无法解析的,我们用一个双卷曲表达式替换,该表达式简单地解析为原始表达式作为字符串。
在调用 render 之前,通过下面的 preserve_undefineds_in_template
函数传递您的模板和上下文:
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
示例:
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)
输出为:
hello Alice, {{ preserve_me }} {{ preserve.me[2] }}
我有兴趣在多个步骤中呈现模板或保留 Jinja2 中未定义变量的标签。我相信这不仅意味着创建“UndefinedSilent”class(因此模板不会因丢失数据而崩溃)而且还意味着在标签丢失时使用适当的变量名称。
示例: 假设我们在上下文中包含 name = "Test",但缺少 quantity。
给出以下模板:
<p>{{name}} has {{quantity}}</p>
渲染后,我需要模板变成:
<p>test has {{quantity}}</p>
有谁知道这是否可以实现?
使用 default built-in filter 可以实现。
<p>{{name|default('{{name}}')}} has {{quantity|default('{{quantity}}')}}</p>
缺点是代码变丑,变量名重复,降低可维护性
向环境中未定义的命名参数提供 DebugUndefined
,显然可以解决问题。呈现的模板保留了 {{<undefined variable}}
.
点赞:
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"}))
结果是:
a is defined, but {{ b }} is undefined
我也看到了同样的行为。库 jinja2schema 提供模板所需的变量架构。
我的解决步骤是:
- 有模板
- 获取模式结构
- 填写一些数据
- 完整的de数据用原始字符串填充缺失的项目
from jinja2 import Template
import jinja2schema
def assign(schema, data, root=''):
'''Returns a corrected data with untouched missing fields
'''
out = {}
for key in schema.keys():
if isinstance(schema[key], (str, jinja2schema.model.Scalar)):
try:
out[key] = data[key]
except:
out[key] = f'{{{{ {root+key} }}}}'
elif isinstance(schema[key], (dict, jinja2schema.model.Dictionary)):
out[key]={}
try:
data[key]
except:
data[key] = {}
out[key] = assign(schema[key], data[key], root+key+'.')
return out
# original template
template_str = '<p>{{name}} has {{quantity}}</p>'
# read schema
schema = jinja2schema.infer(template_str)
# available data
data = {'name':'test'}
# data autocompleted
data_corrected = assign(schema, data)
# render
template = Template(template_str)
print(template.render(data_corrected))
输出为
<p>test has {{ quantity }}</p>
这是预期的结果。
希望对您有所帮助。该解决方案不适用于列表,但我认为可以扩展该解决方案。如果需要,您还可以获得缺失字段的列表。
这是 Template
而不是 Environment
的版本:
from jinja2 import Template, DebugUndefined
template = Template("<p>{{name}} has {{quantity}}</p>", undefined=DebugUndefined)
new_template = Template(template.render(name="Test"))
感谢@giwyni
另一个小技巧,如果你只有几个变量:
<p>{{name}} has {{ "{{quantity}}" }}</p>
第二个替换将替换为 {{quantity}}
所以一切都很好 ;)
这是另一种在渲染后保留未定义的双卷曲表达式的方法,包括那些包含“多级”(dot-notated) 引用以及任何其他引用的表达式。
Willy Pregliasco 提供的答案不支持保留未定义的列表类型,例如 {{ some_list[4] }}
这是我需要的。下面的解决方案解决了这个问题,以及所有可能的模式类型。
想法是解析输入模板并尝试使用提供的上下文解析每个表达式。任何无法解析的,我们用一个双卷曲表达式替换,该表达式简单地解析为原始表达式作为字符串。
在调用 render 之前,通过下面的 preserve_undefineds_in_template
函数传递您的模板和上下文:
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
示例:
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)
输出为:
hello Alice, {{ preserve_me }} {{ preserve.me[2] }}