3.1 模板基本用法
创建模板
user = {
"username" : "Artio",
"bio": "A boy who loves movies and music"
}
movies = [
{"name": "My nEIGHBORS Totoro", "year": "1998"},
{"name": "Three Colous trilogy", "year": "1993"}
]
电影清单模板
watchlist.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{ user.username}}'s Watchlist</title>
</head>
<body>
<a href="{{ url_for("index") }}">←Return</a>
<h2>{{ user.username }}</h2>
{% if user.bio %}
<i>{{ user.bio}} </i>
{% else %}
<i>This user has not provided a bio.</i>
{% endif %}
<h5> {{ user.username }}'s WatchList</h5>
<ul>
{% for movie in movies %}
<li>{{ movie.name}}-{{ movie.year }}</li>
{% endfor %}
</ul>
</body>
</html>
(1) 语句
if 语句
{% if %}
...
{% elif %}
...
{% else %}
...
{% endif %}
for 语句
{% for item in items %}
{% endfor %}
(2)表达式
比如字符串, 变量,函数调用等;
{{ user }}
(3) 注释
{# ... #}
另外 在模板中 Jinja2支持使用"." 获取变量的属性 , 比如user字典中的username键值通过"."获取, 即user.username 在效果上等同于user["username"]
3.1.2 模板语法
{ % if user.bio %}
<i>{{ user.bio }}</i>
{ % else %}
<i>This user has not provider a bio.</i>
{% endif %}
{% for movie in movies %}
{{ movie.index() }}-{{ movie.name }}
{ % endfor %}
常用的JInja2 for模板特殊变量
变量 | 说明 |
---|---|
loop.index | 当前迭代数(从1开始) |
loop.index() | 当前迭代数(从0开始) |
loop.reindex | 当前反向迭代数(从1开始) |
loop.reindex() | 当前反向迭代数(从0开始) |
loop.first | 如果是第一个元素 则为True |
loop.last | 如果是最后一个元素 则为True |
loop.preitem | 上一个迭代的条目 |
loop/nextitem | 下一个迭代的条目 |
loop.leghth | 序列包含的元素数量 |
3.1.3 渲染模板
from flask import render_template, Flask
...
@app.route("/watchlist")
def watch_list():
return render_template("watchlist.html", user=user, movies=movies)
除了render_template Flask还提供了一个render_template_string()函数用来渲染模板字符串
-
{{ mylist[0] }} 获取列表的第一个元素
-
{{ my_tuple[0] }} 获取元祖的第一个元素
-
{{ my_dict["name"]}} 获取字典的键name的值
-
{{ my_object.name() }} 调用my_object调用某方法的返回值
3.2.1 上下文
除了渲染时传入变量 还可以在模板中定义变量 使用set标签
{% set navigation = [("/", "Home"), ("/about", "About")] %}
也可以将一部分模板数据定义为变量 使用set 标签和endset标签声明开始和结束
{% set navigation %}
<a href="/"> Home </a>
<a href="/about">About</a>
{% endset %}
- 1.内置上下文
Flask在木棒上下文提供了一些内置变量 可以再模板中直接使用
变量 | 说明
----- | ------
config| 当前的配置对象
request| 当前的请求对象 ,在已激活的请求环境下可用
session| 当前的会话对象,在已激活的请求环境下可用
g | 与请求绑定的全局变量 在已激活的请求环境下可用 - 2.自定义上下文
注册模板上下文处理函数
@app.content_processor
def inject_foo():
foo = "I am foo"
return dict(foo=foo)
当我们调用render_template()函数渲染任意一个模板时,所有使用app.content_processor装饰器的模板上下文处理函数都会被执行,这些函数的返回值会被添加到模板中, 因此我们可以再模板中直接使用foo变量
- 除了使用app.context_processor装饰器 也可以直接将其作为方法调用 传入模板上下文处理函数
def inject_foo():
foo = "I am foo"
return dict(foo=foo)
app.context_processor(inject_foo)
使用lambda可以简化为:
app.context_processor(lambda : dict(foo="I am foo.")
3.2.2 全局对象
- 1.内置全局函数
jinja2内置模板全局函数
函数 | 说明 |
---|---|
range(start,] stop[, step]) | 和python中的range()用法相同 |
lipsum(n=5, html=True, min=20, max=100) | 生成随机文本(lorem ipsum), 可以再测试时间用来填充页面。默认生成5段HTML文本 ,每段包含20~100个单词 |
dict(**items) | 和python中的dict()用法想通过 |
除了Jinja2内置的全局函数 Flask模板内置了2个全局函数
函数 | 说明 |
---|---|
url_for() | 用于生成URL函数 |
get_flashed_messages() | 用于获取flask消息的函数 |
- 2.自定义全局函数
除了使用app_context_processor 注册模板上下文处理函数来传入函数 我们也可以使用app.template_global装饰器来将函数注册为模板全局函数。
@app.template_global()
def bar():
return "I am bar."
默认使用函数的原名称传入模板 在app.template_global() 装饰器中使用name参数可以指定一个自定义的名称
3.2.3 过滤器
过滤器使用的例子:
{{ name| title }}
Jinja2常用内置过滤器
过滤器 | 说明 |
---|---|
default(value, default_value="", boolean=False) | 设置默认值,默认值作为参数传入,别名为d |
escape(s) | 转义HTML文本,别名为e |
first(seq) | 返回序列的第一个元素 |
last(seq) | 返回序列的最后一个元素 |
length(object) | 返回变量的疮毒 |
random(seq) | 返回序列中的随机元素 |
safe(value) | 将变量值标记为安全, 避免转义 |
trim(value) | 清楚变量值前后的空格 |
max(value, case_sensitive=False, attribute=None) | 返回序列中的最大值 |
min(value, case_sensitive=False, attribute=None) | 返回序列中的最小值 |
unique(value, case_sensitive=False, attribute=None) | 返回序列中不重复的值 |
striptags(value) | 清楚变量值内的HTML标签 |
urlize(value, trim_url_limit=None, nofollow=False, target=None, rel=None) | 将URL文本转换为可单击的HTML连接 |
wordcount(s) | 计算单词数据 |
tojson(value, indent=None | 将变量值转换为JSON数据 |
turncate(s, length=255, killwords=False, end="...", leeway=None) | 截断字符串,常用于显示文本摘要, length参数设置截断的长度, killwords参数设置是否截断单词, end参数设置结尾的符号. |
<h1> Hello, {{ name|default("陌生人")|title }}</h1>
示例中name为变量 设置默认值 并将其标题化
- 2.自定义过滤器
使用from flask import Markup @app.template_filter() def musical(s): return s + Markup(' %#9835;')
{{ name|musical }}
3.2.4 测试器
使用is连接变量和测试器
{% if age is number %}
{{ age * 365 }}
{% else %}
无效的数字
{% endif %}
1. 内置测试器
Jinja2内置测试器
测试器 | 说明 |
---|---|
callable(value) | 判断对象是否可被调用 |
defined(value) | 判断变量是否已定义 |
undefined(value) | 判断对象是否未定义 |
none(value) | 判断对象是否为None |
number(value) | 判断对象是否为数字 |
string(value) | 判断对象是否为字符串 |
sequence(value) | 判断变量是否是序列, 如字符串 列表 元祖 |
iterable(value) | 判断变量是否可迭代 |
mapping(value) | 判断变量是否是匹配对象 比如字典 |
sameas(value, other) | 判断变量与pther是否指向相同的内存地址 |
其他参数可以添加括号传入 比如sameas
-
判断foo是否和bar是同一地址
{% if foo is sames(bar) %}
2.自定义测试器
@app.template_test()
def baz(n):
if n == "baz":
return True
return False
同样的 测试器名称默认用函数名,需要自定义用name参数传入装饰器app.template_test(name=name)
3.2.5
模板环境对象
在程序中, 我们可以使用app.jinja_env更改Jinja2设置 比如我们自定义所有的定界符
使用variable_start_string, variable_end_string
app = Flask(__name__)
app.jinja_env.variable_start_string = "[["
app.jinja_env.variable_start_string = "]]"
添加自定义全局对象
def bar():
return "I am bar"
foo = "I am foo"
app.jinja_env.globals["bar"] = bar # 注册全局函数
app.jinja_env.globals["foo"] = foo # 注册全局变量
添加自定义过滤器
def smiling():
return s + ” :)“
app.jinja_env.filters["smiling"] = smiling
添加自定义测试器
def baz(n):
if n == "baz":
retrun True
return False
app.jinja_env.test["baz"] = baz
3.3 模板结构组织
3.3.1 局部模板
{% include "_banner.html" %}
为了和普通模板区分 局部模板的命名一般以一个下划线开始
3.3.2 宏
macors.html
{% macro qux(amount=1) %}
{% if amount == 1 %}
I am qux.
{% elif amount > 1 %}
We are quxs.
{% endif %}
{% endmacor %}
在其他模板调用
{% from 'macros.html' import qux %}
...
{{ qux(amount=5) }}
在使用宏时 我们需要注意上下文问题 在Jinja2中, 处于性能的考虑 并且为了让这一切保持显示,默认情况下包含(include)一个局部模板会传递当前上下文到局部模板中 但导入(import)却不会
, 具体来说 当我们使用render_template()函数渲染一个foo.html模板时.这个foo.html的模板上下文包含下列对象
- Flask使用内置的模板上下文处理函数提高的g. session, config, request
- 扩展使用内置的模板上下文处理函数提供的变量
- 自定义模板上下文处理器传入的变量
- 使用render_template() 函数传入的变量
- Jinja2和Flaks内置及自定义的全局对象
- Jinja2内置及自定义过滤器
- Jinja2内置及自定义测试器
inclode标签插入的局部模板 同样可以使用上述上下文的变量和函数 ,而导入另一个并非直接渲染的模板 比如macros.html时, 这个模板仅包含下列这些对象 - Jinja2和Flask内置的全局函数和自定义全局函数
- Jinja2内置及自定义过滤器
- Jinja2内置及自定义测试器
如果我们需要在导入的宏中使用第一个列表的2, 3, 4项,就需要在导入时显式的使用with context声明传入当前模板的上下文
{% from "macros.html" import foo with context %}
虽然Flask使用内置的模板上下文处理函数传入session, g, request 和config, 但它同时也是用app.jinjia_env.globals字典将这几个变量设置为全局变量 所以我们仍然可以再不显示声明传入上下文的情况下,直接在导入的宏中使用他们.
3.3.3 模板继承
编写模板 base.html
<!DOCTYPE html>
<html>
<head>
{% block head %}
<meta charset="utf-8">
<title>{% block title %}Template-HelloFlask{% endblock %}</title>
{%block styles%}{% endblock %}
{% endblock %}
</head>
<body>
<nav>
<ul><li><a heaf="{{url_for('index') }}"></a>Home</li></ul>
<nav>
<main>
{% block content %}{% endblock %}
</main>
<footer>
{% block footer %}{% endblock %}
</footer>
{% block scripts%}{% endblock %}
</body>
</html>
2.编写子模板
- index.html
{% extends "base.html" %}
{% from "macros.html" import qux %}
{% block content %}
{% set name="baz" %}
<h1>Template</h1>
<ul>
<li><a href="{{ url_for('watchlist') }}">WatchList</a></li>
<li>Filters: {{ foo|masical }}</li>
<li>Flobal: {{ bar() }}</li>
<li>Test: {% if name is bar %}I am baz.{% endif %}</li>
<li>macro: {{ que(amount=5) }}</li>
</ul>
{% endblock %}
extends必须是子模板的第一个标签
3.4 模板进阶实践
3.4.1 空白控制
{% if user.bio %}
<i>{{ user.bio }}</i>
{% else %}
<i> This user has not provided a bio.</i>
{% endif %}
渲染后输出的HTML
<I>{{ user.bio }}</i>
<i>his user has not provided a bio.</i>
如果想在渲染时去掉这些空行 可以再边界符内测添加减号 {%- endfor %}
会移除该语句前的空白,同理在右边的定界符内测添加减号将移除该语句后的空白:
{% if user.bio -%}
<i>{{ user.bio }}</i>
{% else -%}
<i> This user has not provided a bio.</i>
{%- endif %}
现在的输出结果
<I>{{ user.bio }}</i>
<i>his user has not provided a bio.</i>
除了在模板中使用减号控制空白外 我们可以再模板环境对象提供的trim_blocks和lstrip_blocks属性设置 前者用来删除Jinja2语句后的第一个空行 后者则用来删除Jinja2语句所在行之前的空格和制表符。
app.jinja_env.trim_blocks = True
app.jinja_env.lstrip_blocks = True
需要注意的是 宏内的空白控制行为不受trim_bolcks 和lstrip_bolcks控制
{% macro qux(amount=1) %}
{% if amount == 1 -%}
I am qux.
{% elif amount > 1 -%}
We are quxs.
{%- endif %}
{% endmacor %}
3.4.2 加载静态文件
url_for('static', filename="icon.png")
<img src="{{ url_for('static', filename='icon.png') }}" width="50">
<link rel="stylesheet" type="text/css" href={{ url_for(’static', filename='styles.css') }}">
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='icon.png') }}">
s使用宏加载静态资源
为了方便加载静态资源 我们可以创建一个专门用于加载静态资源的宏
{% macro static_file(type, filename_or_url, locak=True %}
{% if local %}
{% set filename_or_url = url_for("static", filename=file_or_url) %}
{% endif %}
{% if type == 'css' %}
<link rel="stylesheet" href="{{ filename_or_url }}" type="text/css">
{% elif type == 'js' %}
<script type="text/javascript" src="{{ filename_or_url }}"></script>
{% elif type == 'icon' %}
<link rel="icon" href="{{ filename_or_url }}" >
{% endif %}
{% endmacro %}
使用该宏加载静态资源
{% from "macros.html" import static_file %}
{{ static_file('css', 'css/bootstrap.min.css') }}
{{ static_file('css', 'https://maxcdn.../css/bootstrap.min.css', local=False) }}
3.4.3 消息闪现
后端逻辑
from flask import Flask, flash, render_template
app = Flask(__name__)
app.secrect_key = "secrect string"
@app.route("/flash")
def just_flash():
flash("I am flash, whos is looking for me?")
return redirect(url_for("index"))
前端逻辑
<main>
{% for message in get_flash_messages() %}
<div class="alert">{{ message }}</div>
{% endfor %}
{% block content %}{% endblock %}
<main>
3.4.4 自定义错误页面
404.html
{% extents "base.html" %}
{% block title %} 404 - Page Not Found {% endblock %}
{% block content %}
<h1> Page not Found</h1>
<p>You are lost...</p>
{% endblock %}
后端代码
from flask import Flask, render_template
@app.errorhandler(404)
def page_not_found(e):
return render_template("errors/404.html"), 404
错误处理函数接收异常对象作为参数,内置的异常对象提供了下列常用属性
属性 | 说明 |
---|---|
code | 状态码 |
name | 原因短语 |
description | 错误描述,另外使用get_description()方法还可以获取HTML格式的错误代码 |
3.4.5 定义为JavaScript/CSS 变量
<span data-id="{{ user.id }}" data-username="{{ user.username }}">
{{ user.username }}
</span>
在JavaScript中 我们可以使用Dom元素的dataset属性获取data-*属性值
比如element.dataset.username 或者使用getAttribute()方法, 比如element.getAttribute('data-username')
以及用Jquery时 可以直接对Jquery对象调用data方法 比如$element.data("username")
对于需要全局使用的属性 则可以在页面中使用嵌入式JavaScript定义变量
<script type="text/javascript">
var foo = "{{ foo_variable }}";
</script>
同理css文件
<style>
:root {
--theme-color: {{ thrme_color }};
--backgroup-url: {{ url_for("static", filename="backgroup.jpg") }}
}
</style>
在css文件中 使用var()函数并传入变量名即可获取对应的变量值
#foo {
color: var(--theme-color);
}
#bar {
backgroup: var(--backgroup-url);
}
网友评论