Flask-jinja2学习(上)
jinja2的相关知识点
jinja2是python的一个web库,旨在灵活,快速和安全,可以简化python的html语法。
基本语法
- 标记块开头的字符串 {%
- 标记块结束的字符串 %}
- 标记print语句开始的字符串 {{
- 标记print语句结束的字符串 }}
- 标记注释开头的字符串 {#
- 标记注释结尾的字符串 #}
表达式种类
- 最常用的变量,由Flask渲染模板时传过来,类似于 name
- 任意一种python基础类型,例如{{ “hello” }},还有列表,元组,字典等等。
- 运算。包括算数运算,比较运算,逻辑运算 {{False and True}}
- 过滤器 “|” 和测试器 “is”
- 函数调用,如{{ current-time() }},数组下标操作,{{ arr[1] }}
- ‘in’ 操作符,如{{ 1 in [1,2,3] }}
- 字符串连接符‘~’,和python里的‘+’一样。如{{ ‘hello’name‘!’ }}
- ‘ if ’关键字,如{{ hi,%s %name if name }},这里的if不是控制语句
for 循环和 if else
<dl>
{% for user in users if not user.hidden %}
{% if loop.first %}
<div>User List:</div>
{% endif %}
<div class="{{ loop.cycle('odd','even')}}">
<dt>User No.{{loop.index}}</dt>
<dd>{{ user.name }}</dd>
</div>
{% if loop.last %}
<div>Total Users: {{ loop.length }}</div>
{% endif %}
{% else %}
<div>No users found</div>
{% endfor %}
</dl>
循环内置变量loop
- loop.index 循环迭代计数(从1开始)
- loop.index0 循环迭代计数(从0开始)
- loop.revindex 循环迭代倒序计数(从len开始,到1结束)
- loop.revindex0 循环迭代倒序计数(从len开始,到0结束)
- loop.first 是否为循环第一个元素
- loop.length 循环序列中的元素长度
- loop.cycle 在给定的序列中循环
- loop.depth 当前循环在递归的层级
忽略模板写法
<dl>
<dl>忽略模板语法</dl>
{% raw %}
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endraw %}
</dl>
自动转义和给变量赋值
{% autoescape false %}
<h1>Hello {{ name }}!</h1>
{% endautoescape %}
{% set items=[[1,2],[3,4,5]] %}
{{ items }}
with语句
<div>
{% with foo =1 %}
{% set bar =2 %}
{{ foo + bar }}
{% endwith %}
</div>
<div>
{% with arr = ['Sunny','Rainy'] %}
{{ arr }}
{% do arr.append('ltf') %}
{{ arr }}
{% endwith %}
</div>
do 语法需要导入外部扩展
app.jinja_env.add_extension(‘jinja2.ext.do’)
上下文环境
Flask每个请求都有生命周期,在生命周期内请求有其上下文环境Request Context。作为在请求中渲染的模板,自然也在请求的生命周期内,所以Flask应用中的模板可以使用到请求上下文中的环境变量,及一些辅助函数。本文就会介绍下这些变量和函数。
请求对象request
request对象可以用来获取请求的方法”request.method”,表单”request.form”,请求的参数”request.args”,请求地址”request.url”等。它本身是一个字典。在模板中,你一样可以获取这些内容,只要用表达式符号”{{ }}”括起来即可。
会话对象session
session对象可以用来获取当前会话中保存的状态,它本身是一个字典。在模板中,你可以用表达式符号”{{ }}”来获取这个对象
全局对象g
全局变量g,用来保存请求中会用到全局内容,比如数据库连接。模板中也可以访问。
Flask配置对象config
导入的配置信息,就保存在”app.config”对象中。这个配置对象在模板中也可以访问。
url_for()函数
url_for()函数可以用来快速获取及构建URL,Flask也将此函数引入到了模板中,比如下面的代码,就可以获取静态目录下的”style.css”文件。
主程序
#加载扩展
app.jinja_env.add_extension('jinja2.ext.do')
app.secret_key = '123456'
@app.route('/')
def index():
session['user'] = 'guest'
g.db = 'mysql'
return render_template('main.html')
main.html文件
<dl>
<div>
<p>{{ "请求的url为:"~request.url }}</p>
<p>{{ "session保存的用户为:"+session.user }}</p>
<p>DB数据库: {{ g.db}}</p>
<p>Host配置:{{ config }}</p>
</div>
</dl>
<dl>
<div class="odd">
</div>
</dl>
style.css文件
.odd {
background-color: #BDF;
width: 200px;
height: 200px;
}
自定义变量和函数
除了Flask提供的标准上下文变量和函数,我们还可以自己定义。
@app.context_processor
def appinfo():
return dict(appname=current_app.name)
@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)
函数用”@app.context_processor”装饰器修饰,它是一个上下文处理器,它的作用是在模板被渲染前运行其所修饰的函数,并将函数返回的字典导入到模板上下文环境中,与模板上下文合并。然后,在模板中”appname”就如同上节介绍的”request”, “session”一样,成为了可访问的上下文对象。
同理我们可以自定义上下文函数,只需将上例中返回字典的属性指向一个函数即可
<div>
<p>当前 App is: {{ appname }}</p>
<p>当前 Time is: {{ current_time() }}</p>
<p>当前 Day is: {{ current_time("%Y-%m-%d") }}</p>
</div>
内置过滤器
字符串操作
{{ '字符串操作....' }}
{# 单词首字母大写 #}
<p>{{ 'hello' | capitalize }}</p>
{# 单词全小写 #}
<p>{{ 'XML' | lower }}</p>
{# 去除字符串前后的空白字符 #}
<p>{{ ' hello ' | trim }}</p>
{# 字符串反转,返回"olleh" #}
<p>{{ 'hello' | reverse }}</p>
{# 格式化输出,返回"Number is 2" #}
<p>{{ '%s is %d' | format("Number", 2) }}</p>
{# 关闭HTML自动转义 #}
<p>{{ '<em>name</em>' | safe }}</p>
{% autoescape false %}
{# HTML转义,即使autoescape关了也转义,可以缩写为e #}
<p>{{ '<em>name</em>' | escape }}</p>
{% endautoescape %}
数值操作
{{ '数值操作...' }}
{# 四舍五入取整,返回13.0 #}
<p>{{ 12.8888 | round }}</p>
{# 向下截取到小数点后2位,返回12.88 #}
<p>{{ 12.8888 | round(2, 'floor') }}</p>
{# 绝对值,返回12 #}
<p>{{ -12 | abs }}</p>
列表操作
{{ '列表操作...' }}
{# 取第一个元素 #}
<p>{{ [1,2,3,4,5] | first }}</p>
{# 取最后一个元素 #}
<p>{{ [1,2,3,4,5] | last }}</p>
{# 返回列表长度,可以写为count #}
<p>{{ [1,2,3,4,5] | length }}</p>
{# 列表求和 #}
<p>{{ [1,2,3,4,5] | sum }}</p>
{# 列表排序,默认为升序 #}
<p>{{ [3,2,1,5,4] | sort }}</p>
{# 合并为字符串,返回"1 | 2 | 3 | 4 | 5" #}
<p>{{ [1,2,3,4,5] | join('--') }}</p>
{# 列表中所有元素都全大写。这里可以用upper,lower,但capitalize无效 #}
<p>{{ ['tom','bob','ada'] | upper }}</p>
字典操作
{{ '字典列表操作...' }}
{% set users=[{'name':'Tom','gender':'M','age':20},
{'name':'John','gender':'M','age':18},
{'name':'Mary','gender':'F','age':24},
{'name':'Bob','gender':'M','age':31},
{'name':'Lisa','gender':'F','age':19}]
%}
<div>{{ users }}</div>
{# 按指定字段排序,这里设reverse为true使其按降序排 #}
<ul>
{% for user in users | sort(attribute='age', reverse=true) %}
<li>{{ user.name }}, {{ user.age }}</li>
{% endfor %}
</ul>
{# 列表分组,每组是一个子列表,组名就是分组项的值 #}
<ul>
{% for group in users|groupby('gender') %}
<li>{{ group.grouper }}<ul>
{% for user in group.list %}
<li>{{ user.name }}</li>
{% endfor %}</ul></li>
{% endfor %}
</ul>
{# 取字典中的某一项组成列表,再将其连接起来 #}
<p>{{ users | map(attribute='name') | join(', ') }}</p>
语句块过滤
{% filter upper %}
This is a Flask Jinja2 introduction.
{% endfilter %}
自定义过滤器
两种写法
- 不用装饰器
def double_step_filter(l):
return l[::2]
app.add_template_filter(double_step_filter, 'double_step')
- 使用装饰器
@app.template_filter('sub')
def sub(l, start, end):
return l[start:end]
输出…
{# 返回[1,3,5] #}
<p>{{ [1,2,3,4,5] | double_step }}</p>
{# 返回[2,3,4] #}
<p>{{ [1,2,3,4,5] | sub(1,4) }}</p>
内置测试器
显然测试器本质上也是一个函数,它的第一个参数就是待测试的变量,在模板中使用时可以省略去。如果它有第二个参数,模板中就必须传进去。测试器函数返回的必须是一个布尔值,这样才可以用来给if语句作判断。
{% set name = 'Ltf' %}
<p>{{ name }}</p>
{# 检查变量是否被定义,也可以用undefined检查是否未被定义 #}
{% if name is defined %}
<p>Name is: {{ name }}</p>
{% endif %}
{# 检查是否所有字符都是大写 #}
{% if name is upper %}
<h2>"{{ name }}" are all upper case.</h2>
{% endif %}
{# 检查变量是否为空 #}
{% if name is none %}
<h2>Variable is none.</h2>
{% endif %}
{# 检查变量是否为字符串,也可以用number检查是否为数值 #}
{% if name is string %}
<h2>{{ name }} is a string.</h2>
{% endif %}
{# 检查数值是否是偶数,也可以用odd检查是否为奇数 #}
{% if 2 is even %}
<h2>Variable is an even number.</h2>
{% endif %}
{# 检查变量是否可被迭代循环,也可以用sequence检查是否是序列 #}
{% if [1,2,3] is iterable %}
<h2>Variable is iterable.</h2>
{% endif %}
{# 检查变量是否是字典 #}
{% if {'name':'test'} is mapping %}
<h2>Variable is dict.</h2>
{% endif %}
自定义测试器
和自定义过滤器的写法一样
- 无装饰器
import re
def has_number(str):
return re.match(r'.*\d+', str)
app.add_template_test(has_number,'contain_number')
- 有装饰器
@app.template_test('end_with')
def end_with(str, suffix):
return str.lower().endswith(suffix.lower())
- 模板语句
{% if name is contain_number %}
<h2>"{{ name }}" contains number.</h2>
{% endif %}
{% if name is end_with "me" %}
<h2>"{{ name }}" ends with "me".</h2>
{% endif %}