web-dev-qa-db-fra.com

Appeler une fonction python depuis jinja2

J'utilise jinja2 et je souhaite appeler une fonction python comme aide, en utilisant une syntaxe similaire à celle utilisée pour appeler une macro. jinja2 semble vouloir m'empêcher de faire un appel de fonction et insiste pour que je me répète en copiant la fonction dans un modèle sous forme de macro. 

Y at-il un moyen simple de faire cela? Et est-il possible d'importer tout un ensemble de fonctions python et de les rendre accessibles à partir de jinja2, sans passer par beaucoup de rigamarole (comme écrire une extension)?

112
Lee

Pour ceux qui utilisent Flask, mettez ceci dans votre __init__.py:

def clever_function():
    return u'HELLO'

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

et dans votre modèle, appelez-le avec {{ clever_function() }}

191
John32323

Note: Ceci est spécifique à Flask!

Je sais que ce message est assez ancien, mais il existe de meilleures méthodes pour le faire dans les nouvelles versions de Flask utilisant des processeurs de contexte.

Les variables peuvent être facilement créées:

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

Ce qui précède peut être utilisé dans un modèle Jinja2 avec Flask comme ceci:

{{ myexample }}

(Quelles sorties This is an example)

Ainsi que des fonctions à part entière:

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

Le ci-dessus lorsqu'il est utilisé comme suit:

{{ format_price(0.33) }}

(Qui affiche le prix d'entrée avec le symbole monétaire)

Vous pouvez également utiliser jinja filters , cuit dans Flask. Par exemple. en utilisant des décorateurs:

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

Ou, sans décorateurs, et en enregistrant manuellement la fonction:

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

Les filtres appliqués avec les deux méthodes ci-dessus peuvent être utilisés comme ceci:

{% for x in mylist | reverse %}
{% endfor %}
103
Liam Stanley

Je pense que jinja rend délibérément difficile la gestion de python "arbitraire" dans un modèle. Il essaie de renforcer l’opinion selon laquelle moins de logique dans les modèles est une bonne chose.

Vous pouvez manipuler l'espace de noms global au sein d'une instance Environment pour ajouter des références à vos fonctions. Cela doit être fait avant vous chargez des modèles. Par exemple:

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
68
Rob Cowie
from jinja2 import Template

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

template = 'Hey, my name is {{ custom_function(first_name) }}'
jinga_html_template = Template(template)
jinga_html_template.globals['custom_function'] = custom_function

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

Est-ce que la sortie:

Hey, my name is Jay

Fonctionne avec Jinja2 version 2.7.3

34
AJP

Utilisez un lambda pour connecter le modèle à votre code principal

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

Ensuite, vous pouvez appeler de manière transparente la fonction dans le modèle

{{clever_function(value)}}
8
Robert Onslow

Je n'ai jamais vu un moyen aussi simple dans les documents officiels ou au débordement de pile, mais j'ai été étonné de constater ceci:

# 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})
8
Matroskin

J'aime la réponse de @ AJP . Je l'ai utilisé textuellement jusqu'à ce que je termine avec beaucoup de fonctions. Puis je suis passé à un décorateur de fonctions 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))

Les bonnes choses ont un __name__!

8
Bruno Bronosky

Pour appeler une fonction python depuis Jinja2, vous pouvez utiliser des filtres personnalisés qui fonctionnent de la même façon que les globals: http://jinja.pocoo.org/docs/dev/api/#writing-filters

C'est assez simple et utile . Dans un fichier myTemplate.txt, j'ai écrit:

{{ data|pythonFct }}

Et dans un script 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)
3
Ben9000RPM

est-il possible d'importer tout un ensemble de fonctions python et de les avoir accessibles à partir de jinja2?

Oui, en plus des autres réponses ci-dessus, cela fonctionne pour moi. 

Créez une classe et remplissez-la avec les méthodes associées, par exemple

class Test_jinja_object:

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

    def clever_function (self):
        return 'hello' 

Créez ensuite une instance de votre classe dans votre fonction de vue et transmettez l'objet résultant à votre modèle en tant que paramètre pour la fonction render_template

my_obj = Test_jinja_object()

Maintenant, dans votre modèle, vous pouvez appeler les méthodes de classe en jinja comme

{{ my_obj.clever_function () }}
1

Pour importer toutes les fonctions intégrées, vous pouvez utiliser:

app.jinja_env.globals.update(__builtins__)

Ajoutez .__dict__ après __builtins__ si cela ne fonctionne pas.

Basé sur la réponse de John32323 .

0
Solomon Ucko

Si vous le faites avec Django, vous pouvez simplement passer la fonction avec le contexte:

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

Vous pourrez maintenant utiliser la fonction str dans le modèle jinja2

0
Jahid