Flask Web Development —— 模板(上)

作者 | ipython

写代码最关键的是要易于维护且结构清晰整洁。目前为止,你看到的例子都过于简单从而没有做这方面的要求。Flask视图函数希望将两个应该完全独立的任务一并处理,两个任务有两种代码,一并处理势必会引发问题。

明摆着的任务就是生成响应,就像你在第二章看到的示例那样。对于最简单的请求确实已经足够,但是一般而言一个请求会触发应用程序的状态发生改变,而视图函数就是产生这个改变的地方。

例如,用户在网站上注册一个新的账户。用户输入邮箱地址和密码到表单并点击提交按钮。一个包含数据的请求从用户到服务器,Flask将其派发给视图函数去处理这个注册请求。这个视图函数需要告诉数据库添加新的用户并生成一个响应发回给浏览器。这两类任务通常被称作业务逻辑和 显示逻辑。

混合业务逻辑和显示逻辑会导致代码非常的难以理解和维护。想象一下,将从数据库获取的数据和必要的HTML字符串文字进行连接,生成一堆的HTML代码是多么蛋疼的一件事情。而如果将显示逻辑移到模板中去,则势必极大提高应用程序的可维护性。

模板就是包含响应文本的文件,带有占位符变量的动态部分只在请求上下文中被知道。用真实的值替换变量并返回最终响应字符串,这个过程称为渲染。对于渲染模板任务,Flask使用强大的模板引擎Jinja2。

1、Jinja2模板引擎

Jinja2模板最简单的形式就是一个包含响应文本的文件。示例3-1展示了Jinja2模板匹配示例2-1中index()视图函数响应。

示例3-1. templates/index.html:Jinja2模板

<h1>Hello World!</h1>

示例2-2中视图函数user()返回的响应有一个动态部分,该部分由一个变量代表。示例3-2展示模板实现该响应。

示例3-2. templates/user.html:Jinja2模板

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

1.1、渲染模板

Flask默认会在应用程序的templates子目录中寻找模板。下个版本的hello.py,你需要将之前定义的index.html和user.html模板存放到新的templates文件夹下。

应用程序中的视图函数需要修改来渲染这些模板。示例3-3中会展示这些变化。

示例3-3. hello.py:渲染模板

from flask import Flask, render_template

# ...

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

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

render_template函数由集成Jinja2模板引擎的Flask提供。这个函数将模板的文件名作为它的第一个参数。任何附加的参数都是一个键/值对,分别对应被模板引用的变量和真实值。在这个示例中,第二个模板收到name变量。

类似于name=name的关键字参数在前面的示例中是非常常见的,但如果你不使用就很容易混淆且非常难理解。左边的“name”代表参数名称,一般在模板中用于占位符。右边的“name”在当前范围内是一个变量,为同名的参数提供真实的值。

建议:如果你有克隆在GitHub上的应用程序,你现在可以运行git checkout 3a来切换到这个版本的应用程序。

1.2、变量

示例3-2的模板中的{{ 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>

变量可以通过filters来修改,以|为分隔符添加在变量名后。例如,下面的模板展示变量大写:

Hello, {{ name|capitalize }}

表格3-1列出一些常用的Jinja2过滤器。

表格3-1. Jinja2变量过滤器

safe是比较有趣的一个过滤器。出于安全考虑,默认情况下,Jinja2会转义所有变量。例如,如果设置变量的值为'<h1>Hello</h1>',Jinja2将渲染字符串为'<h1>Hello</h1>',这将使得h1元素显示出来且不能被浏览器解释。很多时候有必要显示存储在变量中的HTML代码,对于这些情况就可以使用safe过滤器。

警告:永远不要在不信任的值中使用safe过滤器,例如用户在web表单中输入的文本。

完整的过滤器列表可以从Jinja2官方文档中获取。

1.3、控制结构

Jinja2提供一些控制结构用于改变模板流。这一节用简单例子来介绍一些最有用的。

下面的示例展示条件语句是怎样在模板中使用的:

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

渲染一列元素在模板中是经常用到的。这个示例展示如何使用for循环做到这些:

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

Jinja2同样支持宏,这和Python代码中的函数很像。例如:

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

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

为了提高宏的复用性,可以将它们保存为单独的文件,然后在所有需要的地方import即可:

{% import 'macros.html' as macros %}

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

需要在一些地方重复使用的模板代码可以保存为一个单独的文件,所有模板都可以include它来避免重复:

{% include 'common.html' %}

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

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

这里block标签定义的派生模板是可变的。在这个示例中,有head、title、body块;注意title包含在head中。下面的示例是一个基础模块的派生模块:

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

extends指令定义这个模板由base.html派生。紧随这个指令之后的是三个在基础模板中定义的块,它们将在合适的位置插入。注意在基础模板中新定义的head块是不为空的,所以使用super() 保留原来的内容。

本节呈现的所有控制结构的实际运用将在后面展示,你会有机会看到它们是怎样工作的。

原文转自:https://segmentfault.com/a/1190000000749914

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

推荐阅读更多精彩内容