Jinja 官方文档
Template Designer Documentation
简介 Jinja template 只是一个普通文件, 不需要有 specific extension. 但用 .jinja
可能会便于编辑器的 plugin 识别. (一般把所有 template 文件放在 templates
目录下也便于识别)
Template 中的 variables, expressions 以及 tags 会在模板引擎渲染时替换. 其语法倾向于 Django 和 Python.
Jinja2 API Environment 对象 Environment 是一个核心对象, 其包含了加载, 编译和渲染模板所需的所有配置和信息. 其用作 Jinja2 模板引擎的主要入口点, 用于管理模板文件的加载, 模板渲染的设置和上下文.
可以用 Environment 对象来配置 Jinja2 的各种选项, 如板的搜索路径, 自定义过滤器和全局变量等.
如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from jinja2 import Environment, FileSystemLoader env = Environment( loader=FileSystemLoader('templates' ), trim_blocks=True , lstrip_blocks=True , autoescape=True , ) template = env.get_template('template.html' ) rendered_template = template.render(name='Alice' )print (rendered_template)
一般一个配置就创建一个 Environment
.
FileSystemLoader 加载器类 FileSystemLoader 是 Jinja2 中的加载器之一 (一个类), 用于从文件系统加载模板文件. 它提供了一种从指定目录中加载模板文件的方式.
1 2 3 4 5 6 7 from jinja2 import Environment, FileSystemLoader env = Environment(loader=FileSystemLoader('/path/to/templates' )) template = env.get_template('template.html' )
Template 对象 Template 可用于 rendered. Environment 就是为编译出一个 Template 而进行的设置. (也可以直接创建一个 template 不通过 Environment)
如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from jinja2 import Template template_source = """ Hello, {{ name }}! Additional content. """ template = Template( template_source, trim_blocks=True ) rendered_template = template.render(name='Alice' )print (rendered_template)
基本语法 三种类型的 delimiters {% ... %}, for Statements
{{ ... }}, for Expressions
, for Comments
Variables 即在 template 中展开变量为 python 脚本的传入值, 如:
1 2 3 {{ foo }} {{ foo.bar }} {{ foo ['bar'] }}
Filters Filters 用于修改变量的值. 多个 filter 用 |
分割, 排在 variable 之后, 且用 ()
传递参数, 如;
1 {{ name|striptags |title }}
即对 name
变量应用 striptags
和 title
两个 filter.
传入参数如:
用 ,
来连接传入的 list.
更多可用的 builtin filter 可查看 文档 .
Tests 就是变种 if
语句, 用内置的一些比较方法来测试变量值, 如:
1 2 {% if loop.index is divisibleby 3 %} {% if loop.index is divisibleby(3) %}
这里 if
和 is
是关键字, loop.index
是变量, divisibleby
是内置的方法.
可用的 Test 也可以查看 官方文档 .
语法为:
如:
1 2 3 4 5 {# note: commented-out template because we no longer use this {% for user in users %} ... {% endfor %} #}
Whitespace Control Jinja2 可以启用 trim_blocks
和 lstrip_blocks
选项, 前者会移除 a template tag 之后的第一个 newline, 后者会移除 block tag 前的 tabs 和 spaces.
1 2 3 4 5 6 from jinja2 import Environment, FileSystemLoader env = Environment(loader=FileSystemLoader("templates/" ), lstrip_blocks=True , trim_blocks=True ) template = env.get_template("hello.html" )print (template.render())
示例如:
1 2 3 4 5 <div > {% if True %} yay {% endif %}</div >
在渲染之后, 会变成:
而不是:
可以在 block 之前放置 +
来禁用 lstrip_blocks
选项, 如:
1 2 3 <div > {%+ if something %}yay{% endif %}</div >
可以在 block 后放置 +
来禁用 trim_blocks
选项, 如:
1 2 3 4 5 <div > {% if something +%} yay {% endif %}</div >
(还有一些功能这里没记录, 可见文档)
Tags 为 {%` 和 `%}
包裹的部分, 作用类似于 python 中的关键词 (做一些逻辑控制, 如 if
, for
等).
Conditional 语法为:
1 {% if ... %} ... {% else %} ... {% endif %}
如:
1 2 3 4 5 6 7 8 9 10 11 12 13 {# tamplates/all.txt #} Hello {{ name }} , {% if score > 80 %} I'm happy to inform you that you did vary well on today's {{ test_name }} . {% else %} Your score on today's test {{ test_name }} could have been better. More studying, less vampire hunting! {% endif %} You achieved {{ score }} out of {{ max_score }} points! See you tomorrow, Ms. Calendar
Loops 语法为:
1 {% for ... %} ... {% endfor %}
如:
1 2 3 4 5 6 <dl > {% for key, value in my_dict.items() %} <dt > {{ key|e }} </dt > <dd > {{ value|e }} </dd > {% endfor %} </dl >
Macros Macros 相当于是可复用的 functions, 示例:
1 2 3 4 {% macro input(name, value='', type='text', size=20) -%} <input type =" {{ type }} " name =" {{ name }} " value =" {{ value |e }} " size =" {{ size }} " >{%- endmacro %}
使用如:
1 2 <p > {{ input ('username' ) }} </p > <p > {{ input ('password' , type ='password' ) }} </p >
在 macros 中可以访问三个特殊变量:
varargs
, “variable arguments”, 如果向 macro 传递的参数数量超过其接收的量, 多出的就存在这个变量中
kwargs
, “keyword arguments”, 未被使用的参数存储在这里
caller
, 如果 macro 由 call
tag 调用, 则 caller 会存储在这里
如果一个 macro name 以下划线开头, 则其无法被导出.
Call Call 用于实现传递一个 macro 给另一个 macro (call 就像一个没有名字的 macro, 可供其内部的 macro 使用), 如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 {% macro dump_users(users) -%} <ul > {%- for user in users %} <li > <p > {{ user.username|e }} </p > {{ caller(user) }} </li > {%- endfor %} </ul > {%- endmacro %} {% call (user) dump_users(list_of_user) %} <dl > <dt > Realname</dt > <dd > {{ user.realname|e }} </dd > <dt > Description</dt > <dd > {{ user.description }} </dd > </dl > {% endcall %}
Filters Filter section 相当于用指定的 filter 处理 block 中的内容:
1 2 3 {% filter upper %} This text becomes uppercase {% endfilter %}
这里将 block 内的内容转为大写.
Assignments 用于创建变量来使用:
1 2 {% set navigation {% set
但要注意, 这些变量在其他 block 中无法被使用.
Block Assignments 如:
1 2 3 4 {% set navigation %} <li > <a href ="/" > Index</a > <li > <a href ="/downloads" > Downloads</a > {% endset %}
将 block 内的内容赋值给 navigation
.
Include 用于渲染 another template 并输出到当前 template:
1 2 3 {% include 'header.html' %} Body goes here. {% include 'footer.html' %}
Import 用于引入其他 tamplates 中的 macros, 与 python 中 import
语句的语法一致.
假设一个 template 文件内容为:
1 2 3 4 5 6 7 8 {% macro input(name, value='', type='text') -%} <input type =" {{ type }} " value =" {{ value |e }} " name =" {{ name }} " >{%- endmacro %} {%- macro textarea(name, value='', rows=10, cols=40) -%} <textarea name =" {{ name }} " rows =" {{ rows }} " cols =" {{ cols }} " >{{ value |e }} </textarea > {%- endmacro %}
(这里定义了 input
和 textarea
两个 macros)
使用:
1 2 3 4 5 6 7 8 {% import 'forms.html' as forms %} <dl > <dt > Username</dt > <dd > {{ forms.input ('username' ) }} </dd > <dt > Password</dt > <dd > {{ forms.input ('password' , type ='password' ) }} </dd > </dl > <p > {{ forms.textarea ('comment' ) }} </p >
或者只导入一个 macro:
1 2 3 4 5 6 7 8 {% from 'forms.html' import input as input_field, textarea %} <dl > <dt > Username</dt > <dd > {{ input_field ('username' ) }} </dd > <dt > Password</dt > <dd > {{ input_field ('password' , type ='password' ) }} </dd > </dl > <p > {{ textarea ('comment' ) }} </p >
Import Context Behavior include
和 import
tag 对导入 template 的 context 的处理不同, 前者默认传递 context, 而后者默认不传递. 这些可通过添加 with context
或 without context
选项来改变:
1 2 {% from 'forms.html' import input with context %} {% include 'header.html' without context %}
Escaping 使用:
1 {% raw %} ... {% endrow %}
block 可以输出 raw 文本, 即转义其中的所有特殊字符.
1 2 3 4 5 6 7 {% raw %} <ul> {% for item in seq %} <li>{{ item }}</li> {% endfor %} </ul> {% endraw %}
Line Statements 可以不使用类似 {% %}
来包裹 statement, 而是定义一个 “line statement prefix” (这里假设为 #
) 下面两个语句等价:
1 2 3 4 5 6 7 8 9 10 11 <ul > # for item in seq <li > {{ item }}</li > # endfor</ul > <ul > {% for item in seq %} <li > {{ item }}</li > {% endfor %}</ul >
为了提高可读性, 可以在 block 头 (如 for
, if
, elif
) 的末尾加上冒号, 如:
1 2 3 # for item in seq: ... # endfor
这种语法也能跨多行写:
1 2 3 4 5 6 <ul > # for href, caption in [('index.html', 'Index'), ('about.html', 'About')]: <li > <a href ="{{ href }}" > {{ caption }}</a > </li > # endfor</ul >
(这里并没有用类似 \
之类的符号表示跨行)
除 statement 之外, 可定义 “line-comment prefix” (这里假设为 ##
) 来更改注释的写法:
1 2 3 # for item in seq: <li > {{ item }}</li > ## this comment is ignored # endfor
示例 1 2 3 4 5 6 7 8 9 10 11 from jinja2 import Environment, FileSystemLoader env = Environment( loader=FileSystemLoader("templates/" ), lstrip_blocks=True , trim_blocks=True , line_statement_prefix="#" , line_comment_prefix="##" ) template = env.get_template("hello.html" )print (template.render())
hello.html
的内容为:
1 2 3 4 5 <div > # if True yay ## hahaha # endif</div >
输出为:
Template Inheritance Template Inheritance 允许创建一个 base template (父模板), 其中包含通用的结构和布局, 然后在基础模板的基础上创建子模板, 继承父模板的结构, 并可以覆盖或扩展特定部分.
Base Template base.html
文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <!DOCTYPE html > <html lang ="en" > <head > {% block head %} <link rel ="stylesheet" href ="style.css" /> <title > {% block title %}{% endblock %} - My Webpage</title > {% endblock %}</head > <body > <div id ="content" > {% block content %}{% endblock %}</div > <div id ="footer" > {% block footer %} © Copyright 2008 by <a href ="http://domain.invalid/" > you</a > . {% endblock %} </div > </body > </html >
注意这里以 {% block %}
tag 定义的部分都是等待 child template 来 override 的部分.
{% block %}
tag 可以内嵌于其他 tag 中.
Child Template 如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 {% extends "base.html" %} {% block title %}Index{% endblock %} {% block head %} {{ super() }} <style type ="text/css" > .important { color : #336699 ; } </style > {% endblock %} {% block content %} <h1 > Index</h1 > <p class ="important" > Welcome to my awesome homepage. </p > {% endblock %}
{% extends %}
tag 指明 base template (也就是 parent), 即以哪一个 template 来扩充. 注意这里指定的文件名还是和 FileSystemLoader
设置的路径相关.
{% extends %}
之前的部分可以正常编写.
注意这里 child template 只是定义了 base template 中等待被 fill in 的部分, 若未被定义则使用 base template 中的设置.
若想重复使用一个 block 名称, 可以借助 self
变量写为:
1 2 3 <title > {% block title %}{% endblock %}</title > <h1 > {{ self.title() }}</h1 > {% block body %}{% endblock %}
Super Blocks 调用 super()
来获取渲染后的 parent block.
1 2 3 4 5 {% block sidebar %} <h3 > Table Of Contents</h3 > ... {{ super() }} {% endblock %}
Nesting extends 在嵌套使用 {% extends %}
之后, 可以用 super.super()
的方式嵌套调用 super
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # parent.tmpl body: {% block body %}Hi from parent.{% endblock %} # child.tmpl {% extends "parent.tmpl" %} {% block body %}Hi from child. {{ super() }}{% endblock %} # grandchild1.tmpl {% extends "child.tmpl" %} {% block body %}Hi from grandchild1.{% endblock %} # grandchild2.tmpl {% extends "child.tmpl" %} {% block body %}Hi from grandchild2. {{ super.super() }} {% endblock %}
可以把 block name 也放在 end tag 来提高可读性:
1 2 3 4 5 {% block sidebar %} {% block inner_sidebar %} ... {% endblock inner_sidebar %} {% endblock sidebar %}
(前提是两个名称得写一样)
Block Nesting and Scope 默认情况下, block 内不能访问外部的变量, 如:
1 2 3 {% for item in seq %} <li > {% block loop_item %}{{ item }}{% endblock %}</li > {% endfor %}
这里最终输出 empty <li>
, 因为 block 无法访问到 item
.
可以通过添加 scoped
现象来使其可以访问:
1 2 3 {% for item in seq %} <li > {% block loop_item scoped %}{{ item }}{% endblock %}</li > {% endfor %}
Required Blocks 具体见文档. (感觉用不上)
Template Object 具体见文档. (感觉用不上)
示例 python interactive 模式示例 1 2 3 4 5 >>> import jinja2 >>> env = jinja2.Environment() >>> template = env.from_string("Hello {{ name }}" ) >>> template.render(name="World" )'Hello World!'
从一个文件中加载模板并使用基本 variable replacement 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 from jinja2 import Environment, FileSystemLoader MAX_SCORE = 100 TEST_NAME = "Python Challenge" students = [ {"name" :"Wiliow" , "score" :100 }, {"name" :"Cordelia" , "score" :85 }, {"name" :"Oz" , "score" :95 }, ] env = Environment(loader=FileSystemLoader("templates/" )) template = env.get_template("good.txt" )for student in students: name = student["name" ] filename = f"output/message_{name.lower()} .txt" content = template.render( student, max_score=MAX_SCORE, test_name=TEST_NAME ) with open (filename, mode="w" , encoding="utf-8" ) as output: output.write(content) print ("... wrote" , filename)print ("Finish..." )
good.txt
的内容为:
1 2 3 4 5 6 7 8 9 {# templates/good.txt #} Hello {{ name }} , I'm happy to inform you that you did very well on today's {{ test_name }} . You achived {{ score }} out of {{ max_score }} points! See you tomorrow, Ms. Calendar
Flask 中的应用 一个简单的 flask 程序如:
1 2 3 4 5 6 7 8 9 10 11 import flask app = flask.Flask(__name__)@app.route("/hello/" ) def hello (): return "Hello World!" if __name__ = "__main__" : app.run(debug=True )
结合 jinja2 使用:
1 2 3 4 5 6 7 8 import flask app = flask.Flask(__name__)@app.route("/" ) def home (): return flask.render_template("base.html" , title="Jenny's Server" )
而 base.html
的内容为:
1 2 3 4 5 6 7 8 9 10 11 12 13 {# templates/base.html #}<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="utf-8" > <title > {{ title }}</title > </head > <body > {% block content %} <h1 > Welcome to {{ title }}!</h1 > {% endblock content %}</body > </html >