·
Jinja2是啥
为了把业务逻辑和表现逻辑分离,将表现逻辑移到模板中是不错的选择;模板是一个包含响应文本的文件,里面用占位变量来表示动态部分,当有上下文时再用真实的值取代之并返回最终得到的响应字符串,这个过程就叫渲染,Jinja2是渲染模板的模板引擎
如何渲染
模板的位置默认是在程序文件夹中的templates子文件夹中寻找,渲染模板有个render_template('模板文件名', [键值对参数])
函数,返回传入了参数的响应字符串,html文件部分:
<!--程序目录/template/user.html-->
<h1>Hello, your name is : {{ name }}~</h1>
--------------------------------------------
<!--程序目录/template/index.html-->
<h1>Hello, im index~</h1>
程序部分:
@app.route('/')
def index():
return render_template('index.html')
@app.route('/user/<name>')
def user(name):
return render_template('user.html', name=name)
效果是和之前是一样的,只是这次是通过渲染模板
模板文件夹的位置可以更改:在实例化时传入
template_folder
参数,即Flask(name,template_folder=”存放模板文件夹名称”)
变量
在模板中用{{ }}
占位符来表示一个变量,里面放变量名,Jinja2能识别所有类型的变量,list、dict、tuple、对象都可,这里要提一句过滤器,它可以根据需求修改变量,用竖线分隔变量名后加在后面,如下:
hi, {{ name|upper }}
upper过滤器把变量name的值都转换成了大写,下面是一些常用过滤器:
过滤器名 | 说 明 |
---|---|
safe | 渲染值时不转义 |
capitalize | 把值的首字母转换成大写,其他字母转换成小写 |
lower | 把值转换成小写形式 |
upper | 把值转换成大写形式 |
title | 把值中的每个单词的首字母都转换成大写 |
trim | 把值的首尾空格去掉 |
striptags | 渲染之前把值中所有的HTML标签都删掉 |
控制结构
顾名思义,可以在模板中用条件控制语句,如if、for,具体见下例:
----------------if-------------------
{% if user %}
Hello, {{ user }}!
{% else %}
Hello, Stranger!
{% endif %}
---------------for--------------------
<ul>
{% for comment in comments %}
<li>{{ comment }}</li>
{% endfor %}
</ul>
Jinja2还支持宏,简化重复操作,可以把宏保存在单独的文件中,需要时导入:
{% import 'macros.html' as macros %}
<ul>
{% for comment in comments %}
{{ macros.render_comments(comment) }}
{% endfor %}
</ul>
重复使用模板的常用两种方法:
include
:把文件包含进来,语句{% include 'common.html' %}
extends
:模板继承,其中block元素可以在衍生模板中修改,语句{% extends "base.html" %}
下面为base模板和继承自base模板的衍生模板例子:
-----------------------base.html---------------------------
<html>
<head>
{% block head %}
<title>{% block title %}{% endblock %}万事屋</title>
{% endblock %}
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
------------------------index.html--------------------------
{% extends "base.html" %}
{% block title %}首屋{% endblock %}
{% block head %}
{{ super() }}
<style>
</style>
{% endblock %}
{% block body %}
<h1>Hello man</h1>
{% endblock %}
要说一下super()
,它的作用是获取原来的内容,如果要在已经有内容的block中添加新内容,需要先用它
Flask-Bootstrap的使用
Bootstrap是客户端框架,不会直接涉及服务器,服务器只需提供引用了Bootstrap的代码响应,并在前端代码中实例化所需组件,我们在模板中执行这些操作
安装和初始化
除了直接修改模板来集成Bootstrap,还可以用Flask拓展中的Flask-Bootstrap,安装只需一行代码的功夫:
pip install flask-bootstrap
初始化也是和之前一样,在创建程序实例时进行:
from flask_bootstrap import Bootstrap
#...
bootstrap = Bootstrap(app)
开始用
下面是继承了Bootstrap的base模板的user页,可理解为base模板中画好了骨架,在衍生模板中填肉,其中title、navbar、content块中的内容都是填的:
{% extends "bootstrap/base.html" %}
{% block title %}欢迎屋{% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">万事屋</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/">HOME</a></li>
</ul>
</div>
</div>
</div>
{% endblock %}
{% block content %}
<div class="container">
<div class="page-header">
<h1>Hello, {{ name | upper}}!</h1>
</div>
</div>
{% endblock %}
访问user页,一下子就美观不少有木有:
base模板中定义的块:
块名 | 说明 |
---|---|
doc | 整个HTML文档 |
html_attribs | <html> 标签的属性 |
html | <html> 标签中的内容 |
head | <head> 标签中的内容 |
title | <title> 标签中的内容 |
metas | 一组<meta> 标签 |
styles | 层叠样式表定义 |
body_attribs | <body> 标签中的内容 |
navbar | 用户定义的导航条 |
content | 用户定义的页面内容 |
scripts | 文档底部的JavaScript声明 |
为了以后的页面继承起来更方便,避免每次继承Bootstrap的base模板时做重复修改,我们可以自己定义一个base模板:
{% extends "bootstrap/base.html" %}
{% block title %}万事屋{% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle"
data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">万事屋</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
<li><a href="/user/Master">欢迎屋</a></li>
</ul>
</div>
</div>
</div>
{% endblock %}
{% block content %}
<div class="container">
{% block page_content %}{% endblock %}
</div>
{% endblock %}
错误页
当访问无效或发生异常时会有错误页(通常很简陋),为了使其和其他页面一致,我们通过继承模板来美化它
后端部分,添加两个路由,注意返回值的第二个是状态码:
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500
前端添加相应的页面,以404为例,新建404.html并继承 自己的 base模板,即:
{% extends "base.html" %}
{% block title %}空屋{% endblock %}
{% block page_content %}
<h1>空荡荡</h1>
{% endblock %}
链接
当写导航条标签的URL时,手写有时会很困难(如有动态部分),Flask也想到这点,所以有了url_for()
函数,它返回视图函数对应的URL,以视图函数名作为参数,如果需要返回绝对地址则传入参数_external=True
生成连接程序内不同路由的链接时用相对地址足矣,若要生成在浏览器之外使用的链接则必须用绝对地址
用url_for()
生成动态地址时,可以将动态部分作为关键字参数传入,而且不仅限于动态路由中的参数,可以是任何额外参数,如url_for('index', page=66)
的返回结果就是/?page=66
PS:URL中用
?
分隔URL和传输数据(如参数),参数之间用&
相连,若遇到空格则转为+
,若是中文就用BASE64加密后得到16进制表示的ASCII码,像这样%XX
静态文件
之前在用app.url_map
检查URL映射时发现有个static
路由,该路由是对静态文件的引用,即/static/<filename>
,举个例子就很容易明白,调用url_for('static', filename='css/styles.css', _external=True)
,得到的就是styles.css
文件的绝对地址,服务器收到这个URL后会生成一个包含该文件内容的响应
正常情况下,静态文件放在程序根目录的static子目录中;试试在该目录中放一个图片,以该图作为页面图标:
{% block head %}
{{ super() }}
<link rel="shortcut icon" href="{{ url_for('static', filename='laugh.jpg') }}"
type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename='laugh.jpg') }}"
type="image/x-icon">
{% endblock %}
运行如图: