Web开发-Flask从零开始的学习(二)
3 模板
将业务逻辑和表现逻辑进行分离,把表现逻辑移到模板中能提升应用的可维护性。
模板是包含响应文本的文件,其中包含用占位变量表示的动态部分,其具体值只在请求的上下文中才能知道。使用真实值替换变量,再返回最终得到的响应字符串,这一过程称为渲染。为了渲染模板,Flask 使用一个名为 Jinja2 的强大模板引擎。
Jinja2模板引擎
默认情况下,Flask 在应用目录中的 templates 子目录里寻找模板。
jinja2模板:
Hello World!
Hello, {{name}} !
渲染模板:
from flask import Flask
from flask import request,render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/user/')
def user(name):
return render_template('user.html',name=name)
Flask 提供的 render_template() 函数把 Jinja2 模板引擎集成到了应用中。
这个函数的第一个参数是模板的文件名,随后的参数都是键 – 值对,表示模板中变量对应的具体值。左边为模板中使用的占位符,右边为当前作用域的变量
变量
在模板中使用的 {{ name }} 结构表示一个变量,这是一种特殊的占位符,告诉模板引擎这个位置的值从渲染模板时使用的数据中获取。
- jinja2能识别许多类型的变量:
A value from a dictionary: {{ mydict['key'] }}.
A value from a list: {{ mylist[3] }}.
A value from a list, with a variable index: {{ mylist[myintvar] }}.
A value from an object's method: {{ myobj.somemethod() }}.
- 变量的值可以使用过滤器修改。过滤器添加在变量名之后,二者之间以竖线分隔,例如
{{name|capitalize}}
首字母大写
safe 渲染值时不转义
capitalize 把值的首字母转换成大写,其他字母转换成小写
lower 把值转换成小写形式
upper 把值转换成大写形式
title 把值中每个单词的首字母都转换成大写
trim 把值的首尾空格删掉
striptags 渲染之前把值中所有的 HTML 标签都删掉
默认情况下,出于安全考虑,Jinja2 会转义所有变量。
很多情况下需要显示变量中存储的HTML 代码,这时就可使用 safe 过滤器。
page41
控制结构
Jinja2 提供了多种控制结构,可用来改变模板的渲染流程。
- 条件判断
{% if user %}
Hello, {{ user }}!
{% else %}
Hello, Stranger!
{% endif %}
- 循环结构
{% for comment in comments %}
- {{ comment }}
{% endfor %}
- 支持宏(类似函数)
{% macro render_comment(comment) %}
{{ comment }}
{% endmacro %}
{% for comment in comments %}
{{ render_comment(comment) }}
{% endfor %}
-
代码复用:
-
重复使用宏:把宏保存在单独的文件中,然后在需要使用的模板中导入
{% import 'macros.html' as macros %}
-
{% for comment in comments %}
{{ macros.render_comment(comment) }}
{% endfor %}
或者
{% include 'common.html' %}
-
模板继承,类似类继承
基模板
base.html
:{% block head %}
{% block title %}{% endblock %} - My Application {% endblock %} {% block body %} {% endblock %}基模板中定义的区块(block)可在衍生模板中覆盖。
衍生模板:
{% extends "base.html" %} {% block title %}Index{% endblock %} {% block head %} {{ super() }} {% endblock %} {% block body %}
Hello, World!
{% endblock %}如果基模板和衍生模板中的同名区块中都有内容,显示衍生模板
在衍生模板的区块里可以调用 super(),引用基模板中同名区块里的内容
-
Flask-Bootstrap拓展集成Bootstrap
Bootstrap是一个开源Web前端框架。按照flask-bootstrap
拓展
- 在实例py文件中初始化
from flask_bootstrap import Bootstrap
# ...
bootstrap = Bootstrap(app)
- 导入后可以利用jinja2的模板继承机制来拓展基模板
{% extends "bootstrap/base.html" %}
{% block title %}Flasky{% endblock %}
{% block navbar %}
Hello, {{ name }}!
{% endblock %}
{% endblock %}
{% block content %}
extends 指令从 Flask-Bootstrap 中导入bootstrap/base.html,从而实现模板继承。Flask-Bootstrap 的基模板提供了一个网页骨架,引入了 Bootstrap 的所有 CSS 和 JavaScript文件。
很多区块都是 Flask-Bootstrap 自用的,如果直接覆盖可能会导致一些问题。
例如,Bootstrap 的 CSS 和 JavaScript 文件在 styles 和 scripts 区块中声明。如果应用需要向已经有内容的块中添加新内容,必须使用 Jinja2 提供的 super() 函数。
{% block scripts %}
{{ super() }}
{% endblock %}
自定义错误界面
Flask 允许应用使用模板自定义错误页面。最常见的错误代码有两个:
- 404,客户端请求未知页面或路由时显示
- 500,应用有未处理的异常时显示
使用 app.errorhandler
装饰器为这两个错误提供自定义的处理函数:
@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
与视图函数一样,错误处理函数也返回一个响应。此外,错误处理函数还要返回与错误对应的数字状态码。
同时这两个错误处理视图函数的模板也需要我们编写,而再去分别创建两个html文件是重复劳动。
利用jinja2的模板继承机制解决这一问题。Flask-Bootstrap 提供了一个具有页面基本布局的基模板;同样,应用也可以自定义一个具有统一页面布局的基模板,其中包含导航栏,而页面内容则留给衍生模板定义。
建立我们自己定义的应用基模板templates/base.html
:
{% extends "bootstrap/base.html" %}
{% block title %}Flasky{% endblock %}
{% block navbar %}
{% block page_content %}{% endblock %}
{% endblock %}
{% endblock %}
{% block content %}
自定义的基模板继承自bootstrap,后续应用的其他模板(404.html等)继承该自定义基模板
链接
任何具有多个路由的应用都需要可以连接不同页面的链接,例如导航栏
- 直接编写简单路由的URL链接
- 对于包含可变部分的动态路由
为了避免直接编写导致URL对路由产生不必要的依赖关系:
url_for()
:使用应用中URL映射中保存的信息生成URL(或者使用端点名)
url_for('index') # 结果:/
url_for('index',_external=True) # 返回绝对地址 http://localhost:5000/
# 动态URL生成
url_for('user',name='join',_external=True) # 返回http://localhost:5000/user/join
# 同时不限于动态路由中的参数,也包括非动态参数
url_for('user',name='john',page=2,version=1) #返回结果 /user/john?page=2&version=1
静态文件
Web 应用不是仅由 Python 代码和模板组成,多数应用还会使用静态文件:HTML 代码引用的图像、JavaScript 源码文件和 CSS。
审查 hello.py 应用的 URL 映射时,其中有一个 static 路由。
这是 Flask 为了支持静态文件而自动添加的,这个特殊路由的 URL 是 /static/。例如调用 url_for('static', filename='css/styles.css', _external=True) 得到的结果是 http://localhost:5000/static/css/styles.css。
默认设置下,Flask 在应用根目录中名为 static 的子目录中寻找静态文件。如果需要,可在static 文件夹中使用子文件夹存放文件。
{% block head %}
{{ super() }}
{% endblock %}
这个图标的声明插入 head 区块的末尾。注意,为了保留基模板中这个区块里的原始内容,我们调用了 super()。
Flask-Moment本地化日期和时间
服务器需要统一时间单位,这和用户所在的地理位置无关,所以一般使用协调世界时(UTC,coordinated universal time)
而用户往往需要当地时间和当地惯用的时间格式
Flask-Moment 是一个 Flask 扩展,能简化把 Moment.js 集成到 Jinja2 模板中的过程。
Moment.js是使用 JavaScript 开发的优秀客户端开源库,它可以在浏览器中渲染日期和时间。
- hello.py:初始化 Flask-Moment
from flask_moment import Moment
moment = Moment(app)
- 基模板的scripts块中引入Moment.js库
{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{{ moment.locale('zh-cn') }}
{% endblock %}
为了处理时间戳,Flask-Moment 向模板开放了 moment 对象。
- hello.py:添加一个 datetime 变量
from datetime import datetime
@app.route('/')
def index():
return render_template('index.html',current_time=datetime.utcnow())
- templates/index.html:使用 Flask-Moment 渲染时间戳
The local date and time is {{ moment(current_time).format('LLL') }}.
That was {{ moment(current_time).fromNow(refresh=True) }}
一篇关于flask-moment的
format('LLL') 函数根据客户端计算机中的时区和区域设置渲染日期和时间。
第二行中的 fromNow() 渲染相对时间戳,而且会随着时间的推移自动刷新显示的时间。设定 refresh=True 参数后,其内容会随着
时间的推移而更新。