Gọi một hàm python từ jinja2


144

Tôi đang sử dụng jinja2 và tôi muốn gọi một hàm python là một trình trợ giúp, sử dụng một cú pháp tương tự như thể tôi đang gọi một macro. jinja2 dường như có ý định ngăn tôi thực hiện cuộc gọi hàm và khăng khăng tôi lặp lại chính mình bằng cách sao chép hàm vào một mẫu dưới dạng macro.

Có cách nào đơn giản để làm điều này? Và, có cách nào để nhập toàn bộ tập hợp các hàm python và có thể truy cập chúng từ jinja2, mà không cần thông qua rất nhiều Rigamarole (chẳng hạn như viết một phần mở rộng) không?

Câu trả lời:


223

Đối với những người sử dụng Flask, hãy đặt cái này vào __init__.py:

def clever_function():
    return u'HELLO'

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

và trong mẫu của bạn gọi nó với {{ clever_function() }}


bạn có thể vượt qua nhiều chức năng như thế này?
ffghfgh

6
Trong các phiên bản mới hơn (tôi đang sử dụng Jinja2 2.9.6) có vẻ như hoạt động dễ dàng hơn nhiều. Sử dụng chức năng như bạn sẽ sử dụng một biến (cũng hoạt động trong các tình huống phức tạp hơn):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
Thậm chí 8 năm sau, nếu bạn đang sử dụng Flask, đây có vẻ là một giải pháp sạch hơn bất kỳ câu trả lời nào gần đây. Và để trả lời câu hỏi cũ từ @ffghfgh, vâng, bạn có thể vượt qua nhiều chức năng.
kevinmicke

132

Lưu ý: Đây là Flask cụ thể!

Tôi biết bài đăng này khá cũ, nhưng có nhiều phương pháp tốt hơn để làm điều này trong các phiên bản mới hơn của Flask sử dụng bộ xử lý bối cảnh.

Các biến có thể dễ dàng được tạo:

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

Ở trên có thể được sử dụng trong mẫu Jinja2 với Flask như vậy:

{{ myexample }}

(Đầu ra nào This is an example)

Cũng như các chức năng chính thức đầy đủ:

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

Ở trên khi sử dụng như vậy:

{{ format_price(0.33) }}

(Điều này đưa ra giá đầu vào với ký hiệu tiền tệ)

Ngoài ra, bạn có thể sử dụng bộ lọc jinja , nướng vào Flask. Ví dụ: sử dụng trang trí:

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

Hoặc, không có trang trí và đăng ký chức năng theo cách thủ công:

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

Các bộ lọc được áp dụng với hai phương pháp trên có thể được sử dụng như thế này:

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

4
Những chức năng này nên tồn tại ở đâu, init, view hay bất cứ nơi nào?
knk

3
__init__.pygiả sử bạn tuyên bố flask.Flask(__name__)ở đó.
Liam Stanley

6
Down đã bình chọn khi câu hỏi về Jinja2 và câu trả lời là Flask cụ thể.
AJP

13
@AJP Vẫn theo lý thuyết trả lời câu hỏi. Đây là MỘT cách để giải quyết vấn đề, với điều kiện bạn cũng đang sử dụng Flask. Một chút giống như tất cả các câu hỏi JavaScript thường trả lời đưa ra các lựa chọn thay thế có hoặc không có jQuery hoặc các câu hỏi về Python thường trả lời cả cho Python2 và 3. Câu hỏi không loại trừ Flask. (không giống như câu hỏi về Py2 sẽ loại trừ câu trả lời Py3). Câu trả lời này đã giúp tôi.
jeromej

2
Rất hữu ích, và chỉ những gì tôi đang tìm kiếm. Jinja2 là một phần của khung web và do đó không hoàn toàn độc lập với back-end. Tôi làm việc trong cả Django và Flask với Python và bài đăng này, cũng như những người khác ở đây có liên quan đến tôi. Cố gắng vượt quá chỉ định một câu hỏi có hại theo quan điểm của tôi là mơ hồ không cần thiết.

