Rufen Sie eine Python-Funktion von jinja2 auf

144

Ich verwende jinja2 und möchte eine Python-Funktion als Helfer aufrufen, wobei ich eine ähnliche Syntax verwende, als würde ich ein Makro aufrufen. jinja2 scheint darauf bedacht zu sein, mich daran zu hindern, einen Funktionsaufruf zu tätigen, und besteht darauf, dass ich mich wiederhole, indem ich die Funktion als Makro in eine Vorlage kopiere.

Gibt es eine einfache Möglichkeit, dies zu tun? Und gibt es eine Möglichkeit, eine ganze Reihe von Python-Funktionen zu importieren und über jinja2 zugänglich zu machen, ohne viel Rigamarole zu durchlaufen (z. B. eine Erweiterung zu schreiben)?

Lee
quelle

Antworten:

223

Wenn Sie Flask verwenden, geben Sie Folgendes ein __init__.py:

def clever_function():
    return u'HELLO'

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

und in Ihrer Vorlage nennen Sie es mit {{ clever_function() }}

John32323
quelle
Können Sie mehrere Funktionen wie diese übergeben?
ffghfgh
6
In neueren Versionen (ich verwende Jinja2 2.9.6) scheint es viel einfacher zu funktionieren. Verwenden Sie die Funktion wie eine Variable (funktioniert auch in komplexeren Situationen):from jinja2 import Template ##newline## def clever_function(): ##newline## return "Hello" ##newline## template = Template("{{ clever_function() }}") ##newline## print(template.render(clever_function=clever_function))
Semjon Mössinger
1
Selbst 8 Jahre später, wenn Sie Flask verwenden, scheint dies eine sauberere Lösung zu sein als jede der neueren Antworten. Und um die alte Frage von @ffghfgh zu beantworten: Ja, Sie können mehrere Funktionen übergeben.
Kevinmicke
132

Hinweis: Dies ist flaschenspezifisch!

Ich weiß, dass dieser Beitrag ziemlich alt ist, aber es gibt bessere Methoden, dies in den neueren Versionen von Flask unter Verwendung von Kontextprozessoren zu tun.

Variablen können einfach erstellt werden:

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

Das Obige kann in einer Jinja2-Vorlage mit Flask wie folgt verwendet werden:

{{ myexample }}

(Welche Ausgänge This is an example)

Sowie vollwertige Funktionen:

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

Das obige, wenn es so verwendet wird:

{{ format_price(0.33) }}

(Welches gibt den Eingabepreis mit dem Währungssymbol aus)

Alternativ können Sie Jinja-Filter verwenden , die in Flask eingebrannt sind. ZB mit Dekorateuren:

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

Oder ohne Dekorateure und manuelles Registrieren der Funktion:

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

Filter, die mit den beiden oben genannten Methoden angewendet werden, können folgendermaßen verwendet werden:

{% for x in mylist | reverse %}
{% endfor %}
Liam Stanley
quelle
4
Wo sollen diese Funktionen existieren, Init, Ansichten oder einfach irgendwo?
Knk
3
__init__.pyvorausgesetzt, Sie haben dort erklärt flask.Flask(__name__).
Liam Stanley
6
Down hat als Frage zu Jinja2 abgestimmt und die Antwort ist flaschenspezifisch.
AJP
13
@AJP Beantwortet theoretisch immer noch die Frage. Dies ist EIN Weg, um das Problem zu lösen, vorausgesetzt, Sie verwenden auch Flask. Ein bisschen wie bei allen JavaScript-Fragen werden häufig Alternativen mit oder ohne jQuery beantwortet, oder Fragen zu Python werden häufig sowohl für Python2 als auch für 3 beantwortet. Die Frage schloss Flask nicht aus. (Im Gegensatz zu einer Frage zu Py2 würde eine Py3-Antwort ausgeschlossen). Diese Antwort hat mir geholfen.
Jeromej
2
Sehr hilfreich und genau das, wonach ich gesucht habe. Jinja2 ist Teil eines Webframeworks und daher nicht völlig unabhängig vom Back-End. Ich arbeite sowohl in Django als auch in Flask mit Python und dieser Beitrag sowie die anderen hier sind für mich relevant. Der Versuch, eine Frage zu genau zu spezifizieren, ist meiner Meinung nach ebenso schädlich wie unnötig vage.
76

