如何向Jinja巨集傳遞額外引數(*args和**kwargs)?
這段時間有多個讀者問關於Jinja巨集定義時的引數處理問題。這一點在《Flask Web開發實戰》裡沒有介紹,這篇文章作為一個補充。
一個不符合直覺的設定
在某個晴朗的早晨,你開啟電腦,想在你的專案Jinja模板裡編寫一個巨集來簡化操作。按照直覺,你可能會像定義Python函式那樣來定義巨集,傳入**kwargs來讓它接收任意數量的關鍵字引數,比如:
{% macro say_hello(**kwargs) %} ... {% endmacro %}
或是傳入*args讓它接收任意數量的位置引數:
{% macro say_hello(*args) %} ... {% endmacro %}
遺憾的是,上面的用法會分別獲得下面的錯誤資訊:
jinja2.exceptions.TemplateSyntaxError: expected token 'name', got '**' jinja2.exceptions.TemplateSyntaxError: expected token 'name', got '*'
在Jinja巨集裡接收額外的關鍵字引數和位置引數
在Jinja中,巨集預設會自動接收額外的關鍵字引數和位置引數,並在巨集內部提供kwargs 和varargs 特殊變數來獲取它們。具體來說,在定義巨集的時候,不需要進行任何宣告。在巨集的內部,你可以直接使用kwargs字典來獲取額外的關鍵字引數;同樣的,你可以使用varargs元組來獲取額外傳入的位置引數。
下面是使用kwargs的示例:
{% macro say_hello() %} <p>Hello, {{ kwargs['name'] }}!</p> {% endmacro %} {# 呼叫示例 #} {{ say_hello(name='Grey')}}
你可以把這個字典傳遞給其他函式,比如url_for():
{% macro nav_link(endpoint, text) %} <a href="{{ url_for(endpoint, **kwargs) }}">{{ text }}</a> {% endmacro %} {# 呼叫示例 #} {{ nav_link('index', 'Home', foo='value1', bar='value2')}}
下面是使用varargs的示例:
{% macro say_hello() %} <p>Hello, {{ varargs[0] }}!</p> {% endmacro %} {# 呼叫示例 #} {{ say_hello('Grey')}}
提示 在巨集內部,如果kwargs字典裡沒有對應的鍵,那麼會返回空字串,而不是丟擲KeyError異常;如果向varargs元組索引一個超出範圍的下標值,也會返回空值,而不會丟擲IndexError異常。
隱藏的陷阱
雖然巨集自動處理額外傳入的關鍵字引數和位置引數,但是這裡有一個隱藏的小陷阱。如果你在呼叫一個巨集的時候傳入了額外的關鍵字引數和位置引數,但是巨集的內部並沒有使用它們,這時就會出錯。比如下面使用kwargs的示例:
{% macro say_hello() %} <p>Hello!</p> {% endmacro %} {# 呼叫示例 #} {{ say_hello(name='Grey')}}
呼叫巨集的時候傳入了name引數,但是巨集內部並沒有使用它,這時Jinja會丟擲下面的異常:
TypeError: macro 'say_hello' takes no keyword argument 'name'
類似的是位置引數,你會獲得下面的異常:
TypeError: macro 'say_hello' takes not more than 1 argument(s)
所以,如果你想讓某個巨集接收額外的關鍵字引數或位置引數,你就分別需要在這個巨集內部至少呼叫一次(access)kwargs字典或是varargs元組。一般情況下,你並不需要擔心這個問題。
本文隸屬於ofollow,noindex" target="_blank">《Flask Web開發實戰》番外文章 系列。