抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

挺久之前氵的笔记

0. jinja2 vs Django template

  • 性能上

    jinja2能够比Django的模版引擎快10到20倍

  • 语法上

    概括来说,可以总结为:Jinja的语法更接近于python的语法,Jinja模版中可以使用某些Python表达式的子集

    例如,拼接列表操作中,可以看出Jinja中过滤器更像是对象的方法的调用:

    Django:

    1
    2
    {{ items|join:", " }}
    ## {{obj|filter_name:param}}

    Jinja:

    1
    2
    {{ items|join(', ') }}
    ## {{obj|filter_name(param)}}

1.简介

Jinja模版与普通的html相比,包含两个特殊的部分:

  • 标签:用于控制模版的逻辑
  • 变量/表达式:用于在模版渲染时替换为值

Jinja中的默认分隔符如下:

  • {% ... %} 用于语句(标签)

  • {{ ... }}用于打印表达式的输出

  • {# ... #}用于注释

  • # … ## 行语句,需要应用程序启用此功能,功能类似于{% ... %},例如,以下两段示例完全等效:

    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>

关于行语句,由于功能完全类似于{% ... %},可以忽略

2.基本语法

2.1 变量

除了普通的字符串变量,Jinja2还支持列表、字典和对象,你可以这样获取变量值:

1
2
3
4
5
6
7
8
{{ mydict['key'] }}
{{ mylist[3] }}
{{ mylist[myintvar] }}
{{ myobj.somemethod() }}

{# 获取变量属性 #}
{{ foo.bar }}
{{ foo['bar'] }}

2.2 变量赋值

1
2
{% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
{% set key, value = call_something() %}

2.3 控制结构

2.3.1 for

使用方法:

1
2
3
4
5
6
<h1>Members</h1>
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>

循环语句中字典可能无序,可使用dictsort过滤器

相比于Python中的for循环,使用Jinja时,在for循环的循环体中可以使用一些特殊的变量:

avatar

2.3.2 if

if的使用十分类似于Python中的if。

1
2
3
4
5
6
7
{% if kenny.sick %}
Kenny is sick.
{% elif kenny.dead %}
You killed Kenny! You bastard!!!
{% else %}
Kenny looks okay --- so far
{% endif %}

2.4 宏

Jinja中的宏(macro)类似于常规编程语言中的宏。宏的存在是为了替换简单的多次重复的代码。

例如,可以首先定义一个宏:

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>

*如果宏是在其他模板中定义的,则必须首先将其[导入](#2.7 import)*。

2.5 块——模板继承

块的作用主要体现在模板继承。

在父模版中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{# base.html #}
<!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 %}
&copy; Copyright 2008 by <a href="http://domain.invalid/">you</a>.
{% endblock %}
</div>
</body>
</html>

子模版中:

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 %}

在子模板的开头定义了{% extend 'base.html' %}语句来声明继承,此后在子模板中由{% block block_name %}`和`{% endblock %}所包括的语句块,将会替换父模板中同样由{% block block_name %}`和`{% endblock %}所包括的部分。

  • 模板不支持多继承,也就是子模板中定义的块,不可能同时被两个父模板替换。
  • 模板中不能定义多个同名的块,子模板和父模板都不行,因为这样无法知道要替换哪一个部分的内容。
  • 建议在endblock关键字后也加上块名,比如{% endblock block_name %}。可提高程序可读性。
  • 若不希望父模板中的块被子模板替换,可在子模版的块中使用{{ super() }}
  • 默认情况下,块内语句是无法访问块外作用域中的变量。如果想在块内访问块外的变量,需要在块声明时添加scoped关键字:{% block block_name scoped %}{% endblock %}

2.6 include

include用于实现代码重用的功能。通过{% include %}语句,可以将另一个模板加载到当前模板中,并直接渲染在当前位置上。

1
2
3
{% include 'header.html' %}
Body
{% include 'footer.html' %}

当”include”的模板文件不存在时,程序会抛出异常。可以加上”ignore missing”关键字,这样如果模板不存在,就会忽略这段{% include %}语句。

1
{% include 'footer.html' ignore missing %}

{% include %}语句还可以跟一个模板列表:

1
{% include ['footer.html','bottom.html','end.html'] ignore missing %}

上例中,程序会按顺序寻找模板文件,第一个被找到的模板即被加载,而其后的模板都会被忽略。如果都没找到,那整个语句都会被忽略。

2.7 import

一个模版中定义的[宏(macro)](#2.4 宏)可以被不同的模板使用,因此可以将其声明在一个单独的模板文件中。需要使用时import进行导入。

import的语法类似于python中的import

1
2
3
4
{% import 'form.html' as form %}
<p>{{ form.input('username', value='user') }}</p>
<p>{{ form.input('password', 'password') }}</p>
<p>{{ form.input('submit', 'submit', 'Submit') }}</p>

1
2
3
4
{% from 'form.html' import input %}
<p>{{ input('username', value='user') }}</p>
<p>{{ input('password', 'password') }}</p>
<p>{{ input('submit', 'submit', 'Submit') }}</p>

3. 上下文

在Flask应用中的模板可以使用到请求上下文中的环境变量,及一些辅助函数。

例如:

1
{{ request.url }}

上下文变量和函数都可以自定义:

1
2
3
4
5
from flask import current_app

@app.context_processor
def appinfo():
return dict(appname=current_app.name)

函数用@app.context_processor装饰器修饰,它是一个上下文处理器,它的作用是在模板被渲染前运行其所修饰的函数,并将函数返回的字典导入到模板上下文环境中,与模板上下文合并。

自定义函数方法如下:

1
2
3
4
5
6
7
import time

@app.context_processor
def get_current_time():
def get_time(timeFormat="%b %d, %Y - %H:%M:%S"):
return time.strftime(timeFormat)
return dict(current_time=get_time)
1
2
<p>Current Time is: {{ current_time() }}</p>
<p>Current Day is: {{ current_time("%Y-%m-%d") }}</p>

4. 过滤器

变量可以通过过滤器被修改。过滤器可以被认为是一种转换函数,使用过滤器利用的是管道符|。输入的参数就是其所修饰的变量,一些可选参数可以包含在括号中,返回的就是变量转换后的值。过滤器的使用举例如下:

1
2
3
{{ "<p>test</p>"|striptags|upper }}
{# striptags:去除标签,返回"test" #}
{# upper:大写,返回"TEST" #}
1
{{ listx|join(', ') }}

Jinja内置的过滤器如下图所示

avatar

这些过滤器具体的作用可以参考官方文档

同时,还可以使用自定义的过滤器。自定义过滤器可以通过两种方式实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 方式一,通过app.add_template_filter方法
def list_reverse(li):
temp = list(li)
temp.reverse()
return temp

# 第一个参数指定过滤器函数,第二个(可选)参数表示过滤器的名字,默认为函数名
app.add_template_filter(list_reverse,'li_reverse')


# 方式二,通过装饰器
# filter_name参数可选,默认为函数名
@app.template_filter(filter_name)
def my_filter(args):
temp = list(args)
temp.reverse()
return temp

随后可以使用自定义的过滤器

1
{{ listx|list_reverse }}

评论