Jinja2 模板的学习

默认情况下,Flask 在程序文件夹中的 templates 子文件夹中寻找模板。在下一个 hello.py 版本中,要把前面定义的模板保存在 templates 文件夹中,并分别命名为 index.html 和 user.html。

index.thm的代码如下,不传参数

<html>
    <head>
        <title>欢迎来到王者荣耀</title> 
    </head>
    <body>
        <p>你是不是要成为一个高手</p>
    </body>

</html>

user.html中传递一个参数

<html>
    <head>
        <title>欢迎来到王者荣耀</title> 
    </head>
    <body>
        <p>你是不是要成为一个高手,你的名字是{{name}}</p>
    </body>

</html>

在hello.py中,

from flask import 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)

1、变量

在模板中使用的 {{ 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>

可以使用过滤器修改变量,过滤器名添加在变量名之后,中间使用竖线分隔。例如,下述 模板以首字母大写形式显示变量 name 的值:

Hello, {{ name|capitalize }}

下面是常用的过滤器

8568F68D-6315-4E2D-88A3-205D6C011941.png

更多的fliter官方文档

2、Jinja2的控制结构

if - else 的写法

    {% if name %}
        Hello,{{name}}
    {% else %}
        Hello ,你没名字一看就是菜啊!!!
    {% endif %}

for 循环

    {% for title in ["黄金4","黄金3","黄金2"] %}
        <li>{{title}}</li>
    {% endfor %}

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' %}

另一种重复使用代码的强大方式是模板继承,它类似于 Python 代码中的类继承。首先,创
建一个名为 base.html 的基模板:

<html>
    <head>
        {% block head %}
        <title>{% block title %}{% endblock %} - MyApplication</title>
        {% endblock %}
    </head>
    <body>
        {%block body %}
        <p>我是来自基类</p>
        {%endblock %}
    </body>

</html>

新建一个son.html 继承base.html

{% extends "base.html" %}
{% block title %} INDEX {% endblock %}
{% block head %}
    {{super()}}
{% endblock %}
{% block body %}
{{super()}}
<h1>我也是醉了,这是啥基础自基础模板</h1>
{% endblock %}

extends 指令声明这个模板衍生自 base.html。在 extends 指令之后,基模板中的 3 个块被 重新定义,模板引擎会将其插入适当的位置。注意新定义的 head 块,在基模板中其内容不 是空的,所以使用 super() 获取原来的内容。

3、自定义错误页面的方式

自定义错误页面
     @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

4、链接

任何具有多个路由的程序都需要可以连接不同页面的链接,例如导航条。
在模板中直接编写简单路由的 URL 链接不难,但对于包含可变部分的动态路由,在模板 中构建正确的 URL 就很困难。而且,直接编写 URL 会对代码中定义的路由产生不必要的 依赖关系。如果重新定义路由,模板中的链接可能会失效。
为了避免这些问题,Flask 提供了 url_for() 辅助函数,它可以使用程序 URL 映射中保存 的信息生成 URL。
url_for() 函数最简单的用法是以视图函数名(或者 app.add_url_route() 定义路由时使用 的端点名)作为参数,返回对应的 URL。例如,在当前版本的 hello.py程序中调用 url_ for('index')得到的结果是/。调用url_for('index', _external=True)返回的则是绝对地 址,在这个示例中是 http://localhost:5000/
生成连接程序内不同路由的链接时,使用相对地址就足够了。如果要生成在 浏览器之外使用的链接,则必须使用绝对地址,例如在电子邮件中发送的 链接。
使用 url_for() 生成动态地址时,将动态部分作为关键字参数传入。例如,url_for ('user', name='john', _external=True) 的返回结果是 http://localhost:5000/user/john
传入 url_for() 的关键字参数不仅限于动态路由中的参数。函数能将任何额外参数添加到 查询字符串中。例如,url_for('index', page=2) 的返回结果是 /?page=2。

5、静态文件

Web 程序不是仅由 Python 代码和模板组成。大多数程序还会使用静态文件,例如 HTML
代码中引用的图片、JavaScript 源码文件和 CSS。
例如,调用 url_for('static', filename='css/styles.css', _external=True) 得 到 的 结 果 是 http:// localhost:5000/static/css/styles.css。
默认设置下,Flask 在程序根目录中名为 static 的子目录中寻找静态文件。如果需要,可在 static 文件夹中使用子文件夹存放文件。服务器收到前面那个 URL 后,会生成一个响应, 包含文件系统中 static/css/styles.css 文件的内容。

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

有一个使用 JavaScript 开发的优秀客户端开源代码库,名为 moment.js(http://momentjs. com/),它可以在浏览器中渲染日期和时间。Flask-Moment 是一个 Flask 程序扩展,能把 moment.js 集成到 Jinja2 模板中。Flask-Moment 可以使用 pip 安装:

 pip3 install flask-moment

这个扩展的初始化方法如示例 3-11 所示。 示例 3-11 hello.py:初始化 Flask-Moment

from flask.ext.moment import Moment moment = Moment(app)

7、使用Flask-Bootstrap集成Twitter Bootstrap

安装

 pip3 install flask-bootstrap

初始化 Flask-Bootstrap

from flask_bootstrap import Bootstrap
bootstrap = Bootstrap(app)

新建一个home.html模板,继承bootstrap的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">
         <div class="page-header">
             <h1>Hello, {{ name }}!</h1>
         </div>
     </div>
{% endblock %}

Flask-Bootstrap 的 base.html 模板还定义了很多其他块,都可在衍生模板中使用。表 3-2 列

![Uploading 18C6212F-F78C-4AA2-8281-9FAB2C5FC341_474250.png . . .]
18C6212F-F78C-4AA2-8281-9FAB2C5FC341.png

上图中很多块都是 Flask-Bootstrap 自用的,如果直接重定义可能会导致一些问题。例 如,Bootstrap 所需的文件在 styles 和 scripts 块中声明。如果程序需要向已经有内容的块 中添加新内容,必须使用 Jinja2 提供的 super() 函数。例如,如果要在衍生模板中添加新 的 JavaScript 文件,需要这么定义 scripts 块:

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

7、web表单

尽管 Flask 的请求对象提供的信息足够用于处理 Web 表单,但有些任务很单调,而且要重 复操作。比如,生成表单的 HTML 代码和验证提交的表单数据。
Flask-WTF(http://pythonhosted.org/Flask-WTF/)扩展可以把处理 Web 表单的过程变成一 种愉悦的体验。这个扩展对独立的 WTForms(http://wtforms.simplecodes.com)包进行了包 装,方便集成到 Flask 程序中。
Flask-WTF 及其依赖可使用 pip 安装:

pip3 install flask-wtf

默认情况下,Flask-WTF能保护所有表单免受跨站请求伪造(Cross-Site Request Forgery,
CSRF)的攻击。恶意网站把请求发送到被攻击者已登录的其他网站时就会引发 CSRF 攻击。 为了实现 CSRF 保护,Flask-WTF 需要程序设置一个密钥。Flask-WTF 使用这个密钥生成
加密令牌,再用令牌验证请求中表单数据的真伪。设置密钥的方法设置 Flask-WTF

app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
   33

app.config 字典可用来存储框架、扩展和程序本身的配置变量。使用标准的字典句法就能 把配置值添加到 app.config 对象中。这个对象还提供了一些方法,可以从文件或环境中导 入配置值。
SECRET_KEY 配置变量是通用密钥,可在 Flask 和多个第三方扩展中使用。如其名所示,加 密的强度取决于变量值的机密程度。不同的程序要使用不同的密钥,而且要保证其他人不 知道你所用的字符串。

表单类:
使用 Flask-WTF 时,每个 Web 表单都由一个继承自 Form 的类表示。这个类定义表单中的 一组字段,每个字段都用对象表示。字段对象可附属一个或多个验证函数。验证函数用来 验证用户提交的输入值是否符合要求。
下面是一个简单的 Web 表单,包含一个文本字段和一个提交按钮。

from flask_wtf import Form
from wtforms import StringField, SubmitField 
from wtforms.validators import Required
class NameForm(Form):
    name = StringField('What is your name?', validators=[Required()]) 
    submit = SubmitField('Submit')

这个表单中的字段都定义为类变量,类变量的值是相应字段类型的对象。在这个示例中, NameForm 表单中有一个名为 name 的文本字段和一个名为 submit 的提交按钮。StringField 类表示属性为 type="text" 的 <input> 元素。SubmitField 类表示属性为 type="submit" 的 <input> 元素。字段构造函数的第一个参数是把表单渲染成 HTML 时使用的标号。
StringField 构造函数中的可选参数 validators 指定一个由验证函数组成的列表,在接受 用户提交的数据之前验证数据。验证函数 Required() 确保提交的字段不为空。

WTForms 支持的 HTML 标准字段如下图

9278B3CA-AF23-4D0D-82F1-069F17A27188.png

WTForms 内建的验证函数

3A0C9B81-7C18-404B-AC94-6E71EDE4FEE5.png

把表单类渲染为HTML
例通过参数 form 传入模板,在模板中可以生成一个简单的表单,如下所示:

     <form method="POST">
         {{ form.hidden_tag() }}
         {{ form.name.label }} {{ form.name() }}
         {{ form.submit() }}
    </form>

即便能指定 HTML 属性,但按照这种方式渲染表单的工作量还是很大,所以在条件允许的 情况下最好能使用 Bootstrap 中的表单样式。Flask-Bootstrap 提供了一个非常高端的辅助函 数,可以使用 Bootstrap 中预先定义好的表单样式渲染整个 Flask-WTF 表单,而这些操作 只需一次调用即可完成。使用 Flask-Bootstrap,上述表单可使用下面的方式渲染:

{% import "bootstrap/wtf.html" as wtf %}
 {{ wtf.quick_form(form) }}

Flash消息
请求完成后,有时需要让用户知道状态发生了变化。这里可以使用确认消息、警告或者错 误提醒。一个典型例子是,用户提交了有一项错误的登录表单后,服务器发回的响应重新 渲染了登录表单,并在表单上面显示一个消息,提示用户用户名或密码错误。
这种功能是 Flask 的核心特性。

@app.route('/',methods = ['GET','POST'])
def index():
    name = None
    form = NameForm()
    if form.validate_on_submit():
        
        old_name = session.get('name')
        new_name = form.name.data
        session['name'] = new_name
        if old_name is not None and old_name != new_name:
            flash('你似乎改变了你的名字')
        return redirect(url_for('index'))
        
    return render_template('testForm.html', form=form, name=session.get('name'))

在这个示例中,每次提交的名字都会和存储在用户会话中的名字进行比较,而会话中存储 的名字是前一次在这个表单中提交的数据。如果两个名字不一样,就会调用 flash() 函数, 在发给客户端的下一个响应中显示一个消息。

获取get_flashed_messages()来获取消息
在html中显示消息

{% for message in get_flashed_messages() %}
        <div class="alert alert-waring">
            <button type="button" class="close" data-dismiss="alert">&times;</button>
            {{message}}
        </div>
{% endfor %}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容