76

Tôi nghĩ rằng jinja cố tình gây khó khăn cho việc chạy trăn 'tùy ý' trong một mẫu. Nó cố gắng thực thi ý kiến ​​rằng ít logic trong các mẫu là một điều tốt.

Bạn có thể thao tác không gian tên toàn cục trong một Environmentthể hiện để thêm các tham chiếu đến các hàm của bạn. Nó phải được thực hiện trước khi bạn tải bất kỳ mẫu nào. Ví dụ:

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
Tôi cũng phát hiện ra điều này - bạn có thể thêm một mô-đun bằng cách sử dụng một cái gì đó như thế này: import utils.helpers env.globals['helpers'] = utils.helpers
Lee

@Lee. Có, bạn có thể 'tiêm' không gian tên (mô-đun), hàm, thể hiện lớp, v.v ... Nó rất hữu ích, nhưng không linh hoạt như các công cụ mẫu khác như mako. Tuy nhiên, jinja có những điểm tốt khác. Tôi sẽ biết ơn nếu bạn chấp nhận câu trả lời nếu nó giúp :)
Rob Cowie

đã lừa tôi khi thực hiện dự án công cụ ứng dụng của tôi (webapp2 và jinja2). cảm ơn
Sojan V Jose

@RobCowie sau khi thêm smart_function vào từ điển env.globals, làm thế nào người ta có thể gọi hàm từ mẫu.
Jorge Vidinha

1
Do đó, {{ 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

Bạn cũng có thể cung cấp hàm trong các trường theo câu trả lời của Matroskin

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

Sẽ xuất:

Hey, my name is Jay Kay

Hoạt động với Jinja2 phiên bản 2.7.3

Và nếu bạn muốn một người trang trí dễ dàng xác định các chức năng, template.globalshãy xem câu trả lời của Bruno Bronosky


8
Có lẽ bởi vì bạn đã đánh giá thấp câu trả lời của mọi người khác :(
Borko Kovacev

13
@BorkoKovacev đó không phải là lý do chính đáng. Tôi chỉ bỏ phiếu 2 câu trả lời; câu trả lời là về Flask chứ không phải Jinja2. Nếu họ muốn chỉnh sửa câu trả lời của họ theo chủ đề và về Jinja2, tôi sẽ bỏ phiếu cho họ.
AJP

Tx @BorkoKovacev :)
AJP

1
Tôi đã thực hiện một phiên bản trang trí chức năng của câu trả lời này. Nó hiện đang ở dưới cùng với 0 phiếu :, - ( stackoverflow.com/a/47291097/117471
Bruno Bronosky

2
@BrunoBronosky đẹp. Đã bình chọn :) ... cho nó một thập kỷ nữa và nó có thể cao hơn của tôi: P ... sẽ không bao giờ bắt được bình; P
AJP

25

Tôi thích câu trả lời của @ AJP . Tôi đã sử dụng nó nguyên văn cho đến khi tôi kết thúc với rất nhiều chức năng. Sau đó, tôi chuyển sang một trang trí chức năng 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))

Chức năng điều tốt có a __name__!


1
Điều này là cực kỳ mát mẻ. Khi bạn chú thích một hàm trong python, nó có tự động chuyển tên hàm cho hàm của chú thích không?
mutant_city

@mutant_city, vâng. Đọc rằng liên kết trang trí chức năng Python. Công cụ tuyệt vời!
Bruno Bronosky

1
@BrunoBronosky Trình diễn tuyệt vời về cách sử dụng hợp lý và sạch sẽ cho các nhà trang trí trăn là tốt. Bài đăng tuyệt vời!
dreftymac

1
Thật là một triển khai tuyệt vời!
Philippe Oger

16

Chưa bao giờ thấy cách đơn giản như vậy tại các tài liệu chính thức hoặc tại stack stack, nhưng tôi đã rất ngạc nhiên khi tìm thấy điều này:

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

