如何在Jinja2中让自定义模板函数获取render中的变量?
解决Jinja2自定义函数获取外部变量的问题(无需修改模板调用写法)
问题根源
你遇到的问题本质是作用域不共享:
- 第一个示例中,
do_it()属于全局作用域,无法访问calling_function()内的局部变量var1,所以抛出NameError。 - 第二个示例中,
calling_function()内的var1是局部变量,并没有修改全局的var1,所以do_it()拿到的还是初始空字符串。
可行方案(无需修改{{ do_it() }}写法)
方案1:使用闭包绑定变量
在calling_function()内部包装do_it,通过闭包捕获局部变量var1,这样模板调用do_it()时就能自动获取到变量值:
import jinja2 def calling_function(): var1 = "joe" # 定义闭包函数,捕获当前作用域的var1 def wrapped_do_it(): return "yes: %s" % var1 environment = jinja2.Environment() environment.globals.update({"do_it": wrapped_do_it}) output = environment.from_string("{{ do_it() }}").render() print(output) # 输出: yes: joe calling_function()
如果需要保留原do_it函数的逻辑,可以把它作为闭包的内部调用:
import jinja2 def do_it(var): return "yes: %s" % var def calling_function(): var1 = "joe" def wrapped_do_it(): return do_it(var1) environment = jinja2.Environment() environment.globals.update({"do_it": wrapped_do_it}) output = environment.from_string("{{ do_it() }}").render() print(output) calling_function()
方案2:用functools.partial绑定默认参数
通过functools.partial给do_it预设参数值,这样模板中无需传参就能调用:
import jinja2 from functools import partial def do_it(var): return "yes: %s" % var def calling_function(): var1 = "joe" # 绑定var1作为do_it的默认参数 wrapped_do_it = partial(do_it, var=var1) environment = jinja2.Environment() environment.globals.update({"do_it": wrapped_do_it}) output = environment.from_string("{{ do_it() }}").render() print(output) # 输出: yes: joe calling_function()
方案3:修改全局变量的正确方式(不推荐,但可行)
如果一定要用全局变量,需要在calling_function()内用global声明修改全局变量:
import jinja2 var1 = "" def calling_function(): global var1 # 声明要修改全局变量 var1 = "joe" environment = jinja2.Environment() environment.globals.update({"do_it": do_it}) output = environment.from_string("{{ do_it() }}").render() print(output) # 输出: yes: joe def do_it(): return "yes: %s" % var1 calling_function()
注:全局变量方式不推荐,尤其是在Celery多任务场景下,容易引发并发安全问题。
内容的提问来源于stack exchange,提问作者user25432002
相关产品推荐
相关产品推荐

