Jinja2'den bir python işlevi çağırın


144

Ben jinja2 kullanıyorum ve ben bir makro çağırıyor gibi benzer bir sözdizimi kullanarak bir yardımcı olarak bir python işlevi çağırmak istiyorum. jinja2, bir işlev çağrısı yapmama engel olmaya niyet ediyor gibi görünüyor ve işlevi makro olarak bir şablona kopyalayarak kendimi tekrar etmem konusunda ısrar ediyor.

Bunu yapmanın kolay bir yolu var mı? Ve, bir dizi python işlevini içe aktarmanın ve bir sürü rigamarole (bir uzantı yazma gibi) geçmeden jinja2'den erişmelerini sağlamanın herhangi bir yolu var mı?

Yanıtlar:


223

Flask kullananlar için bunu aşağıdakilere ekleyin __init__.py:

def clever_function():
    return u'HELLO'

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

ve şablonunuzda {{ clever_function() }}


bunun gibi birden fazla işlevi iletebilir misiniz?
ffghfgh

6
Daha yeni sürümlerde (Jinja2 2.9.6 kullanıyorum) çok daha kolay çalışıyor gibi görünüyor. Bir değişkeni kullandığınız gibi işlevi kullanın (daha karmaşık durumlarda da çalışır):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 yıl sonra bile, Flask kullanıyorsanız, bu daha yeni cevaplardan daha temiz bir çözüm gibi görünüyor. Ve @ffghfgh'dan gelen eski soruyu cevaplamak için, evet — birden fazla işlevi iletebilirsiniz.
kevinmicke

133

Not: Bu Flask'a özgüdür!

Bu yazının oldukça eski olduğunu biliyorum, ancak bunu bağlam işlemcilerini kullanarak Flask'ın yeni sürümlerinde yapmanın daha iyi yöntemleri var.

Değişkenler kolayca oluşturulabilir:

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

Yukarıda Flask ile bir Jinja2 şablonunda kullanılabilir:

{{ myexample }}

(Hangi çıktılar This is an example)

Tam teşekküllü fonksiyonların yanı sıra:

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

Yukarıdaki gibi kullanıldığında:

{{ format_price(0.33) }}

(Girdi fiyatını para birimi simgesiyle veren)

Alternatif olarak, Flask'a pişmiş jinja filtreleri kullanabilirsiniz . Örneğin dekoratörler kullanmak:

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

Veya dekoratörler olmadan ve işlevi manuel olarak kaydetme:

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

Yukarıdaki iki yöntemle uygulanan filtreler şu şekilde kullanılabilir:

{% for x in mylist | reverse %}
{% endfor %}

4
bu işlevler nerede bulunmalı, başlatılmalı, görüntülenmeli veya herhangi bir yerde olmalıdır?
27'de knk

3
__init__.pyflask.Flask(__name__)orada beyan ettiğinizi varsayarak .
Liam Stanley

6
Down, Jinja2 hakkında soru sorulduğunda oy verdi ve cevap Flask'a özgüdür.
AJP

13
@AJP Hala teorik olarak soruyu cevaplıyor. Bu sorunu çözmenin BİR yoludur, ayrıca Flask kullanıyorsunuz. Tüm JavaScript soruları çoğu zaman jQuery ile veya olmadan alternatifler vermeye cevap veriyor veya Python ile ilgili sorular genellikle Python2 ve 3 için cevap veriyor. Soru Flask'ı hariç tutmadı. (Py2 ile ilgili bir sorunun aksine Py3 cevabı hariç tutulur). Bu cevap bana yardımcı oldu.
jeromej

2
Çok yararlı ve sadece ne aradığını. Jinja2 bir web çerçevesinin bir parçasıdır ve bu nedenle arka uçtan tamamen bağımsız değildir. Django ve Flask'ta Python ve bu yazı ile çalışıyorum ve buradaki diğerleri benimle alakalı. Bir soruyu gereğinden fazla belirtmeye çalışmak, bence gereksiz yere belirsiz olmak kadar zararlı.

76

Bence jinja bir şablon içinde 'keyfi' python çalıştırmayı zorlaştırıyor. Şablonlardaki daha az mantığın iyi bir şey olduğu fikrini güçlendirmeye çalışır.

Environmentİşlevlerinize başvurular eklemek için bir örnek içindeki genel ad alanını değiştirebilirsiniz . Herhangi bir şablon yüklemeden önce yapılmalıdır . Örneğin:

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
Bunu da keşfettim - böyle bir şey kullanarak bir modül ekleyebilirsiniz: import utils.helpers env.globals['helpers'] = utils.helpers
Lee

@Lee. Evet, ad alanlarını (modülleri), işlevleri, sınıf örneklerini vb. 'Enjekte edebilirsiniz'. Yararlıdır, ancak mako gibi diğer şablon motorları kadar esnek değildir. Yine de jinja'nın başka iyi noktaları var. Eğer yardımcı olduysa cevabı kabul ederseniz minnettar olurum :)
Rob Cowie

uygulama motoru projemi (webapp2 ve jinja2) yaparken benim için hile yaptı. teşekkürler
Sojan V Jose

@RobCowie, env.globals sözlüğüne clever_function ekledikten sonra işlevi şablondan nasıl çağırabilir.
Jorge Vidinha

