Flask 学习(二)jinja2模板介绍
控制语句和表达式
举例
Flask Python代码
from flask import Flask, render_template, redirect, request app = Flask(__name__) STUDENT = {'name': 'Old', 'age': 38, 'gender': '中'} STUDENT_LIST = [ {'name': 'Old', 'age': 38, 'gender': '中'}, {'name': 'Boy', 'age': 73, 'gender': '男'}, {'name': 'EDU', 'age': 84, 'gender': '女'} ] STUDENT_DICT = { 'a': {'name': 'Old', 'age': 38, 'gender': '中'}, 'b': {'name': 'Boy', 'age': 73, 'gender': '男'}, 'c': {'name': 'EDU', 'age': 84, 'gender': '女'}, } @app.route("/detail") def detail(): print(url_for("detail")) return render_template("detail.html", **STUDENT) @app.route("/detail_list", ) def detail_list(): return render_template("detail_list.html", stu_list=STUDENT_LIST) @app.route("/detail_dict") def detail_dict(): return render_template("detail_dict.html", stu_dict=STUDENT_DICT)
detail.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ stu }} <table border="1px"> <tr> <td>name</td> <td>age</td> <td>gender</td> </tr> <tr> <td>{{ name }}</td> <td>{{ age }}</td> <td>{{ gender }}</td> </tr> </table> </body> </html>
detail_list.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ stu_list }} <table border="1px"> <tr> <td>name</td> <td>age</td> <td>gender</td> </tr> {% for stu in stu_list %} {% if stu.name != "Old" %} {% if stu.age != 73 %} <tr> <td>{{ stu.name }}</td> <td>{{ stu.get("age") }}</td> <td>{{ stu["gender"] }}</td> </tr> {% endif %} {% endif %} {% endfor %} </table> </body> </html>
detail_dict.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ stu_dict }} <table border="1px"> <tr> <td>id</td> <td>name</td> <td>age</td> <td>gender</td> </tr> {% for stu_key,stu_value in stu_dict.items() %} <tr> <td>{{ stu_key }}</td> <td>{{ stu_value.get("name") }}</td> <td>{{ stu_value.age }}</td> <td>{{ stu_value.gender }}</td> </tr> {% endfor %} </table> </body> </html>
表达式
1.最常用的是变量,由Flask渲染模板时传过来,比如name 也可以是任意一种Python基础类型,比如字符串{{stu_list}},用引号括起;或者数值,列表,元祖,字典,布尔值。直接显示基础类型没啥意义,一般配合其他表达式一起用 2.运算。包括算数运算,如{{ 2 + 3 }};比较运算,如{{ 2 > 1 }};逻辑运算,如{{ False and True }} 3.过滤器|和测试器is 4.函数调用,如{{ current_time() }};数组下标操作,如{{ arr[1] }} in操作符,如{{ 1 in [1,2,3] }} 5.字符串连接符~,作用同Python中的+一样,如{{ "Hello " ~ name ~ "!" }}
6.None值处理{{name or ""}
控制语句
Jinja2的控制语句主要就是条件控制语句if,和循环控制语句for,语法类似于Python if-else: {% if name and name == 'admin' %} <h1>This is admin console</h1> {% elif name %} <h1>Welcome {{ name }}!</h1> {% else %} <h1>Please login</h1> {% endif %} for: {% for stu in stu_list%} {{ stu }} {% endfor %}
过滤器
字符串
<body> {# 当变量未定义时,显示默认字符串,可以缩写为d #} <p>{{ name | default('No name', true) }}</p> {# 单词首字母大写 #} <p>{{ 'hello world' | capitalize }}</p> {# 单词全小写 #} <p>{{ 'XML' | lower }}</p> {# 去除字符串前后的空白字符 #} <p>{{ ' hello ' | trim }}</p> {# 字符串反转,返回"olleh" #} <p>{{ 'hello' | reverse }}</p> {# 格式化输出,返回"Number is 99" #} <p>{{ '%s is %d' | format("Number", 99) }}</p> {# 关闭HTML自动转义 #} <p>{{ '<em>name</em>' | safe }}</p> {% autoescape false %} {# HTML转义,即使autoescape关了也转义,可以缩写为e #} <p>{{ '<em>name</em>' | escape }}</p> {% endautoescape %} </body>
数值操作
{# 四舍五入取整,返回13.0 #} <p>{{ 12.98 | round }}</p> {# 向下截取到小数点后2位,返回12.88 #} <p>{{ 12.8888 | round(2, 'floor') }}</p> {# 绝对值,返回12 #} <p>{{ -12 | abs }}</p>
列表操作
# 取第一个元素 #} <p>{{ [1,2,3] | first }}</p> {# 取最后一个元素 #} <p>{{ [1,2,3] | 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>{{ ['alex','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}] %} {# 按指定字段排序,这里设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>
自定义过滤器
# 第一种方式 def get_even_list(l): return l[::2] # 函数的第一个参数是过滤器函数,第二个参数是过滤器名称 app.add_template_filter(get_even_list, 'even_filter') # 第二种方式 @app.template_filter() # 过滤器函数 def is_even(num): if num % 2 == 0: return "even number" else: return "odd number"
使用
<p>{{ [1,2,3,4,5] | even_filter }}</p> <p>{{ 2 | is_even }}</p>
测试器
测试器总是返回一个布尔值,它可以用来测试一个变量或者表达式,使用”is”关键字来进行测试。
{% set name='ab' %} {% if name is lower %} <h2>"{{ name }}" are all lower case.</h2> {% endif %}
测试器本质上也是一个函数,它的第一个参数就是待测试的变量,在模板中使用时可以省略去。如果它有第二个参数,
模板中就必须传进去。测试器函数返回的必须是一个布尔值,这样才可以用来给if语句作判断。
内置测试器
举例
{# 检查变量是否被定义,也可以用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 %}
官方文档
https://jinja.palletsprojects.com/en/master/templates/#builtin-tests
自定义测试器
定义
# 自定义测试器 # 第一种方式 import re def test_tel(tel_num): tel_re = r'\d{11}' return re.match(tel_re,tel_num) app.add_template_test(test_tel,"is_tel") # 第二种方式 @app.template_test('start_with') def start_with(str, suffix): return str.lower().startswith(suffix.lower())
使用
{% set tel = '18910171111' %} {% if tel is is_tel %} <h2>{{ tel }} is mobile phone</h2> {% endif %} {% set name = 'Hello world' %} {% if name is start_with 'hello' %} <h2>"{{ name }}" start_with "hello"</h2> {% endif %}
全局函数
内置全局函数
{# 全局函数range()的作用同Python里的一样,返回指定范围内的数值序列。三个参数分别是开始值,结束值(不包含),间隔。 如果只传两个参数,那间隔默认为1;如果只传1个参数,那开始值默认为0。 #} <ul> {% for num in range(10,20,2) %} <li>Number is "{{ num }}"</li> {% endfor %} </ul> {# dict()函数,方便生成字典型变量 #} {% set user = dict(name='Joh',age=22) %} <p>{{ user | tojson | safe }}</p> {# 显示 '{"age": 22, "name": "Joh"}' #} {# joiner()函数,它可以初始化为一个分隔符,然后第一次调用时返回空字符串,以后再调用则返回分隔符 #} {% set sep = joiner("|") %} {% for val in range(5) %} {{ sep() }} <span>{{ val }}</span> {% endfor %} {# 显示 "0 | 1 | 2 | 3 | 4" #} {# cycler()函数,在给定的序列中轮循 #} {% set cycle = cycler('odd', 'even') %} <ul> {% for num in range(10, 20, 2) %} <li class="{{ cycle.next() }}">Number is "{{ num }}", next line is "{{ cycle.current }}" line.</li> {% endfor %} {# next(),返回当前值,并往下一个值轮循 reset(),重置为第一个值 current,当前轮循到的值 #} </ul>
官方文档
https://jinja.palletsprojects.com/en/master/templates/#list-of-global-functions
自定义全局函数
定义
# 自定义全局函数 # 第一种方式 @app.template_global() def add_sum(*args): return sum(args) # 第二种方式 import time def current_time(timeFormat="%b %d, %Y - %H:%M:%S"): return time.strftime(timeFormat) app.add_template_global(current_time, 'current_time')
使用
<p>{{ add_sum(1,2,3,4,5) }}</p> <p>Current Time is: {{ current_time() }}</p> <p>Current Day is: {{ current_time("%Y-%m-%d") }}</p>
块 (Block)
一般我们的网站虽然页面多,但是很多部分是重用的,比如页首,页脚,导航栏之类的。对于每个页面,都要写这些代码,很麻烦。
Flask的Jinja2模板支持模板继承功能,省去了这些重复代码。
template:
<body> 你好,template1 {% block template1 %} {% endblock %} 你好,template2 {% block template2 %} {% endblock %} 你好,template {% block template %} {% endblock %} </body>
extend:
{% extends "he.html" %} {% block template %} <h1>yuan</h1> {% endblock %} {% block template1 %} <h1>alex</h1> {% endblock %} {% block template2 %} <h1>wu</h1> {% include "aaa.html" %} {% endblock %}