从jinja2调用python函数


144

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

有没有简单的方法可以做到这一点?而且,有没有什么方法可以导入整套python函数并使它们从jinja2中进行访问,而无需经历大量的rigamarole(例如编写扩展名)?

Answers:


223

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

def clever_function():
    return u'HELLO'

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

然后在您的模板中调用 {{ clever_function() }}


您可以通过这样的多种功能吗?
ffghfgh

6
在较新的版本中(我正在使用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))
SemjonMössinger17年

1
即使8年后,如果您使用的是Flask,这似乎也是比任何最近的答案更干净的解决方案。并回答@ffghfgh的旧问题,是的,您可以传递多个函数。
kevinmicke '19

132

注意:这是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:.2f}{1}'.format(amount, currency)
    return dict(format_price=format_price)

上面这样使用时:

{{ format_price(0.33) }}

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

另外,您也可以使用Flask中的jinja过滤器。例如使用装饰器:

@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 %}

4
这些函数应该存在于何处,init,视图还是仅存在于任何地方?
knk 2014年

3
__init__.py假设你flask.Flask(__name__)在那里宣布。
利亚姆·斯坦利

6
当被问及有关Jinja2的问题时,被否决了,答案是Flask特有的。
AJP 2014年

13
@AJP理论上仍然可以回答问题。如果您也使用Flask,这是解决问题的一种方法。有点像所有JavaScript问题通常会回答是否带jQuery或不带jQuery的问题,或者关于Python的问题通常会回答Python2和3。这个问题并不排除Flask。(不同于有关Py2的问题会排除Py3答案)。这个答案帮助了我。
杰罗米

2
非常有帮助,而且正是我想要的。Jinja2是Web框架的一部分,因此并不完全独立于后端。我在Django和Flask中都使用Python进行工作,本文以及其他与我相关的文章。在我看来,试图过度指定一个问题与不必要地含糊其辞一样有害。

76

我认为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

5
我也发现了这一点–您可以使用类似以下的方式添加模块: import utils.helpers env.globals['helpers'] = utils.helpers
Lee

@李 是的,您可以“注入”名称空间(模块),函数,类实例等。它很有用,但不如mako等其他模板引擎灵活。不过,神社还有其他优点。如果您能接受答案,我将不胜感激:)
罗布·考伊

在执行我的应用程序引擎项目(webapp2和jinja2)时为我完成了窍门。谢谢
Sojan V Jose 2015年

@RobCowie将clever_function添加到字典env.globals后,如何从模板中调用该函数。
豪尔赫·维迪尼亚

1
因此, {{ clever_function('a', 'b') }}
Rob Cowie

41
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的答案在字段中提供功能

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的答案


8
可能是因为您
否决了

13
@BorkoKovacev并不是一个很好的理由。我只对2个答案投了反对票;答案是关于Flask而不是Jinja2。如果他们想编辑关于主题的答案,并且关于Jinja2,我将对其进行投票。
AJP

Tx @BorkoKovacev :)
AJP

1
我做了这个答案的函数装饰器版本。目前,它以0票位于底部:
:-(

2
@BrunoBronosky很好。投票赞成:)……再给个十年,它可能比我高:P……虽然永远不会抓到烧瓶; P
AJP

25

我喜欢@AJP的答案。我逐字使用它,直到获得了很多功能。然后我切换到Python函数装饰器

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__


1
这真是太酷了。当您在python中注释函数时,它会自动将函数名称传递给注释的函数吗?
突变体城市'19

@mutant_city,是的。阅读该Python函数装饰器链接。好东西!
布鲁诺·布鲁诺斯基

1
@BrunoBronosky很好地演示了python装饰器的合理和清洁用法。很棒的帖子!
dreftymac

1
多么棒的实现!
菲利普·奥格

16

从来没有在官方文档或堆栈溢出中看到过如此简单的方法,但是当我发现这一点时,我感到惊讶:

# 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的一等公民之后,您只需按照与传递值完全相同的方式将函数传递给模板:)
Mark Kortink

8

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

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

然后,您可以无缝调用模板中的函数

{{clever_function(value)}}

1
聪明地使用lambda函数。

23
@odiumediae:不,不是。完全没有必要。只需传递函数本身即可:clever_function = clever_function
vezult 2016年

@vezult我明白了。我怎么会错过呢?感谢您清理!

6

要从Jinja2调用python函数,您可以使用自定义过滤器,其工作原理与全局变量类似: http //jinja.pocoo.org/docs/dev/api/#writing-filters

这非常简单和有用。在文件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)

5

有什么方法可以导入整套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 () }}

等效且稍微简单的方法:将模板的所有功能放在模块中,导入该模块并将其添加为全局模板。一个模块是包含功能:)对象(而不是方法-不需要自我PARAM和无级reqiured)
埃里克·阿劳霍

@ÉricAraujo如果我只需要一个或两个模板中的一组功能,而不是全部都需要该怎么办?另外,如果我在不同的Jinjas模板中需要不同的python函数集怎么办?您是否仍然认为将所有它们作为模板全局变量导入而不是将它们作为方法放入类并仅将类与所需的方法一起传递才有效。
Kudehinbu Oluwaponle,

要仅在特定模板中使用,我将仅将函数(或包含函数的模块)添加到由使用这些模板的视图所破坏的模板上下文字典中。
埃里克·阿劳霍


2

如果使用Django,则只需将函数与上下文一起传递即可:

context = {
    'title':'My title',
    'str': str,
}
...
return render(request, 'index.html', context)

现在您将能够使用strjinja2模板中的功能


1

有一个更简单的决定。

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

def y(text):
    return text

然后,在test.html中

{{ y('hi') }}

jinja2.exceptions.UndefinedError:'y'未定义
lww

是的,因为应该在test.html中使用foo
luckyguy73
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.