Flask学习(2)——模板

一、Jinja2模板引擎

1.1 变量

默认情况下,Flask 在应用目录中的 template 子目录中寻找模板。

在模板中使用 {{}} 结构来表示一个变量,是一种特殊的占位符,告诉模板引擎这个位置的值从渲染模板时使用的数据中获取。

index.html:

<h1>Hello World!</h1>

user.html:

<h1>Hello, {{ name }}!</h1>

hello.py

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/')
def index():
    	return render_template('index.html')

@app.route('/user/<name>')
def user(name):
	return render_template('user.html', name=name)

Jinja2 能识别所有类型的变量,甚至是一些复杂的类型,例如列表、字典和对象。下面是在模板中使用变量的一些示例:

<p>A value from a dictionary: {{ mydict['key'] }}.</p>
<p>A value from a list: {{ mylist[3] }}.</p>
<p>A value from a list, with a variable index: {{ mylist[myintvar] }}.</p>
<p>A value from an object's method: {{ myobj.somemethod() }}.</p>

变量的值可以使用过滤器修改。过滤器添加在变量名之后,二者之间以竖线分隔。

Jinjia2变量过滤器:

过滤器名 说明
safe 渲染值时不转义
capitalize 把值的首字母转换成大写,其他字母转换成小写
lower 把值转换成小写形式
upper 把值转换成大写形式
title 把值中每个单词的首字母都转换成大写
trim 把值的首尾空格删掉
striptags 渲染之前把值中所有的 HTML 标签都删掉

1.3 控制结构

(1)条件判断语句

{% if user %}
	Hello, {{ user }}!
{% else %}
	Hello, Stranger!
{% endif %}

(2)for循环语句

<ul>
	{% for comment in comments %}
		<li>{{ comment }}</li>
	{% endfor %}
</ul>

(3)宏

Jinja2支持宏,类似于函数的作用,例如:

{% macro render_comment(comment) %}
<li>{{ comment }}</li>
{% endmacro %}
<ul>

{% for comment in comments %}
{{ render_comment(comment) }}
{% endfor %}
</ul>

还可以把宏保存在单独文件中,然后重复使用,如

macors.html:

{% macro render_comment(comment) %}
<li>{{ comment }}</li>
{% endmacro %}
<ul>

然后再需要用到宏的模板中使用 import 语句导入:

{% import 'macros.html' as macros %}
<ul>
	{% for comment in comments %}
		{{ macros.render_comment(comment) }}
	{% endfor %}
</ul>

(4)模板

需要在多处重复使用的模板代码片段可以写入单独的文件,再引入所有模板中,以以避免重复:

{% include 'common.html' %}

模板也是可以继承的,先创建一个基模板 base.html 如下:

<html>
    <head>
        {% block head %}
        <title>{% block title %}{% endblock %} - My Application</title>
        {% endblock%}
    </head>
<body>
    {% block body %}
    {% endblock %}
</body>
</html>

上面 blockendblock 定义的区块中内容可以在衍生模板中覆盖,如下:

{% extends "base.html" %}
{% block title%}Index{% endblock %}
{% block head %}
    {{ super() }}
    <style>
    </style>
{% endblock %}
{% block body %}
<h1>Hello, World!</h1>
{% endblock %}

extends 声明此模板衍生自 base.html,然后将基模板中定义的3个区块重新覆盖,在衍生模板的区块里可以调用 super()来使用基模板中的内容。


二、使用Flask-Bootstrap集成Bootstrap

使用前需要先初始化Flask-Bootstrap,如下

from flask_bootstrap import Bootstrap
# ...
bootstrap = Bootstrap(app)

然后可以直接继承提供的 bootstrap/base.html 模板,其是一个包含了Bootstrap 文件和一般结构的基模板,衍生模板 user.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">
    <div class="page-header">
        <h1>Hello, {{ name }}!</h1>
    </div>
</div>
{% endblock %}

完整hello.py如下:

from flask import Flask, render_template
from flask_bootstrap import Bootstrap

app = Flask(__name__)
bootstrap = Bootstrap(app)

@app.route('/')
def index():
    	return render_template('index.html')

@app.route('/user/<name>')
def user(name):
	return render_template('user.html', name=name)

Flask-Bootstrap基模板中定义的区块如下:

区块名 说明
doc 整个 HTML 文档
html_attribs <html> 标签的属性
html <html> 标签中的内容
head <head> 标签中的内容
title <title> 标签中的内容
metas 一组 <meta> 标签
styles CSS 声明
body_attribs <body> 标签的属性
body <body> 标签中的内容
navbar 用户定义的导航栏
content 用户定义的页面内容
scripts 文档底部的 JavaScript 声明

若要在衍生模块中添加新的JavaScript文件,为了防止覆盖掉原有的引入Bootstrap的内容,需要使用 super() 函数:

{% block scripts %}
{{ super() }}
<script type="text/javascript" src="my-script.js"></script>
{% endblock %}

三、自定义错误页面

通常的错误页面有如下两种:

  • 404:客户端请求未知页面或路由时显示
  • 500:有未处理的异常时显示

使用 app.errorhandler 装饰器为这两个错误提供自定义的处理函数:

@app.errorhandler(404)
def pate_not_found(e):
	return render_template('404.html'), 404

@app.errorhandler(500)
def internal_server_error(e):
	return render_template('500.html'), 500

为了减少代码重复,我们可以在上面的基础上改进一下 base.html,使其成为继承了 bootstrap/base.html 的二级基模板,也可以被其他模板继承。

二级模板 templates/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 %}

这样,应用中的模板继承自这个模板即可。

templates/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 也可以继承改模板来简化:

{% extends "base.html" %}

{% block title %}Flasky{% endblock %}

{% block page_content %}
<div class="page-header">
    <h1>Hello, {{ name }}!</h1>
</div>
{% endblock %}

四、链接

有时需要用动态路由链接多个不同的页面,此时就可以使用 url_for() 辅助函数,它使用应用的 URL 映射中保存的信息生成URL。

用法如下:

(1)以视图名为参数,返回对应的URL

url_for('index')

返回根URL /

(2)使用参数 _external 可返回绝对地址

url_for('index', _external=True)

返回绝对地址 http://localhost:5000/

(3)生成动态URL,将动态部分作为关键词参数传入

url_for('user', name='john', _external=True)

返回结果是 http://localhost:5000/user/john

(4)还可以添加非动态参数到查询字符串中

url_for('user', name='john', pate=2, version=1)

返回结果是 /user/john?page=2&version=1


五、静态文件

Flask路由中有一个特殊路由 static 路由:/static/<filename>
例如调用:

url_for('static', filename='css/styles.css', _external=True)

返回的结果是 http://localhost:5000/static/css/styles.css

Flask默认在根目录中名为 static 的子目录中寻找静态文件。

下例说明了在应用的基模板中引入 favicon.ico 图标:

templates/base.html

{% block head %}
{{ super() }}
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}", type="image/x-icon">
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}", type="image/x-icon">
{% endblock %}

六、使用Flask-Moment本地化日期和时间

服务器一般使用协调世界时(UTC)来统一时间,但是把时间发送个Web浏览器时需要转换成当地时间,然后用Javascript渲染。

Flask-Moment是一个Flask扩展,能简化把Moment.js 集成到 Jinja2 模板中的过程。

使用pip在虚拟环境中安装 flask-moment 之后同样需要进行初始化

from flask_moment import Moment
moment = Moment(app)

除了Moment.js, Flask-Moment 还依赖 jQuery.js。因此要在 HTML 文档的某个地方引入这两个库。Bootstrap 已经引入了 jQuery.js,因此只需引入 Moment.js 即可。

在基模板中的 scripts 块中引入这个库,同时保留原始内容。
在 templates/base.html 的任何位置引入:

{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{% endblock %}

为了处理时间戳,Flask-Moment 向模板开放了 moment 对象,下例把变量 current_time 传入模板进行渲染:

hello.py:

from flask import Flask, render_template
from flask_bootstrap import Bootstrap
from flask_moment import Moment
from datetime import datetime

app = Flask(__name__)
bootstrap = Bootstrap(app)
moment = Moment(app)

@app.route('/')
def index():
    	return render_template('index.html', current_time=datetime.utcnow())

然后在index.html 中渲染模板变量 current_time

templates/index.html

{% extends "base.html" %}

{% block title %}Flasky{% endblock %}

{% block page_content %}
<div class="page-header">
    <h1>Hello World!</h1>
</div>

<p>The local date and time is {{ moment(current_time).format('LLL') }}.</p>
<p>That was {{ moment(current_time).fromNow(refresh=True) }}</p>

{% endblock %}

format('LLL')函数根据客户端计算机中的时区和区域设渲染日期和时间。参数决定了渲染的方式,从 'L''LLLL' 分别对应不同的复杂度。format() 函数还可接受很多自定义的格式说明符。

第二行中的 fromNow() 渲染相对时间戳,而且会随着时间的推移自动刷新显示的时间。这个时间戳最开始显示为 a few seconds ago,但设定 refresh=True 参数后,其内容会随着时间的推移而更新。

Moment.js文档:http://momentjs.com/docs/#/displaying/

效果如下:
在这里插入图片描述

另外,Flask-Moment渲染的时间还可以实现多种语言的本地化,方法时在引入 Moment.js 后, 立即把两个字母的语言代码传给 locale() 函数,如西班牙语方式:

{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{{ moment.locale('es') }}
{% endblock %}
发布了42 篇原创文章 · 获赞 31 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_42181428/article/details/104073388