从 jinja2 调用 python 函数

Posted

技术标签:

【中文标题】从 jinja2 调用 python 函数【英文标题】:Call a python function from jinja2 【发布时间】:2022-01-12 14:55:02 【问题描述】:

我正在使用 jinja2,我想调用一个 python 函数作为助手,使用类似于调用宏的语法。 jinja2 似乎有意阻止我进行函数调用,并坚持我通过将函数作为宏复制到模板中来重复自己。

有没有直接的方法来做到这一点?而且,有没有什么方法可以导入一整套 python 函数并让它们从 jinja2 访问,而无需经过大量繁琐的工作(例如编写扩展程序)?

【问题讨论】:

【参考方案1】:

对于使用 Flask 的用户,请将其放入您的 __init__.py

def clever_function():
    return u'HELLO'

app.jinja_env.globals.update(clever_function=clever_function)

并在您的模板中使用 clever_function() 调用它

【讨论】:

你能像这样传递多个函数吗? 在较新的版本中(我使用的是 Jinja2 2.9.6),它似乎更容易工作。像使用变量一样使用函数(也适用于更复杂的情况):from jinja2 import Template ##newline## def clever_function(): ##newline## return "Hello" ##newline## template = Template(" clever_function() ") ##newline## print(template.render(clever_function=clever_function)) 即使 8 年后,如果您使用的是 Flask,这似乎比任何最近的答案都更清洁。要回答来自@ffghfgh 的老问题,是的——你可以传递多个函数。 Semjon 的解决方案简直太棒了。像魅力一样工作! 现在flask.app有一个装饰器:@app.template_global(name)。 flask.palletsprojects.com/en/2.0.x/api/…github.com/pallets/flask/blob/…【参考方案2】:

注意:这是 Flask 特有的!

我知道这篇文章已经很老了,但是在新版本的 Flask 中使用上下文处理器有更好的方法。

可以轻松创建变量:

@app.context_processor
def example():
    return dict(myexample='This is an example')

上面可以在带有 Flask 的 Jinja2 模板中使用,如下所示:

 myexample 

(输出This is an example

以及完整的功能:

@app.context_processor
def utility_processor():
    def format_price(amount, currency=u'€'):
        return u'0:.2f1'.format(amount, currency)
    return dict(format_price=format_price)

上面这样使用时:

 format_price(0.33) 

(输出带有货币符号的输入价格)

或者,您可以使用jinja filters,烘焙到 Flask 中。例如。使用装饰器:

@app.template_filter('reverse')
def reverse_filter(s):
    return s[::-1]

或者,没有装饰器,手动注册函数:

def reverse_filter(s):
    return s[::-1]
app.jinja_env.filters['reverse'] = reverse_filter

上面两种方法应用的过滤器可以这样使用:

% for x in mylist | reverse %
% endfor %

【讨论】:

这些函数应该存在于何处,init、views 还是任何地方? __init__.py 假设你在那里声明了flask.Flask(__name__) 对 Jinja2 的问题投了反对票,答案是 Flask 特定的。 @AJP 理论上仍然回答了这个问题。这是解决问题的一种方法,前提是您也在使用 Flask。有点像所有的 javascript 问题经常回答是否有 jQuery 或者关于 Python 的问题经常回答 Python2 和 3。这个问题不排除 Flask。 (不像关于 Py2 的问题会排除 Py3 的答案)。这个答案对我有帮助。 非常有帮助,正是我想要的。 Jinja2 是 Web 框架的一部分,因此并不完全独立于后端。我使用 Python 在 Django 和 Flask 中工作,这篇文章以及这里的其他文章都与我相关。在我看来,试图过度指定问题与不必要地含糊不清一样有害。【参考方案3】:

我认为 jinja 故意让在模板中运行“任意”python 变得困难。它试图强化这样一种观点,即模板中的逻辑越少越好。

您可以在Environment 实例中操作全局命名空间来添加对您的函数的引用。它必须在您加载任何模板之前完成。例如:

from jinja2 import Environment, FileSystemLoader

def clever_function(a, b):
    return u''.join([b, a])

env = Environment(loader=FileSystemLoader('/path/to/templates'))
env.globals['clever_function'] = clever_function

【讨论】:

我也发现了这一点——您可以使用以下方式添加模块:import utils.helpers env.globals['helpers'] = utils.helpers @Lee。是的,您可以“注入”命名空间(模块)、函数、类实例等。它很有用,但不如 mako 等其他模板引擎灵活。不过,神社还有其他优点。如果有帮助,如果您接受答案,我将不胜感激:) 在做我的应用程序引擎项目(webapp2 和 jinja2)时为我做了诀窍。谢谢 @RobCowie 在将clever_function添加到字典env.globals后,如何从模板中调用该函数。 因此, clever_function('a', 'b') 【参考方案4】:
from jinja2 import Template