1
Böylece, {{ 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

Ayrıca işlevi Matroskin'in cevabına göre alanlara da verebilirsiniz

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

Çıktı olacak:

Hey, my name is Jay Kay

Jinja2 sürüm 2.7.3 ile çalışır

Ve bir dekoratörün Bruno Bronosky'nin cevabınatemplate.globals göz atma işlevlerini tanımlamasını kolaylaştırmasını istiyorsanız


8
Muhtemelen başkalarının cevaplarını
düşürdüğünüz için

13
@BorkoKovacev bu iyi bir neden değil. Sadece 2 cevap verdim; Jinja'dan ziyade Flask ile ilgili cevaplar. Eğer cevaplarını konu hakkında ve Jinja2 ile ilgili olarak düzenlemek istiyorlarsa, ben oylayacağım.
AJP

Tx @BorkoKovacev :)
AJP

1
Bu cevabın fonksiyon dekoratör versiyonunu yaptım. Şu anda 0 oyla altta:, - ( stackoverflow.com/a/47291097/117471
Bruno Bronosky

2
@BrunoBronosky güzel. Yukarı oy ver :) ... bir on yıl daha ver ve benimkinden daha yüksek olabilir: P ... şişeleri asla yakalayamayacak; P
AJP

25

@ AJP'nin cevabını seviyorum . Bir çok fonksiyonla bitene kadar kelimesi kelimesine kullandım. Sonra bir Python fonksiyon dekoratörüne geçtim .

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

İyi bir şey fonksiyonları var __name__!


1
Bu delicesine harika. Python'da bir işleve ek açıklama eklediğinizde, işlev adını otomatik olarak ek açıklama işlevine geçirir mi?
mutant_city

@ mutant_city, evet. Bu Python işlevi dekoratör bağlantısını okuyun. Harika şeyler!
Bruno Bronosky

1
@BrunoBronosky Python dekoratörleri için de mantıklı ve temiz bir kullanımın mükemmel bir gösterimi. Harika gönderi!
dreftymac

1
Ne harika bir uygulama!
Philippe Oger

16

Resmi belgelerde veya yığın taşmasında böyle basit bir yol görmedim, ancak bunu bulduğumda şaşırdım:

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

Bu cevap açık ara en iyi imho. Tüm fonksiyonlar
python'da

8

Şablonu ana kodunuza bağlamak için lambda kullanın

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

Ardından, şablondaki işlevi sorunsuz bir şekilde çağırabilirsiniz

{{clever_function(value)}}

1
Lambda fonksiyonlarının akıllı kullanımı.

23
@odiumediae: Hayır, değil. Tamamen gereksiz. İşlev tutamacının kendisini geçmeniz yeterlidir: clever_function = clever_function
vezult

@vezult Anlıyorum. Bunu nasıl özleyebilirim? Temizlediğiniz için teşekkürler!

6

Jinja2'den bir python işlevini çağırmak için, küresellere benzer şekilde çalışan özel filtreler kullanabilirsiniz: http://jinja.pocoo.org/docs/dev/api/#writing-filters

Oldukça basit ve kullanışlı. MyTemplate.txt dosyasında şunu yazdım:

{{ data|pythonFct }}

Ve bir python betiğinde:

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

bir dizi python fonksiyonlarını içe aktarmanın ve jinja2'den erişilebilir olmasını sağlamanın bir yolu var mı?

Evet, yukarıdaki diğer cevaplara ek olarak, bu benim için işe yarıyor.

Bir sınıf oluşturun ve ilgili yöntemlerle doldurun.

class Test_jinja_object:

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

    def clever_function (self):
        return 'hello' 

Ardından, görünüm işlevinizde sınıfınızın bir örneğini oluşturun ve sonuçtaki nesneyi render_template işlevi için bir parametre olarak şablonunuza geçirin

my_obj = Test_jinja_object()

Şimdi şablonunuzda, jinja'daki sınıf yöntemlerini şöyle çağırabilirsiniz

{{ my_obj.clever_function () }}

Eşdeğer ve biraz daha basit bir yol: şablonlar için tüm işlevleri bir modüle koyun, o modülü alın ve şablon global olarak ekleyin. Bir modül, işlevleri içeren bir nesnedir :) (ancak yöntem değil - kendi kendine parazite gerek yoktur ve sınıf gerektirmez!)
Éric Araujo

@ ÉricAraujo Ya sadece bir ya da iki şablonda fonksiyon setine ihtiyacım olursa ve hepsine değil. Ayrıca, farklı jinjas şablonlarında farklı python işlevlerine ihtiyacım olursa ne olur? Hepsini bir sınıfa yöntem olarak yerleştirmek yerine yalnızca şablon globalleri olarak içe aktarmanın etkili olduğunu düşünüyor musunuz ve yalnızca sınıfları ihtiyacınız olan yöntemlerle geçiriyor musunuz?
Kudehinbu Oluwaponle

Yalnızca belirli şablonlarda kullanmak için, işlevleri (veya işlevleri içeren bir modülü) yalnızca bu şablonları kullanan görünümler tarafından döndürülen şablon bağlamı diksiyonuna eklerdim.
Éric Araujo

3

Tüm yerleşik işlevleri içe aktarmak için şunları kullanabilirsiniz:

app.jinja_env.globals.update(__builtins__)

Bu işe yaramazsa .__dict__sonra ekleyin __builtins__.

John32323'ün cevabına dayanarak .


2

Django ile yapıyorsanız, işlevi bağlamla geçirebilirsiniz:

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

Artık strjinja2 şablonundaki fonksiyonu kullanabileceksiniz


1

Çok daha basit bir karar var.

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

def y(text):
    return text

Ardından, test.html dosyasında :

{{ y('hi') }}

jinja2.exceptions.UndefinedError: 'y' undefined
lww

evet, çünkü test.html foo kullanmak gerekiyordu
luckyguy73
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.