试图函数的作用,就是生成请求的响应。
服务器接受请求,flask把请求分发到处理请求的视图函数,这两个过程分别称为业务逻辑和表现逻辑。
把表现逻辑移到模板中能提升程序的可维护性。
模板是一个包含响应文本的文件,其中包含用占位变量表示的动态部分。使用真实值替换变量,再返回最终够得到的响应字符串,这一过程称为渲染。
为了渲染模板,flask用一个名为Jinja2的强大模板引擎。
1.Jinja2
形式最简单的Jinja2模板就是一个包含响应文本的文件。
#和(二)中的试图函数index()的响应一样 templates/index.html:Jinja2模板 <h1>Hello World!</h1> #试图函数user返回的响应中包含一个使用变量表示的动态部分 templates/user.html:Jinja2模板 <h1>Hello World!,{{name}}!</h1>
1.1渲染模板
默认情况下,flask在程序文件夹中的templates子文件夹中寻找模板。
flask提供的render_template函数把Jinja2模板继承到了程序中。render_template函数第一个参数是模板的文件名,后面的参数都是键值对,表示模板中变量对应的真实值。
调用render_template函数可以返回HTML文件
from flask import Flask, render_template @app.route('/') def index(): return render_template('index.html') @app.route('/user/<name>') def user(name): return render_template('user.html', name=name) #左边的name表示参数名,右边的name是当前作用域中的变量,表示同名参数的值
render_template第二个及之后的参数可以用字典:
from flask import Flask, render_template @app.route('/') def index(): context = { 'username = 'john' 'gender' = 'man' } return render_template('index.html',**context) @app.route('/user/<name>') def user(name): return render_template('user.html', name=name)
1.2变量
之前例子中的{{ name }}结构表示一个变量,这是一种搞特殊的占位符,告诉模板引擎这个位置的值从渲染模板是使用的数据中获取。
Jinja2能识别所有类型的变量,甚至列表、字典和对象。{{ params.property }}或{{ params['age'] }}
通过使用过滤器,可以定制 变量的值 显示的效果,把原始的变量经过处理后再展示出来。过滤器的对象是模板中的变量。
Hello, {{ name | capitalize }}#大小写过滤器 {{ name | 过滤器 }}#格式 {{ name | default('none') }}#default过滤器,当没有name时,默认展示'none' 评论数 {{ comments | length }}#length过滤器,comments是个列表,返回评论数
部分过滤器:
- safe 渲染值时不转义。默认情况下,出于安全考虑,Jinja2会转义所有变量
- capitalize 把值的首字母转换成大写,其他字母转换成小写
- lower 把值转换成小写形式
- upper 把值转换成大写形式
- title 把值中每个单词的首字母都转换成大写
- trim 把值的首尾空格去掉
- striptags 渲染之前把值中所有的HTML标签都删掉
- default
- length
1.3控制结构
{% if user %} Hello, {{ user }}! {% else %} Hello, Stranger! {% endif %}
用for渲染一组元素:
<ul> {% for comment in comments %} <li>{{ comment }}</li> {% endif %} </ul>
Jinja2还支持宏,宏类似于python的函数:
{% macro render_comment(comment) %} <li>{{ comment }}</li> {% endmacro %} <ul> {% for comment in comments %} {{ render_comment(comment) }} {%endfor %} </ul>
可以将宏保存在单独的文件中,然后重复使用,只需在需要使用的模板中导入。
{% import 'macros.html' as macros %} <ul> {% for comment in comments %} {{ macros.render_comment(comment) }} {% endfor %} </ul>
需要在多处重复使用的模板代码片段可以写入单独的文件,再包含在所有模板中,以避免重复
{% include 'common.html' %}
模板继承:
是另一种改重复使用代码的方式
先创建一个base.html模板
<html> <head> {% block head %} <title>{% block title %}{% endblock %} - My Application</title> {% endblock %} <body> {% block body %} {% endblock %} </body> </head> </html>
衍生模板:
{% extends "base.html" %} {% block title %}Index{% endblock %} {% block head %} {{ super() }} <style> </style> {% endblock %} {% block body %} <h1>Hello, World!</h1> {% endblock %}
extends指令声明这个模板衍生自base.html。其后基模板中的3个块title、head、body被重新定义。注意在新定义的head块中,因为在基模板中其内容不是空的,所以要使用super()来获取原来的内容
继承必须要在基模板中先定义好接口{% block %} {%endblock %}
注意:子模板继承之后,只能在block块里面写代码
2使用flask-bootstrap继承twitter bootstrap
Bootstrap是客户端框架,因此不会直接涉及服务器。服务器需要做的只是提供引用了Bootstrap层叠样式表(CSS)和JavaScript文 件的HTML响应,并在HTML、CSS和JavaScript代码中实例化所需组件。这些操作最理想的执行场所就是模板。
#初始化flask-bootstrap from flask_bootstrap import Bootstrap #... # 下面这行代码应在app = Flask(__name__)之后 bootstrap = Bootstrap(app) #...
bootstrap的base.html模块定义的块见官方文档
很多块都是Flask-Bootstrap自用的,直接重定义可能会出现问题
3自定义错误页面
像常规路由一样,Flask 允许程序使用基于模板的自定义错误页面。最常见的错误代码有两个:404,客户端请求未知页面或路由时显示;500,有未处理的异常时显示。
@app.errorhandler(404) def page_not_found(e): return render_template('404.html'), 404
可以新建一个新模板,其中定义了导航条,之前的user.html和错误页面都可以继承该模板。
base.html:
{% extends "bootstrap/base.html" %} {% block title %}Flasky{% 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="/">Flasky</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"> {% block page_content %}{% endblock %} </div> {% endblock %}
这个模板的content中只有一个<div>容器,其中包含了一个名为page_content的空块,其中内容由衍生模板定义。
404.html:
{% extends "base.html" %} {% block title %}Flasky - Page Not Found{% endblock %} {% block page_content %} <div class="page-header"> <h1>Not Found</h1> </div> {% endblock %}
user.html:
{% block content %} <div class="container"> <div class="page-header"> <h1>Hello, {{ name }}!</h1> </div> </div> {% endblock %}
4链接
flask提供的url_for()函数,可以使用程序URL映射中保存的信息生成URL。用这个函数完成URL跳转。
url_for()函数最简单的用法是以视图函数名(或app.add_url_route()定义路由器时使用的端点名)作为参数,返回对应的URL。
对于
@app.route('/') def index(): return '<h1>Hello World!</h1>'
url_for('index')得到的结果是/。调用url_for('index',_external=True)返回的则是绝对地址,在这个示例中是http://localhost:5000/
在程序内(模板、视图中)生成连接程序内不同路由的链接时,使用相对地址就足够了,浏览器、程序能够根据当前的 URL 补全。但如果要在浏览器以外生成链接,例如在确认邮件中的链接,则必须使用绝对地址,否则谁也不知道前缀是什么。
使用url_for()生成动态地址是,将动态部分作为关键字参数传入,例如,url_for('user',name='john',_external=True)的返回结果是http://localhost:5000/user/john
穿入url_for()的关键字参数不仅限于动态路由中的参数。函数能将任何格外参数添加到查询字符串中,例如,rul_for('index',page=2)的返回结果是/?page=2
用url_for()加载地址
<a href="{{ url_for('login') }}">登录</a> #login是视图函数的名称
5静态文件
web不仅由代码和模板组成,还会使用静态文件,例如HTML引用的图片,JavaScript源码文件和CSS。
在第二章5.2请求调度中,检查程序的URL映射时,有一个static路由,这是因为对静态文件的引用被当成一个特殊的路由,即/static/<filename>。例如,调用url_for('static',filename='css/styles.css',_external=True)返回的结果是http://localhost:5000/static/css/styles.css
默认设置,flask在根目录中的static寻找静态文件,可在static文件夹中使用子文件夹存放文件。服务器在收到前面那个URL后,会生成一个响应,包含文件系统中static/css/styles.css文件的内容。
加载静态文件:
语法:url_for('static',filename='路径')
下例是在页面中链接static文件夹下的css/index.css文件,默认从static文件夹寻找文件
<link rel="stylesheet" href="{{ url_for('static',filename='css/index.css' }}"></link>
链接img:
<img src="{{ url_for('static',filename='images/a.png') }}" alt=""> </img>
链接js:
<script src="{{ url_for('static',filename='js/index.js')" }}> </script>
6使用Flask-Moment本地化日期和时间
Flask-Moment是一个flask拓展程序,能把moment.js集成到jinja2模板中