def custom_function(a):
    return a.replace('o', 'ay')

template = Template('Hey, my name is  custom_function(first_name)   func2(last_name) ')
template.globals['custom_function'] = custom_function

您也可以按照Matroskin's answer在字段中给出函数

fields = 'first_name': 'Jo', 'last_name': 'Ko', 'func2': custom_function
print template.render(**fields)

将输出:

Hey, my name is Jay Kay

适用于 Jinja2 2.7.3 版

如果你想要一个装饰器来简化 template.globals 上的定义函数,请查看 Bruno Bronosky's answer

【讨论】:

可能是因为你对其他人的答案投了反对票:( @BorkoKovacev 这不是一个好的理由。我只投了 2 个答案;关于 Flask 而不是 Jinja2 的答案。如果他们想编辑他们的答案以成为主题和关于 Jinja2 我会投票给他们。 一旦我注意到我已经看过jinga_html_template.globals['custom_function'] = custom_function 行,就可以完美地工作。有很大的不同。 我制作了这个答案的函数装饰器版本。它目前以 0 票排在最后 :,-( ***.com/a/47291097/117471 @BrunoBronosky 不错。已经投票了:) ...再给它十年,它可能会比我的高:P ...虽然永远不会赶上烧瓶;P【参考方案5】:

我喜欢@AJP's answer。我逐字使用它,直到我最终得到了很多功能。然后我切换到Python function decorator。

from jinja2 import Template

template = '''
Hi, my name is  custom_function1(first_name) 
My name is  custom_function2(first_name) 
My name is  custom_function3(first_name) 
'''
jinga_html_template = Template(template)

def template_function(func):
    jinga_html_template.globals[func.__name__] = func
    return func

@template_function
def custom_function1(a):
    return a.replace('o', 'ay')

@template_function
def custom_function2(a):
    return a.replace('o', 'ill')

@template_function
def custom_function3(a):
    return 'Slim Shady'

fields = 'first_name': 'Jo'
print(jinga_html_template.render(**fields))

好东西函数有一个__name__

【讨论】:

这太酷了。当你在 python 中注解一个函数时,它会自动将函数名传递给注解的函数吗? @BrunoBronosky 也很好地展示了对 python 装饰器的明智和干净的使用。很棒的帖子! 多么棒的实现! 我不熟悉装饰器这么简单!在我遇到的每个教程中,您都需要在外部函数中定义内部函数,然后在定义函数时调用 @ ,装饰器应该对其进行操作。疯狂!这对眼睛来说更容易。 @Maëlan 注意到我的装饰器接受并返回一个函数作为对象,并且名称是(不相关的)稍后设置的。所以,我可以做到custom_function1 = template_function(custom_function1)。这表明您想要的装饰器可以类似地用于嵌套,如return environmentalfuction(func)。但是,我不在电脑前尝试这个。让我知道它是否适合您。【参考方案6】:

在官方文档或堆栈溢出中从未见过如此简单的方法,但当我发现这个时我很惊讶:

# jinja2.__version__ == 2.8
from jinja2 import Template

def calcName(n, i):
    return ' '.join([n] * i)

template = Template("Hello  calcName('Gandalf', 2) ")

template.render(calcName=calcName)
# or
template.render('calcName': calcName)

【讨论】:

这个答案是迄今为止最好的恕我直言。您只需以与传递值完全相同的方式将函数传递给模板,毕竟所有函数都是 python 中的一等公民 :)【参考方案7】:

有一个更简单的决定。

@app.route('/x')
def x():
    return render_template('test.html', foo=y)

def y(text):
    return text

然后,在test.html中:

 foo('hi') 

【讨论】:

jinja2.exceptions.UndefinedError: 'y' is undefined 是的,因为应该在 test.html 中使用 foo【参考方案8】:

使用 lambda 将模板连接到您的主代码

return render_template("clever_template", clever_function=lambda x: clever_function x)

然后就可以无缝调用模板中的函数了

clever_function(value)

【讨论】:

巧妙使用 lambda 函数。 @odiumediae:不,不是。这是完全没有必要的。只需传递函数句柄本身:clever_function=clever_function @vezult 我明白了。我怎么能错过呢?感谢您清除它!【参考方案9】:

要从 Jinja2 调用 python 函数,您可以使用 custom filters,其工作方式与 globals 类似。

它非常简单实用。 在 myTemplate.txt 文件中,我写道:

 data | pythonFct 

在 python 脚本中:

import jinja2

def pythonFct(data):
    return "This is my data: 0".format(data)
    
input="my custom filter works!"
  
loader = jinja2.FileSystemLoader(path or './')
env = jinja2.Environment(loader=loader)
env.filters['pythonFct'] = pythonFct
result = env.get_template("myTemplate.txt").render(data=input)
print(result)

【讨论】:

【参考方案10】:

有什么方法可以导入一整套python函数并让它们从jinja2访问?

是的,除了上面的其他答案之外,这对我有用。

创建一个类并使用相关方法填充它,例如

class Test_jinja_object:

    def __init__(self):
        self.myvar = 'sample_var'

    def clever_function (self):
        return 'hello' 

然后在您的视图函数中创建您的类的实例,并将生成的对象作为 render_template 函数的参数传递给您的模板

my_obj = Test_jinja_object()

现在在您的模板中,您可以像这样调用 jinja 中的类方法

 my_obj.clever_function () 

【讨论】:

等效且稍微简单的方法:将模板的所有函数放在一个模块中,导入该模块并将其添加为模板全局。模块是一个包含函数的对象 :)(但不是方法——不需要 self 参数,也不需要类!) @ÉricAraujo 如果我只需要一个或两个模板中的一组函数而不是全部,该怎么办。另外,如果我在不同的 jinjas 模板中需要不同的 python 函数集怎么办?您是否仍然认为将所有这些作为模板全局导入而不是将它们作为方法放在一个类中并且只使用您需要的方法传递这些类是有效的。 要仅在特定模板中使用,我将仅将函数(或包含函数的模块)添加到使用这些模板的视图返回的模板上下文字典中。【参考方案11】:

要导入您可以使用的所有内置函数:

app.jinja_env.globals.update(__builtins__)

如果这不起作用,请在 __builtins__ 之后添加 .__dict__

基于John32323's answer。

【讨论】:

【参考方案12】:

如果你是用 Django 做的,你可以通过上下文传递函数:

context = 
    'title':'My title',
    'str': str,

...
return render(request, 'index.html', context)

现在您可以在 jinja2 模板中使用str 函数

【讨论】:

【参考方案13】:

@John32323 的回答是一个非常干净的解决方案。

这里是同一个,但保存到一个单独的文件中,也许更干净。

创建帮助文件

app\helper.py

from app import app

def clever_function_1():
    return u'HELLO'

def clever_function_2(a, b):
    return a + b



app.jinja_env.globals.update(
    clever_function_1=clever_function_1,
    clever_function_2=clever_function_2,
)

从应用导入

app.py

from app import routes
from app import helper   # add this one

这样使用

app\templates\some.html


 clever_function_1() 
 clever_function_2(a, b) 

【讨论】:

【参考方案14】:

对于使用 FastApi 的用户,请将其放入您的 __init__.py

from fastapi.templating import Jinja2Templates
templates = Jinja2Templates(directory="templates")

def clever_function():
    return u'HELLO'

templates.env.globals.update(clever_function=clever_function)

并在您的模板中使用 clever_function() 调用它

【讨论】:

以上是关于从 jinja2 调用 python 函数的主要内容,如果未能解决你的问题,请参考以下文章

XCTF-攻防世界CTF平台-Web类——12Web_python_template_injection(SSTI服务器模板注入Flask框架之Jinja2模板渲染引擎)

pytest + yaml 框架 -5.调用内置方法和自定义函数

从 Java 调用 python 函数的不同/更好的方法

如何从同步代码 Python 中调用异步函数

从python调用c++函数

如何调用名称以数字分隔的函数?从 t1 到 t20 的示例调用函数 - Python?