Jinja宏如何接收额外参数(*args和**kwargs)?(
转载一篇文章,原文链接:
如何向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元组。一般情况下,你并不需要担心这个问题。