Call a python function from jinja2
I am using jinja2,开发者_Go百科 and I want to call a python function as a helper, using a similar syntax as if I were calling a macro. jinja2 seems intent on preventing me from making a function call, and insists I repeat myself by copying the function into a template as a macro.
Is there any straightforward way to do this? And, is there any way to import a whole set of python functions and have them accessible from jinja2, without going through a whole lot of rigamarole (such as writing an extension)?
For those using Flask, put this in your __init__.py
:
def clever_function():
return u'HELLO'
app.jinja_env.globals.update(clever_function=clever_function)
and in your template call it with {{ clever_function() }}
Note: This is Flask specific!
I know this post is quite old, but there are better methods of doing this in the newer versions of Flask using context processors.
Variables can easily be created:
@app.context_processor
def example():
return dict(myexample='This is an example')
The above can be used in a Jinja2 template with Flask like so:
{{ myexample }}
(Which outputs This is an example
)
As well as full fledged functions:
@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)
The above when used like so:
{{ format_price(0.33) }}
(Which outputs the input price with the currency symbol)
Alternatively, you can use jinja filters, baked into Flask. E.g. using decorators:
@app.template_filter('reverse')
def reverse_filter(s):
return s[::-1]
Or, without decorators, and manually registering the function:
def reverse_filter(s):
return s[::-1]
app.jinja_env.filters['reverse'] = reverse_filter
Filters applied with the above two methods can be used like this:
{% for x in mylist | reverse %}
{% endfor %}
I think jinja deliberately makes it difficult to run 'arbitrary' python within a template. It tries to enforce the opinion that less logic in templates is a good thing.
You can manipulate the global namespace within an Environment
instance to add references to your functions. It must be done before you load any templates. For example:
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
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
You can also give the function in the fields as per Matroskin's answer
fields = {'first_name': 'Jo', 'last_name': 'Ko', 'func2': custom_function}
print template.render(**fields)
Will output:
Hey, my name is Jay Kay
Works with Jinja2 version 2.7.3
And if you want a decorator to ease defining functions on template.globals
check out Bruno Bronosky's answer
I like @AJP's answer. I used it verbatim until I ended up with a lot of functions. Then I switched to a 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))
Good thing functions have a __name__
!
Never saw such simple way at official docs or at stack overflow, but i was amazed when found this:
# 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})
There's a much simpler decision.
@app.route('/x')
def x():
return render_template('test.html', foo=y)
def y(text):
return text
Then, in test.html:
{{ foo('hi') }}
To call a python function from Jinja2, you can use custom filters which work similarly as the globals
.
It's quite simple and useful. In a file myTemplate.txt, I wrote:
{{ data | pythonFct }}
And in a python script:
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)
Use a lambda to connect the template to your main code
return render_template("clever_template", clever_function=lambda x: clever_function x)
Then you can seamlessly call the function in the template
{{clever_function(value)}}
is there any way to import a whole set of python functions and have them accessible from jinja2 ?
Yes there is, In addition to the other answers above, this works for me.
Create a class and populate it with the associated methods e.g
class Test_jinja_object:
def __init__(self):
self.myvar = 'sample_var'
def clever_function (self):
return 'hello'
Then create an instance of your class in your view function and pass the resultant object to your template as a parameter for the render_template function
my_obj = Test_jinja_object()
Now in your template, you can call the class methods in jinja like so
{{ my_obj.clever_function () }}
To import all the builtin functions you can use:
app.jinja_env.globals.update(__builtins__)
Add .__dict__
after __builtins__
if this doesn't work.
Based on John32323's answer.
@John32323 's answer is a very clean solution.
Here is the same one, but save into a seperate file, maybe more cleaner.
Create helper file
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,
)
Import from app
app.py
from app import routes
from app import helper # add this one
Use like this
app\templates\some.html
{{ clever_function_1() }}
{{ clever_function_2(a, b) }}
If you are doing it with Django, you can just pass the function with the context:
context = {
'title':'My title',
'str': str,
}
...
return render(request, 'index.html', context)
Now you will be able to use the str
function in jinja2 template
For those using FastApi, put this in your __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)
and in your template call it with {{ clever_function() }}
Creating a global function without passing to the template
@app.template_global('double')
def double(n):
return 2 * n
Jinja Usage`enter code here`
{{double(77)}}
Or
Creating a filter in jinja.
@app.template_filter('geo_stuff')
def hellome(a,b='dadadd'):
d=a+'it is jerry'
return d
jinja use
{{'foo'|geo_stuff}}
精彩评论