Câu trả lời này là imho tốt nhất. Bạn chỉ cần truyền hàm cho mẫu theo cách chính xác giống như cách bạn chuyển một giá trị, sau khi tất cả các hàm là công dân hạng nhất trong python :)
Mark Kortink

8

Sử dụng lambda để kết nối mẫu với mã chính của bạn

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

Sau đó, bạn có thể gọi liền mạch hàm trong mẫu

{{clever_function(value)}}

1
Sử dụng thông minh các chức năng lambda.

23
@odiumediae: Không, không phải vậy. Nó hoàn toàn không cần thiết. Chỉ cần vượt qua chức năng tự xử lý: notify_function = smart_function
vezult

@vezult Tôi thấy. Làm thế nào tôi có thể bỏ lỡ điều đó? Cám ơn giải thích rõ ràng!

6

Để gọi hàm python từ Jinja2, bạn có thể sử dụng các bộ lọc tùy chỉnh hoạt động tương tự như toàn cầu: http://jinja.pocoo.org/docs/dev/api/#wr-filters

Nó khá đơn giản và hữu ích. Trong một tập tin myTemplate.txt, tôi đã viết:

{{ data|pythonFct }}

Và trong một kịch bản 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

Có cách nào để nhập toàn bộ tập hợp các hàm python và có thể truy cập chúng từ jinja2 không?

Có, ngoài các câu trả lời khác ở trên, điều này làm việc cho tôi.

Tạo một lớp và điền vào nó với các phương thức liên quan, vd

class Test_jinja_object:

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

    def clever_function (self):
        return 'hello' 

Sau đó, tạo một thể hiện của lớp trong hàm xem của bạn và truyền đối tượng kết quả vào mẫu của bạn làm tham số cho hàm render_template

my_obj = Test_jinja_object()

Bây giờ trong mẫu của bạn, bạn có thể gọi các phương thức lớp trong jinja như vậy

{{ my_obj.clever_function () }}

Cách tương đương và đơn giản hơn một chút: đặt tất cả các chức năng cho các mẫu trong một mô-đun, nhập mô-đun đó và thêm nó làm mẫu toàn cầu. Một mô-đun là một đối tượng có chứa các hàm :) (nhưng không phải là các phương thức - không cần tự param và không yêu cầu lớp nào!)
Éric Araujo

@ ÉricAraujo Điều gì xảy ra nếu tôi chỉ cần tập hợp các hàm trong một hoặc hai mẫu chứ không phải tất cả chúng. Ngoài ra, nếu tôi cần các bộ hàm python khác nhau trong các mẫu jinjas khác nhau thì sao? Bạn vẫn sẽ xem nó có hiệu quả không khi nhập tất cả chúng dưới dạng toàn cục khuôn mẫu thay vì đặt chúng làm phương thức trong một lớp và chỉ truyền các lớp với các phương thức bạn cần.
Kudehinbu Oluwaponle

Để chỉ sử dụng trong các mẫu cụ thể, tôi sẽ chỉ thêm các hàm (hoặc mô-đun chứa các hàm) vào ngữ cảnh mẫu mà chính tả được xem lại bởi các chế độ xem sử dụng các mẫu đó.
Éric Araujo

3

Để nhập tất cả các hàm dựng sẵn, bạn có thể sử dụng:

app.jinja_env.globals.update(__builtins__)

Thêm .__dict__sau __builtins__nếu điều này không làm việc.

Dựa trên câu trả lời của John32323 .


2

Nếu bạn đang làm điều đó với Django, bạn có thể chuyển hàm với ngữ cảnh:

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

Bây giờ bạn sẽ có thể sử dụng strchức năng trong mẫu jinja2


1

Có một quyết định đơn giản hơn nhiều.

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

def y(text):
    return text

Sau đó, trong test.html :

{{ y('hi') }}

jinja2.exceptions.Und xác địnhError: 'y' không xác định
lww

vâng, vì tôi nên sử dụng foo trong test.html
luckyguy73
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.