214

I am using jinja2, 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)?

15 Answers 15

273

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() }}

Sign up to request clarification or add additional context in comments.

8 Comments

can you pass multiple functions like this?
In newer versions (I am using Jinja2 2.9.6) it seems to work much easier. Use the function as you would use a variable (works also in more complex situations): from jinja2 import Template ##newline## def clever_function(): ##newline## return "Hello" ##newline## template = Template("{{ clever_function() }}") ##newline## print(template.render(clever_function=clever_function))
Even 8 years later, if you're using Flask, this seems like a cleaner solution than any of the more recent answers. And to answer the old question from @ffghfgh , yes—you can pass multiple functions.
The solution from Semjon is simply brilliant. Works like a charm!
Nowadays there is a decorator for this for flask.app: @app.template_global(name). flask.palletsprojects.com/en/2.0.x/api/… github.com/pallets/flask/blob/…
|
175

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

11 Comments

where should these functions exist, init, views or just anywhere?
__init__.py assuming you declared flask.Flask(__name__) there.
Down voted as question asked about Jinja2 and answer is Flask specific.
@AJP Still theoretically answers the question. This is ONE way to solve the issue, granted you are also using Flask. A bit like all JavaScript questions often answer giving alternatives with or without jQuery or questions about Python often answer both for Python2 and 3. The question wasn't excluding Flask. (unlike a question about Py2 would exclude a Py3 answer). This answer helped me.
Very helpful, and just what I was looking for. Jinja2 is part of a web framework, and as such is not totally independent of the back-end. I work in both Django and Flask with Python and this post, as well as the others here are relevant to me. Trying to over specify a question is as harmful in my opinion as being unnecessarily vague.
|
99

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

6 Comments

I discovered this also -- you can add a module using something like this: import utils.helpers env.globals['helpers'] = utils.helpers
@Lee. Yes, you can 'inject' namespaces (modules), functions, class instances etc. It is useful, but not as flexible as other template engines like mako. Still, jinja has other good points. I'd be grateful if you accept the answer if it helped :)
did the trick for me while doing my app engine project ( webapp2 and jinja2 ) . thanks
@RobCowie after adding the clever_function to dictionary env.globals, how can one call the function from the template.
Thus, {{ clever_function('a', 'b') }}
|
61
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

5 Comments

Probably because you downvoted everyone else's answers :(
@BorkoKovacev that's not a good reason. I only down voted 2 answers; answers which were about Flask rather than Jinja2. If they want to edit their answers to be on topic and about Jinja2 I will up vote them.
works perfectly once I noticed that I had over looked the jinga_html_template.globals['custom_function'] = custom_function line. Makes a big difference.
I made a function decorator version of this answer. It currently at the bottom with 0 votes :,-( stackoverflow.com/a/47291097/117471
@BrunoBronosky nice. Have up voted :) ... give it another decade and it might be higher than mine :P ... will never catch the flasks though ;P
54

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__!

9 Comments

This is insanely cool. When you annotate a function in python does it automatically pass the function name to the annotation's function?
@BrunoBronosky Excellent demonstration of a sensible and clean use for python decorators as well. Great post!
What a great implementation!
I'm not familiar with decorators being so simple! In every tutorial I've come across you need to define an inner function within an outer function, then call @<decorator> when do you define a function, which should be acted upon by the decorator. Insanity! This is much easier on the eyes.
@Maëlan notice that my decorator takes and returns a function as an object and the name is (irrelevant) set later. So, I could have done custom_function1 = template_function(custom_function1). That suggests that your desired decorator could be used similarly for nesting like so return environmentalfuction(func). However, I'm not at a computer to try this. Let me know if it works for you.
|
29

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})

1 Comment

This answer is by far the best imho. You just pass the function to the template in exactly the same way you pass a value, after all functions are first class citizens in python :)
19

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') }}

4 Comments

jinja2.exceptions.UndefinedError: 'y' is undefined
yeah, cuz supposed to use foo in test.html
Not bad, but then you have to pass the function to every single render_template() call
You could create a dictionary of functions pass it instead. The dictionary could be created by writing an annotation, that saves the functions.
9

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)

Comments

7

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)}}

3 Comments

Clever usage of lambda functions.
@odiumediae: No it's not. It's completely unnecessary. Just pass the function handle itself: clever_function=clever_function
@vezult I see. How could I miss that? Thanks for clearing that up!
6

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 () }}

3 Comments

Equivalent and slightly simpler way: put all functions for templates in a module, import that module and add it as template global. A module is an object that contains functions :) (but not methods — no need for self param and no class reqiured!)
@ÉricAraujo What if I only need the set of functions in one or two templates and not all of them. Also, what if I need different sets of python functions in different jinjas templates? Would you still consider it effective to import all of them as template globals rather than put them as methods in a class and only pass the classses with the methods you need.
To use only in specific templates, I would add the functions (or a module containing functions) only to the template context dict that’s returrned by the views that use those templates.
5

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.

Comments

4

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

1 Comment

The elegance of this solution is astounding. Good job. I only define the functions in the app.py file and then there are no ugly imports.
4

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() }}

Comments

2

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

Comments

1
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}}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.