Ich denke, Jinja macht es absichtlich schwierig, "beliebige" Python innerhalb einer Vorlage auszuführen. Es wird versucht, die Meinung durchzusetzen, dass weniger Logik in Vorlagen eine gute Sache ist.

Sie können den globalen Namespace innerhalb einer EnvironmentInstanz bearbeiten, um Verweise auf Ihre Funktionen hinzuzufügen. Dies muss erfolgen, bevor Sie Vorlagen laden. Beispielsweise:

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
Rob Cowie
quelle
5
Ich habe dies auch entdeckt - Sie können ein Modul mit so etwas hinzufügen: import utils.helpers env.globals['helpers'] = utils.helpers
Lee
@Lee. Ja, Sie können Namespaces (Module), Funktionen, Klasseninstanzen usw. "einfügen". Dies ist nützlich, aber nicht so flexibel wie andere Template-Engines wie mako. Trotzdem hat Jinja andere gute Punkte. Ich wäre dankbar, wenn Sie die Antwort akzeptieren würden, wenn es helfen würde :)
Rob Cowie
habe den Trick für mich gemacht, als ich mein App Engine-Projekt (webapp2 und jinja2) gemacht habe. danke
Sojan V Jose
@RobCowie nach dem Hinzufügen der clever_function zum Wörterbuch env.globals, wie kann man die Funktion aus der Vorlage aufrufen.
Jorge Vidinha
1
Also {{ 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

Sie können die Funktion auch in den Feldern gemäß der Antwort von Matroskin angeben

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

Wird ausgegeben:

Hey, my name is Jay Kay

Funktioniert mit Jinja2 Version 2.7.3

Und wenn Sie möchten, dass ein Dekorateur die Definition von Funktionen erleichtert, lesen Sie die Antworttemplate.globals von Bruno Bronosky

AJP
quelle
8
Wahrscheinlich, weil Sie die Antworten aller anderen abgelehnt haben :(
Borko Kovacev
13
@BorkoKovacev das ist kein guter Grund. Ich habe nur 2 Antworten abgelehnt. Antworten, die sich eher auf Flask als auf Jinja2 bezogen. Wenn sie ihre Antworten so bearbeiten möchten, dass sie zum Thema und zu Jinja2 gehören, werde ich sie abstimmen.
AJP
Tx @BorkoKovacev :)
AJP
1
Ich habe eine Funktionsdekorator-Version dieser Antwort erstellt. Es ist derzeit ganz unten mit 0 Stimmen :, - ( stackoverflow.com/a/47291097/117471
Bruno Bronosky
2
@ BrunoBronosky schön. Habe oben abgestimmt :) ... gib es noch ein Jahrzehnt und es könnte höher sein als meins: P ... wird die Flaschen aber nie fangen; P
AJP
25

Ich mag die Antwort von @ AJP . Ich habe es wörtlich verwendet, bis ich viele Funktionen hatte. Dann wechselte ich zu einem Python-Funktionsdekorateur .

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

Gut, dass Funktionen eine haben __name__!

Bruno Bronosky
quelle
1
Das ist wahnsinnig cool. Wenn Sie eine Funktion in Python mit Anmerkungen versehen, wird der Funktionsname automatisch an die Funktion der Anmerkung übergeben?
mutierte_stadt
@mutant_city, yep. Lesen Sie diesen Python-Funktionsdekorator-Link. Tolles Zeug!
Bruno Bronosky
1
@BrunoBronosky Hervorragende Demonstration einer vernünftigen und sauberen Verwendung auch für Python-Dekorateure. Guter Eintrag!
Dreftymac
1
Was für eine großartige Implementierung!
Philippe Oger
16

Ich habe noch nie einen so einfachen Weg bei offiziellen Dokumenten oder beim Stapelüberlauf gesehen, aber ich war erstaunt, als ich Folgendes fand:

# 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})
Matroskin
quelle
Diese Antwort ist bei weitem die beste imho. Sie übergeben die Funktion einfach genauso an die Vorlage, wie Sie einen Wert übergeben, nachdem alle Funktionen erstklassige Bürger in Python sind :)
Mark Kortink
8

Verwenden Sie ein Lambda, um die Vorlage mit Ihrem Hauptcode zu verbinden

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

Dann können Sie die Funktion in der Vorlage nahtlos aufrufen

{{clever_function(value)}}
Robert Onslow
quelle
1
Clevere Nutzung von Lambda-Funktionen.
23
@odiumediae: Nein, ist es nicht. Es ist völlig unnötig. Übergeben Sie einfach das Funktionshandle selbst: clever_function = clever_function
vezult
@vezult Ich verstehe. Wie könnte ich das vermissen? Danke, dass du das geklärt hast!
6

Um eine Python-Funktion von Jinja2 aus aufzurufen, können Sie benutzerdefinierte Filter verwenden, die ähnlich wie die globalen funktionieren: http://jinja.pocoo.org/docs/dev/api/#writing-filters

Es ist ganz einfach und nützlich. In einer Datei myTemplate.txt schrieb ich:

{{ data|pythonFct }}

Und in einem Python-Skript:

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)
Ben9000RPM
quelle
5

