เรียกใช้ฟังก์ชันหลามจาก jinja2


144

ฉันใช้ jinja2 และฉันต้องการเรียกใช้ฟังก์ชันหลามเป็นผู้ช่วยโดยใช้ไวยากรณ์ที่คล้ายกันราวกับว่าฉันกำลังเรียกแมโคร ดูเหมือนว่า jinja2 จะป้องกันไม่ให้ฉันเรียกใช้ฟังก์ชันและยืนยันว่าฉันทำซ้ำตัวเองด้วยการคัดลอกฟังก์ชันลงในเทมเพลตเป็นมาโคร

มีวิธีที่ตรงไปตรงมาในการทำเช่นนี้? และมีวิธีใดที่จะนำเข้าชุดฟังก์ชั่นหลามทั้งชุดและให้พวกเขาสามารถเข้าถึงได้จาก jinja2 โดยไม่ต้องผ่าน rigamarole จำนวนมากทั้งหมด (เช่นเขียนส่วนขยาย)

คำตอบ:


223

สำหรับผู้ที่ใช้ Flask ให้ใส่สิ่งนี้ลงใน__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 ที่มี Flask ดังนี้:

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

(ซึ่งแสดงราคาอินพุตด้วยสัญลักษณ์สกุลเงิน)

อีกทางหนึ่งคุณสามารถใช้ตัวกรอง jinjaอบเข้า Flask เช่นใช้มัณฑนากร:

@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
ฟังก์ชั่นเหล่านี้ควรอยู่ที่ไหนเริ่มต้นดูหรือที่ไหนก็ได้?
knk

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 จงใจทำให้เป็นการยากที่จะเรียกใช้ python 'โดยพลการ' ภายในเทมเพลต มันพยายามที่จะบังคับใช้ความเห็นว่าตรรกะน้อยลงในแม่แบบเป็นสิ่งที่ดี

คุณสามารถจัดการเนมสเปซส่วนกลางภายใน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 ถึงกระนั้นจินจาก็มีคะแนนที่ดีอื่น ๆ ฉันจะขอบคุณถ้าคุณยอมรับคำตอบถ้ามันช่วย :)
24411 Rob

ทำเคล็ดลับสำหรับฉันในขณะที่ทำโปรเจ็กต์แอพของฉัน (webapp2 และ jinja2) ขอบคุณ
Sojan V Jose

@RobCowie หลังจากเพิ่ม clever_function ลงในพจนานุกรม env.globals เราจะเรียกใช้ฟังก์ชันจากเทมเพลตได้อย่างไร
Jorge Vidinha

1
ดังนั้น {{ 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

นอกจากนี้คุณยังสามารถให้ฟังก์ชันในฟิลด์ตามคำตอบของ Matroskin

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ตรวจสอบคำตอบของ Bruno Bronosky


8
อาจเป็นเพราะคุณ
ปฏิเสธ

13
@BorkoKovacev นั่นไม่ใช่เหตุผลที่ดี ฉันลงคะแนนเพียง 2 คำตอบ; คำตอบที่เกี่ยวกับ Flask มากกว่า Jinja2 หากพวกเขาต้องการแก้ไขคำตอบของพวกเขาให้อยู่ในหัวข้อและเกี่ยวกับ Jinja2 ฉันจะโหวตให้พวกเขา
AJP

Tx @BorkoKovacev :)
AJP

1
ฉันทำคำตอบนี้ในเวอร์ชันมัณฑนากร ขณะนี้อยู่ที่ด้านล่างด้วย 0 คะแนน:, - ( stackoverflow.com/a/47291097/117471
Bruno Bronosky

2
@BrunoBronosky ดี โหวตได้แล้ว :) ... ให้อีกสิบปีและมันอาจจะสูงกว่าของฉัน: P ... จะไม่จับขวดใส่ขวดเลย P
AJP

25

ผมชอบ@ คำตอบของ ฉันใช้คำต่อคำจนกว่าจะได้ฟังก์ชั่นมากมาย จากนั้นผมก็เปลี่ยนไปฟังก์ชั่นมัณฑนากรหลาม

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
นี่มันเจ๋งมาก เมื่อคุณใส่คำอธิบายประกอบฟังก์ชั่นในหลามมันจะส่งชื่อฟังก์ชั่นไปยังฟังก์ชันของคำอธิบายประกอบโดยอัตโนมัติหรือไม่
mutant_city

@mutant_city, อ๋อ อ่านลิงค์ตัวตกแต่งฟังก์ชั่น Python สิ่งที่ยอดเยี่ยม!
Bruno Bronosky

1
@BrunoBronosky การสาธิตที่ยอดเยี่ยมเกี่ยวกับการใช้ที่เหมาะสมและสะอาดสำหรับนักตกแต่งของงูใหญ่เช่นกัน โพสต์ที่ยอดเยี่ยม!
dreftymac

1
ช่างเป็นอะไรที่ยอดเยี่ยมมาก!
ฟิลิปป์ Oger

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

คำตอบนี้เป็นคำตอบที่ดีที่สุด คุณเพียงแค่ผ่านฟังก์ชั่นกับแม่แบบในตรงทางเดียวกับที่คุณส่งค่าหลังจากการทำงานทั้งหมดเป็นพลเมืองชั้นแรกในหลาม :)
มาร์ค Kortink

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

ในการเรียกใช้ฟังก์ชันหลามจาก Jinja2 คุณสามารถใช้ตัวกรองแบบกำหนดเองซึ่งทำงานเหมือนกับ globals: http://jinja.pocoo.org/docs/dev/api/#writing-filters

มันค่อนข้างง่ายและมีประโยชน์ ในไฟล์ myTemplate.txt ฉันเขียนว่า:

{{ data|pythonFct }}

และในสคริปต์ไพ ธ อน:

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

มีวิธีใดที่จะนำเข้าชุดฟังก์ชั่นหลามทั้งชุดและสามารถเข้าถึงได้จาก 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()

ตอนนี้ในแม่แบบของคุณคุณสามารถเรียกวิธีการเรียนในจินจาอย่างนั้น

{{ my_obj.clever_function () }}

วิธีที่เท่าเทียมกันและเรียบง่ายขึ้นเล็กน้อย: ใส่ฟังก์ชันทั้งหมดสำหรับเทมเพลตในโมดูลนำเข้าโมดูลนั้นและเพิ่มเป็นเทมเพลตส่วนกลาง โมดูลเป็นวัตถุที่มีฟังก์ชั่น :) (แต่ไม่ใช่วิธีการ - ไม่จำเป็นต้องมีพารามิเตอร์ในตัวเองและไม่มีชั้นเรียน reqiured!)
Éric Araujo

@ ÉricAraujoจะทำอย่างไรถ้าฉันต้องการเพียงชุดฟังก์ชั่นในหนึ่งหรือสองแม่แบบและไม่ใช่ทั้งหมด นอกจากนี้ถ้าฉันต้องการชุดฟังก์ชั่นหลามที่แตกต่างกันในเทมเพลต 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: 'y' ไม่ได้กำหนด
lww

ใช่ cuz ควรใช้ foo ใน test.html
luckyguy73
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.