Jinja 继承、函数和块
最近在为 mkdocs-blogging-plugin 添加自定义的功能,因为本来就是用 Jinja 的 HTML 模版来生成 HTML,所以也想用 Jinja 给用户提供自定义的接口。
大概的要求有:
- 提供自定义单个 post 而保留大体框架的方法
- 提供自定义 style sheet 的方法
- 提供整体重写的方法
这篇文章只会覆盖到部分。
继承:extends
⌗
Jinja 给我们提供了一种类似于继承的方法,可以在保留原来模版所有内容的情况下增加、重写部分内容的方法。
在“子模版”中,通过 extends
引用“父模版”:
{% extends "blog.html" %}
如果只有这一句,相当于将原来 blog.html
中所有内容原封不动地搬到新的模版中。至于增加和重写内容,稍后再谈。
函数:macro
⌗
在用户提供的覆写模版中,我们需要用户向父模版提供一个“函数”,用来生成单个博客的内容。
Jinja 中的“函数”叫做 macro
,通过这样的方法定义:
{% macro render_blog(title, description, time, url) -%}
{# content #}
{%- endmacro %}
以及,这样调用:
{% call render_blog(title, description, time, url) %}
{# caller_content #}
{% endcall %}
在 call
调用 macro
后,将整个 call
块 替换为 macro
块里面的所有内容 {# content #}
,与 C 中的宏定义一致(这应该也是为什么叫做 macro
的原因)。
另外,macro
有一个隐含参数 caller
,其作用是返回 call
块中间的内容 {# caller_content #}
,如:
macro
:
{% macro foo(name) -%}
<div>{{ name + " says: " + caller() }}</div>
{%- endmacro %}
call
:
{% call foo("liang2kl") %}
Hello, World!
{% endcall %}
call
的结果为:
<div>liang2kl says: Hello, World!</div>
值得注意的是,如果不想传递
caller
中的内容,也需要在macro
中调用caller()
,否则会报错:TypeError: macro ‘foo’ was invoked with two values for the special caller argument. This is most likely a bug.
现在,我们只需要用户提供一个产生博客内容的 macro 给我们的父模版就可以了。
重写:block
⌗
Jinja 的继承机制允许我们在“子模版”中选择性地重写部分内容,而保持其他部分不变。实现的机制是 block
。
在父模版中,我们用 block
块划出允许重写的部分。如:
{% block style %}
<style>
...
</style>
{% endblock %}
在子模版中,重写:
{% block style %}
...
{% endblock %}
即可将父模版中的 style
块替换为新的块。
另外,可以用 super()
保留原来内容:
{% block style %}
{{ super() }}
...
{% endblock %}
放在一起⌗
将上面几个功能放在一起,我们给用户提供这样的自定义方式:
{% macro render_blog(title, description, time, url) -%}
<a href="{{ url }}"><h3>{{ title }}</h3></a>
<div>{{ description }}</div>
<div>{{ time }}</div>
<hr/>
{{ caller() }}
{%- endmacro %}
{% extends "blog.html" %}
{% block style %}
.md-typeset .blog-post-title {
color: blue;
}
{% endblock %}
其作用:
- 在引入
blog.html
之前,定义render_blog
的 macro,以便在blog.html
中调用 - 引入
blog.html
- 通过
block
修改blog.html
的style
块