Gibt es eine Möglichkeit, eine ganze Reihe von Python-Funktionen zu importieren und über jinja2 darauf zugreifen zu können?

Ja, zusätzlich zu den anderen Antworten oben funktioniert dies für mich.

Erstellen Sie eine Klasse und füllen Sie sie mit den zugehörigen Methoden, z

class Test_jinja_object:

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

    def clever_function (self):
        return 'hello' 

Erstellen Sie dann eine Instanz Ihrer Klasse in Ihrer Ansichtsfunktion und übergeben Sie das resultierende Objekt als Parameter für die Funktion render_template an Ihre Vorlage

my_obj = Test_jinja_object()

Jetzt können Sie in Ihrer Vorlage die Klassenmethoden in jinja so aufrufen

{{ my_obj.clever_function () }}
Kudehinbu Oluwaponle
quelle
Gleichwertige und etwas einfachere Methode: Fügen Sie alle Funktionen für Vorlagen in ein Modul ein, importieren Sie dieses Modul und fügen Sie es als globale Vorlage hinzu. Ein Modul ist ein Objekt, das Funktionen enthält :) (aber keine Methoden - keine Notwendigkeit für Selbstparameter und keine Klasse erforderlich!)
Éric Araujo
@ ÉricAraujo Was ist, wenn ich nur die Funktionen in einer oder zwei Vorlagen benötige und nicht alle. Was ist auch, wenn ich verschiedene Sätze von Python-Funktionen in verschiedenen Jinjas-Vorlagen benötige? Würden Sie es dennoch für effektiv halten, alle als Vorlagenglobale zu importieren, anstatt sie als Methoden in eine Klasse einzufügen und die Klassen nur mit den von Ihnen benötigten Methoden zu übergeben?
Kudehinbu Oluwaponle
Um nur in bestimmten Vorlagen zu verwenden, würde ich die Funktionen (oder ein Modul, das Funktionen enthält) nur dem Vorlagenkontext hinzufügen, das von den Ansichten, die diese Vorlagen verwenden, wiederholt wird.
Éric Araujo
3

So importieren Sie alle integrierten Funktionen:

app.jinja_env.globals.update(__builtins__)

Fügen Sie .__dict__nach , __builtins__wenn dies nicht funktioniert.

Basierend auf der Antwort von John32323 .

Solomon Ucko
quelle
2

Wenn Sie es mit Django machen, können Sie die Funktion einfach mit dem Kontext übergeben:

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

Jetzt können Sie die strFunktion in der jinja2-Vorlage verwenden

Jahid
quelle
1

Es gibt eine viel einfachere Entscheidung.

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

def y(text):
    return text

Dann in test.html :

{{ y('hi') }}
Никита Шишкин
quelle
jinja2.exceptions.UndefinedError: 'y' ist undefiniert
lww
Ja, weil ich foo in test.html verwenden soll
luckguy73