Викличте функцію python від jinja2


144

Я використовую jinja2, і я хочу викликати функцію python як помічник, використовуючи аналогічний синтаксис, як якщо б я викликав макрос. jinja2, схоже, має намір запобігти мені робити виклик функції, і наполягає на тому, що я повторюю себе, копіюючи функцію в шаблон як макрос.

Чи є простий спосіб це зробити? І чи є можливість імпортувати цілий набір функцій python та мати їх доступними від jinja2, не проходячи цілу кількість ригамаролів (наприклад, написання розширення)?

Відповіді:


223

Для тих, хто використовує колбу, помістіть це у своє __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))
Semjon Mössinger

1
Навіть через 8 років, якщо ви використовуєте Flask, це здається більш чистим рішенням, ніж будь-який із останніх відповідей. І щоб відповісти на старе питання від @ffghfgh, так - ви можете пройти кілька функцій.
kevinmicke

133

Примітка. Це специфічна колба!

Я знаю, що ця публікація досить стара, але існують кращі методи цього робити в нових версіях Flask за допомогою контекстних процесорів.

Змінні можна легко створити:

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

Вищезазначене можна використовувати в шаблоні 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) }}

(Яка виводить ціну вводу із символом валюти)

Крім того, ви можете використовувати фільтри джинджа , запечені в колбу. Наприклад, використовуючи декоратори:

@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, представлення даних або просто де завгодно?
кнк

3
__init__.pyякщо припустити, що ви flask.Flask(__name__)там декларували .
Ліам Стенлі

6
Даун проголосував на запитання про Jinja2, а відповідь - специфічна колба.
AJP

13
@AJP Ще теоретично відповідає на питання. Це ОДИН спосіб вирішити проблему, якщо ви також використовуєте Flask. Так само, як і всі питання JavaScript, часто відповідають на надання альтернатив з jQuery або без нього, або на питання про Python, часто відповідають як для Python2, так і 3. Питання не виключає Flask. (на відміну від запитання про Py2 виключатиме відповідь Py3). Ця відповідь мені допомогла.
jeromej

2
Дуже корисно, і саме те, що я шукав. Jinja2 є частиною веб-бази, і як така не є повністю незалежною від бек-енду. Я працюю і в Django, і в Flask з Python, і ця посада, а також інші тут стосуються мене. Намагання над уточнити питання, на мій погляд, так само шкідливо, як і невиправдано розпливчасте.

76

Я думаю, що jinja навмисно ускладнює запуск «довільного» пітона в шаблоні. Він намагається нав'язати думку, що менше логіки в шаблонах - це добра справа.

Ви можете маніпулювати глобальним простором імен в 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

@RobCowie після додавання розумної функції до словника env.globals, як можна викликати функцію з шаблону.
Хорхе Відінья

1
Таким чином, {{ clever_function('a', 'b') }}
Роб Коуї

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

Ви також можете надати функцію в полях відповідно до відповіді Матроскіна

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ознайомтеся з відповіддю Бруно Броноського


8
Ймовірно, тому, що ви спростували відповіді всіх інших :(
Борко Ковачев

13
@BorkoKovacev це не вагома причина. Я лише проголосував 2 відповіді; відповіді, які стосувались колби, а не Jinja2. Якщо вони хочуть відредагувати свої відповіді на тему і про Jinja2, я буду голосувати за них.
AJP

Tx @BorkoKovacev :)
AJP

1
Я зробив варіант цієї відповіді-декоратора. В даний час на дні з 0 голосів: - ( stackoverflow.com/a/47291097/117471
Бруно Bronosky

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, чи автоматично вона передає ім'я функції функції анотації?
mutant_city

@mutant_city, так. Прочитайте, що посилання декоратора функції Python. Чудові речі!
Бруно Броноський

1
@BrunoBronosky Відмінна демонстрація розумного та чистого використання для декораторів-пітонів. Чудовий пост!
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})

Ця відповідь на сьогоднішній день є найкращим імхо. Ви просто передаєте функцію шаблону точно так само, як і
передаєте

8

Використовуйте лямбда, щоб підключити шаблон до основного коду

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

Тоді ви можете без проблем викликати функцію в шаблоні

{{clever_function(value)}}

1
Розумне використання лямбда-функцій.

23
@odiumediae: Ні, це не так. Це зовсім непотрібно. Просто передайте функцію самої ручки: clever_function = clever_function
vezult

@vezult Я бачу. Як я міг це пропустити? Дякуємо, що очистили це!

6

Щоб викликати функцію python від Jinja2, ви можете використовувати спеціальні фільтри, які працюють аналогічно глобальним: 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 () }}

Еквівалентний і трохи простіший спосіб: покласти всі функції для шаблонів у модуль, імпортувати цей модуль і додати його як шаблон глобального. Модуль - це об'єкт, який містить функції :) (але не методи - немає необхідності в самопараметрі та відсутності переналагодження класу!)
Éric Araujo

@ ÉricAraujo Що робити, якщо мені потрібен лише набір функцій в одному або двох шаблонах, а не у всіх. Крім того, що робити, якщо мені потрібні різні набори функцій python в різних шаблонах jinjas? Ви все ще вважаєте за ефективне імпортувати їх як шаблони глобальних шаблонів, а не ставити їх як методи у класі та передавати класи лише потрібними методами.
Kudehinbu Oluwaponle

Для використання лише у певних шаблонах я б додав функції (або модуль, що містить функції) лише до контексту шаблону шаблону, який повертається переглядами, які використовують ці шаблони.
Éric Araujo

3

Щоб імпортувати всі вбудовані функції, ви можете використовувати:

app.jinja_env.globals.update(__builtins__)

Додати .__dict__після, __builtins__якщо це не працює.

На основі відповіді John32323 .


2

Якщо ви робите це з Django, ви можете просто передати функцію контексту:

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

Тепер ви зможете використовувати strфункцію в шаблоні jinja2


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: 'у' не визначене
LWW

так, тому що повинен використовувати foo в test.html
luckyguy73
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.