<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>模板 &#8211; 李辉 / Grey Li</title>
	<atom:link href="https://greyli.com/tag/%E6%A8%A1%E6%9D%BF/feed/" rel="self" type="application/rss+xml" />
	<link>https://greyli.com</link>
	<description>一个编程和写作爱好者的在线记事本</description>
	<lastBuildDate>Thu, 06 Nov 2025 11:36:11 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.9.26</generator>

<image>
	<url>https://greyli.com/wp-content/uploads/2025/03/avatar-500-compressed-144x144.jpg</url>
	<title>模板 &#8211; 李辉 / Grey Li</title>
	<link>https://greyli.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>《Flask 入门教程》第 6 章：模板优化</title>
		<link>https://greyli.com/flask-tutorial-chapter-6-advanced-templating/</link>
		<comments>https://greyli.com/flask-tutorial-chapter-6-advanced-templating/#respond</comments>
		<pubDate>Tue, 01 Jan 2019 05:31:25 +0000</pubDate>
		<dc:creator><![CDATA[李辉]]></dc:creator>
				<category><![CDATA[计算机与编程]]></category>
		<category><![CDATA[Flask]]></category>
		<category><![CDATA[Flask 入门教程]]></category>
		<category><![CDATA[Jinja2]]></category>
		<category><![CDATA[模板]]></category>

		<guid isPermaLink="false">http://greyli.com/?p=2093</guid>
		<description><![CDATA[这一章我们会继续完善模板，学习几个非常实用的模板编写技巧，为下一章实现创建、编辑电影条目打下基础。 自定义错误 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>
	这一章我们会继续完善模板，学习几个非常实用的模板编写技巧，为下一章实现创建、编辑电影条目打下基础。
</p>
<h2>
	自定义错误页面<br />
</h2>
<p>
	为了引出相关知识点，我们首先要为 Watchlist 编写一个错误页面。目前的程序中，如果你访问一个不存在的 URL，比如 /hello，Flask 会自动返回一个 404 错误响应。默认的错误页面非常简陋，如下图所示：
</p>
<p>
	<a href="https://github.com/greyli/flask-tutorial/blob/master/chapters/images/6-1.png" rel="noopener noreferrer" target="_blank"><img alt="默认的 404 错误页面" src="https://github.com/greyli/flask-tutorial/raw/master/chapters/images/6-1.png" /></a>
</p>
<p>
	在 Flask 程序中自定义错误页面非常简单，我们先编写一个 404 错误页面模板，如下所示：
</p>
<p>
	<em>templates/404.html：404 错误页面模板</em>
</p>
<pre>
&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;title&gt;{{ user.name }}&#39;s Watchlist&lt;/title&gt;
    &lt;link rel=&quot;icon&quot; href=&quot;{{ url_for(&#39;static&#39;, filename=&#39;favicon.ico&#39;) }}&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;{{ url_for(&#39;static&#39;, filename=&#39;style.css&#39;) }}&quot; type=&quot;text/css&quot;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h2&gt;
        &lt;img alt=&quot;Avatar&quot; class=&quot;avatar&quot; src=&quot;{{ url_for(&#39;static&#39;, filename=&#39;images/avatar.png&#39;) }}&quot;&gt;
        {{ user.name }}&#39;s Watchlist
    &lt;/h2&gt;
    &lt;ul class=&quot;movie-list&quot;&gt;
        &lt;li&gt;
            Page Not Found - 404
            &lt;span class=&quot;float-right&quot;&gt;
                &lt;a href=&quot;{{ url_for(&#39;index&#39;) }}&quot;&gt;Go Back&lt;/a&gt;
            &lt;/span&gt;
        &lt;/li&gt;
    &lt;/ul&gt;
    &lt;footer&gt;
        &lt;small&gt;&amp;copy; 2018 &lt;a href=&quot;http://helloflask.com/tutorial&quot;&gt;HelloFlask&lt;/a&gt;&lt;/small&gt;
	&lt;/footer&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>
	接着使用&nbsp;<code>app.errorhandler()</code>&nbsp;装饰器注册一个错误处理函数，它的作用和视图函数类似，当 404 错误发生时，这个函数会被触发，返回值会作为响应主体返回给客户端：
</p>
<p>
	<em>app.py：404 错误处理函数</em>
</p>
<pre>
@app.errorhandler(404)  # 传入要处理的错误代码
def page_not_found(e):  # 接受异常对象作为参数
    user = User.query.first()
    return render_template(&#39;404.html&#39;, user=user), 404  # 返回模板和状态码</pre>
<p>
	<strong>提示</strong>&nbsp;和我们前面编写的视图函数相比，这个函数返回了状态码作为第二个参数，普通的视图函数之所以不用写出状态码，是因为默认会使用 200 状态码，表示成功。
</p>
<p>
	这个视图返回渲染好的错误模板，因为模板中使用了 user 变量，这里也要一并传入。现在访问一个不存在的 URL，会显示我们自定义的错误页面：
</p>
<p>
	<a href="https://github.com/greyli/flask-tutorial/blob/master/chapters/images/6-2.png" rel="noopener noreferrer" target="_blank"><img alt="自定义 404 错误页面" src="https://github.com/greyli/flask-tutorial/raw/master/chapters/images/6-2.png" /></a>
</p>
<p>
	编写完这部分代码后，你会发现两个问题：
</p>
<ul>
<li>
		错误页面和主页都需要使用 user 变量，所以在对应的处理函数里都要查询数据库并传入 user 变量。因为每一个页面都需要获取用户名显示在页面顶部，如果有更多的页面，那么每一个对应的视图函数都要重复传入这个变量。
	</li>
<li>
		错误页面模板和主页模板有大量重复的代码，比如&nbsp;<code>&lt;head&gt;</code>&nbsp;标签的内容，页首的标题，页脚信息等。这种重复不仅带来不必要的工作量，而且会让修改变得更加麻烦。举例来说，如果页脚信息需要更新，那么每个页面都要一一进行修改。
	</li>
</ul>
<p>
	显而易见，这两个问题有更优雅的处理方法，下面我们来一一了解。
</p>
<h2>
	模板上下文处理函数<br />
</h2>
<p>
	对于多个模板内都需要使用的变量，我们可以使用&nbsp;<code>app.context_processor</code>&nbsp;装饰器注册一个模板上下文处理函数，如下所示：
</p>
<p>
	<em>app.py：模板上下文处理函数</em>
</p>
<pre>
@app.context_processor
def inject_user():  # 函数名可以随意修改
    user = User.query.first()
    return dict(user=user)  # 需要返回字典，等同于return {&#39;user&#39;: user}</pre>
<p>
	这个函数返回的变量（以字典键值对的形式）将会统一注入到每一个模板的上下文环境中，因此可以直接在模板中使用。
</p>
<p>
	现在我们可以删除 404 错误处理函数和主页视图函数中的&nbsp;<code>user</code>&nbsp;变量定义，并删除在&nbsp;<code>render_template()</code>&nbsp;函数里传入的关键字参数：
</p>
<pre>
@app.context_processor
def inject_user():
    user = User.query.first()
    return dict(user=user)


@app.errorhandler(404)
def page_not_found(e):
    return render_template(&#39;404.html&#39;), 404


@app.route(&#39;/&#39;)
def index():
    movies = Movie.query.all()
    return render_template(&#39;index.html&#39;, movies=movies)</pre>
<p>
	同样的，后面我们创建的任意一个模板，都可以在模板中直接使用&nbsp;<code>user</code>&nbsp;变量。
</p>
<h2>
	使用模板继承组织模板<br />
</h2>
<p>
	对于模板内容重复的问题，Jinja2 提供了模板继承的支持。这个机制和 Python 类继承非常类似：我们可以定义一个父模板，一般会称之为基模板（base template）。基模板中包含完整的 HTML 结构和导航栏、页首、页脚都通用部分。在子模板里，我们可以使用&nbsp;<code>extends</code>&nbsp;标签来声明继承自某个基模板。
</p>
<p>
	基模板中需要在实际的子模板中追加或重写的部分则可以定义成块（block）。块使用&nbsp;<code>block</code>&nbsp;标签创建，&nbsp;<code>{% block 块名称 %}</code>作为开始标记，<code>{% endblock %}</code>&nbsp;或&nbsp;<code>{% endblock 块名称 %}</code>&nbsp;作为结束标记。通过在子模板里定义一个同样名称的块，你可以向基模板的对应块位置追加或重写内容。
</p>
<h3>
	编写基础模板<br />
</h3>
<p>
	下面是新编写的基模板 base.html：
</p>
<p>
	<em>templates/base.html：基模板</em>
</p>
<pre>
&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    {% block head %}
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;{{ user.name }}&#39;s Watchlist&lt;/title&gt;
    &lt;link rel=&quot;icon&quot; href=&quot;{{ url_for(&#39;static&#39;, filename=&#39;favicon.ico&#39;) }}&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;{{ url_for(&#39;static&#39;, filename=&#39;style.css&#39;) }}&quot; type=&quot;text/css&quot;&gt;
    {% endblock %}
&lt;/head&gt;
&lt;body&gt;
    &lt;h2&gt;
        &lt;img alt=&quot;Avatar&quot; class=&quot;avatar&quot; src=&quot;{{ url_for(&#39;static&#39;, filename=&#39;images/avatar.png&#39;) }}&quot;&gt;
        {{ user.name }}&#39;s Watchlist
    &lt;/h2&gt;
    &lt;nav&gt;
        &lt;ul&gt;
            &lt;li&gt;&lt;a href=&quot;{{ url_for(&#39;index&#39;) }}&quot;&gt;Home&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/nav&gt;
    {% block content %}{% endblock %}
    &lt;footer&gt;
        &lt;small&gt;&amp;copy; 2018 &lt;a href=&quot;http://helloflask.com/tutorial&quot;&gt;HelloFlask&lt;/a&gt;&lt;/small&gt;
	&lt;/footer&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>
	在基模板里，我们添加了两个块，一个是包含&nbsp;<code>&lt;head&gt;&lt;/head&gt;</code>&nbsp;内容的&nbsp;<code>head</code>&nbsp;块，另一个是用来在子模板中插入页面主体内容的&nbsp;<code>content</code>&nbsp;块。在复杂的项目里，你可以定义更多的块，方便在子模板中对基模板的各个部分插入内容。另外，块的名字没有特定要求，你可以自由修改。
</p>
<p>
	在编写子模板之前，我们先来看一下基模板中的两处新变化。
</p>
<p>
	第一处，我们添加了一个新的&nbsp;<code>&lt;meta&gt;</code>&nbsp;元素，这个元素会设置页面的视口，让页面根据设备的宽度来自动缩放页面，让移动设备拥有更好的浏览体验：
</p>
<pre>
&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;</pre>
<p>
	第二处，新的页面添加了一个导航栏：
</p>
<pre>
&lt;nav&gt;
    &lt;ul&gt;
        &lt;li&gt;&lt;a href=&quot;{{ url_for(&#39;index&#39;) }}&quot;&gt;Home&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
&lt;/nav&gt;</pre>
<p>
	导航栏对应的 CSS 代码如下所示：
</p>
<pre>
nav ul {
    list-style-type: none;
    margin: 0;
    padding: 0;
    overflow: hidden;
    background-color: #333;
}

nav li {
    float: left;
}

nav li a {
    display: block;
    color: white;
    text-align: center;
    padding: 8px 12px;
    text-decoration: none;
}

nav li a:hover {
    background-color: #111;
}</pre>
<h3>
	编写子模板<br />
</h3>
<p>
	创建了基模板后，子模板的编写会变得非常简单。下面是新的主页模板（index.html）：
</p>
<p>
	<em>templates/index.html：继承基模板的主页模板</em>
</p>
<pre>
{% extends &#39;base.html&#39; %}

{% block content %}
&lt;p&gt;{{ movies|length }} Titles&lt;/p&gt;
&lt;ul class=&quot;movie-list&quot;&gt;
    {% for movie in movies %}
    &lt;li&gt;{{ movie.title }} - {{ movie.year }}
        &lt;span class=&quot;float-right&quot;&gt;
            &lt;a class=&quot;imdb&quot; href=&quot;https://www.imdb.com/find?q={{ movie.title }}&quot; target=&quot;_blank&quot; title=&quot;Find this movie on IMDb&quot;&gt;IMDb&lt;/a&gt;
        &lt;/span&gt;
    &lt;/li&gt;
    {% endfor %}
&lt;/ul&gt;
&lt;img alt=&quot;Walking Totoro&quot; class=&quot;totoro&quot; src=&quot;{{ url_for(&#39;static&#39;, filename=&#39;images/totoro.gif&#39;) }}&quot; title=&quot;to~to~ro~&quot;&gt;
{% endblock %}</pre>
<p>
	第一行使用&nbsp;<code>extends</code>&nbsp;标签声明扩展自模板 base.html，可以理解成&ldquo;这个模板继承自 base.html&ldquo;。接着我们定义了&nbsp;<code>content</code>块，这里的内容会插入到基模板中&nbsp;<code>content</code>&nbsp;块的位置。
</p>
<p>
	<strong>提示</strong>&nbsp;默认的块重写行为是覆盖，如果你想向父块里追加内容，可以在子块中使用&nbsp;<code>super()</code>&nbsp;声明，即&nbsp;<code>{{ super() }}</code>。
</p>
<p>
	404 错误页面的模板类似，如下所示：
</p>
<p>
	<em>templates/index.html：继承基模板的 404 错误页面模板</em>
</p>
<pre>
{% extends &#39;base.html&#39; %}

{% block content %}
&lt;ul class=&quot;movie-list&quot;&gt;
    &lt;li&gt;
        Page Not Found - 404
        &lt;span class=&quot;float-right&quot;&gt;
            &lt;a href=&quot;{{ url_for(&#39;index&#39;) }}&quot;&gt;Go Back&lt;/a&gt;
        &lt;/span&gt;
    &lt;/li&gt;
&lt;/ul&gt;
{% endblock %}</pre>
<h2>
	添加 IMDb 链接<br />
</h2>
<p>
	在主页模板里，我们还为每一个电影条目右侧添加了一个 IMDb 链接：
</p>
<pre>
&lt;span class=&quot;float-right&quot;&gt;
    &lt;a class=&quot;imdb&quot; href=&quot;https://www.imdb.com/find?q={{ movie.title }}&quot; target=&quot;_blank&quot; title=&quot;Find this movie on IMDb&quot;&gt;IMDb&lt;/a&gt;
&lt;/span&gt;</pre>
<p>
	这个链接的&nbsp;<code>href</code>&nbsp;属性的值为 IMDb 搜索页面的 URL，搜索关键词通过查询参数&nbsp;<code>q</code>&nbsp;传入，这里传入了电影的标题。
</p>
<p>
	对应的 CSS 定义如下所示：
</p>
<pre>
.float-right {
    float: right;
}

.imdb {
    font-size: 12px;
    font-weight: bold;
    color: black;
    text-decoration: none;
    background: #F5C518;
    border-radius: 5px;
    padding: 3px 5px;
}</pre>
<p>
	现在，我们的程序主页如下所示：
</p>
<p>
	<a href="https://github.com/greyli/flask-tutorial/blob/master/chapters/images/6-3.png" rel="noopener noreferrer" target="_blank"><img alt="添加导航栏和 IMDb 链接" src="https://github.com/greyli/flask-tutorial/raw/master/chapters/images/6-3.png" /></a>
</p>
<h2>
	本章小结<br />
</h2>
<p>
	本章我们主要学习了 Jinja2 的模板继承机制，去掉了大量的重复代码，这让后续的模板编写工作变得更加轻松。结束前，让我们提交代码：
</p>
<pre>
$ git add .
$ git commit -m &quot;Add base template and error template&quot;
$ git push</pre>
<p>
	<strong>提示</strong> 你可以在 GitHub 上查看本书示例程序的对应 commit：<a href="https://github.com/greyli/watchlist/commit/3bca489421cc498289734cfef9d6ff90232df8be" spellcheck="false">3bca489</a>。
</p>
<h2>
	进阶提示<br />
</h2>
<ul>
<li>
		本章介绍的自定义错误页面是为了引出两个重要的知识点，因此并没有着重介绍错误页面本身。这里只为 404 错误编写了自定义错误页面，对于另外两个常见的错误 400 错误和 500 错误，你可以自己试着为它们编写错误处理函数和对应的模板。
	</li>
<li>
		因为示例程序的语言和电影标题使用了英文，所以电影网站的搜索链接使用了 IMDb，对于中文，你可以使用豆瓣电影或时光网。以豆瓣电影为例，它的搜索链接为&nbsp;<a href="https://movie.douban.com/subject_search?search_text=%E5%85%B3%E9%94%AE%E8%AF%8D" rel="nofollow">https://movie.douban.com/subject_search?search_text=关键词</a>，对应的&nbsp;<code>href</code>&nbsp;属性即&nbsp;<code>https://movie.douban.com/subject_search?search_text={{ movie.title }}</code>。
	</li>
<li>
		因为基模板会被所有其他页面模板继承，如果你在基模板中使用了某个变量，那么这个变量也需要使用模板上下文处理函数注入到模板里。
	</li>
<li>
		本书主页 &amp; 相关资源索引：<a data-editable="true" data-offset-key="eh3t9-1-0" href="http://helloflask.com/tutorial" target="_blank">http://helloflask.com/tutorial</a>。
	</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://greyli.com/flask-tutorial-chapter-6-advanced-templating/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>《Flask 入门教程》第 3 章：模板</title>
		<link>https://greyli.com/flask-tutorial-chapter-3-template/</link>
		<comments>https://greyli.com/flask-tutorial-chapter-3-template/#respond</comments>
		<pubDate>Sun, 16 Dec 2018 12:50:48 +0000</pubDate>
		<dc:creator><![CDATA[李辉]]></dc:creator>
				<category><![CDATA[计算机与编程]]></category>
		<category><![CDATA[Flask]]></category>
		<category><![CDATA[Flask 入门教程]]></category>
		<category><![CDATA[Jinja2]]></category>
		<category><![CDATA[模板]]></category>

		<guid isPermaLink="false">http://greyli.com/?p=2064</guid>
		<description><![CDATA[在一般的 Web 程序里，访问一个地址通常会返回一个包含各类信息的 HTML 页面。因为我们的程序是动态的，页 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>
	在一般的 Web 程序里，访问一个地址通常会返回一个包含各类信息的 HTML 页面。因为我们的程序是动态的，页面中的某些信息需要根据不同的情况来进行调整，比如对登录和未登录用户显示不同的信息，所以页面需要在用户访问时根据程序逻辑动态生成。
</p>
<p>
	我们把包含变量和运算逻辑的 HTML 或其他格式的文本叫做<strong>模板</strong>，执行这些变量替换和逻辑计算工作的过程被称为<strong>渲染</strong>，这个工作由我们这一章要学习使用的模板渲染引擎&mdash;&mdash;Jinja2 来完成。
</p>
<p>
	按照默认的设置，Flask 会从程序实例所在模块同级目录的 templates 文件夹中寻找模板，我们的程序目前存储在项目根目录的 app.py 文件里，所以我们要在项目根目录创建这个文件夹：
</p>
<pre>
$ mkdir templates</pre>
<h2>
	模板基本语法<br />
</h2>
<p>
	在社交网站上，每个人都有一个主页，借助 Jinja2 就可以写出一个通用的模板：
</p>
<pre>
&lt;h1&gt;{{ username }}的个人主页&lt;/h1&gt;
{% if bio %}
    &lt;p&gt;{{ bio }}&lt;/p&gt;  {# 这里的缩进只是为了可读性，不是必须的 #}
{% else %}
    &lt;p&gt;自我介绍为空。&lt;/p&gt;
{% endif %}  {# 大部分 Jinja 语句都需要声明关闭 #}</pre>
<p>
	Jinja2 的语法和 Python 大致相同，你在后面会陆续接触到一些常见的用法。在模板里，你需要添加特定的定界符将 Jinja2 语句和变量标记出来，下面是三种常用的定界符：
</p>
<ul>
<li>
		<code>{{ ... }}</code>&nbsp;用来标记变量。
	</li>
<li>
		<code>{% ... %}</code>&nbsp;用来标记语句，比如 if 语句，for 语句等。
	</li>
<li>
		<code>{# ... #}</code>&nbsp;用来写注释。
	</li>
</ul>
<p>
	模板中使用的变量需要在渲染的时候传递进去，具体我们后面会了解。
</p>
<h2>
	编写主页模板<br />
</h2>
<p>
	我们先在 templates 目录下创建一个 index.html 文件，作为主页模板。主页需要显示电影条目列表和个人信息，代码如下所示：
</p>
<p>
	templates/index.html：主页模板
</p>
<pre>
&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;title&gt;{{ name }}&#39;s Watchlist&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h2&gt;{{ name }}&#39;s Watchlist&lt;/h2&gt;
    {# 使用 length 过滤器获取 movies 变量的长度 #}
    &lt;p&gt;{{ movies|length }} Titles&lt;/p&gt;
    &lt;ul&gt;
        {% for movie in movies %}  {# 迭代 movies 变量 #}
        &lt;li&gt;{{ movie.title }} - {{ movie.year }}&lt;/li&gt;  {# 等同于 movie['title'] #}
        {% endfor %}  {# 使用 endfor 标签结束 for 语句 #}
    &lt;/ul&gt;
    &lt;footer&gt;
        &lt;small&gt;&amp;copy; 2018 &lt;a href=&quot;http://helloflask.com/tutorial&quot;&gt;HelloFlask&lt;/a&gt;&lt;/small&gt;
	&lt;/footer&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>
	为了方便对变量进行处理，Jinja2 提供了一些过滤器，语法形式如下：
</p>
<pre>
{{ 变量|过滤器 }}</pre>
<p>
	左侧是变量，右侧是过滤器名。比如，上面的模板里使用&nbsp;<code>length</code>&nbsp;过滤器来获取&nbsp;<code>movies</code>&nbsp;的长度，类似 Python 里的&nbsp;<code>len()</code>&nbsp;函数。
</p>
<p>
	<strong>提示</strong>&nbsp;访问&nbsp;<a href="http://jinja.pocoo.org/docs/2.10/templates/#list-of-builtin-filters" rel="nofollow">http://jinja.pocoo.org/docs/2.10/templates/#list-of-builtin-filters</a>&nbsp;查看所有可用的过滤器。
</p>
<h2>
	准备虚拟数据<br />
</h2>
<p>
	为了模拟页面渲染，我们需要先创建一些虚拟数据，用来填充页面内容：
</p>
<p>
	app.py：定义虚拟数据
</p>
<pre>
name = &#39;Grey Li&#39;
movies = [
    {&#39;title&#39;: &#39;My Neighbor Totoro&#39;, &#39;year&#39;: &#39;1988&#39;},
    {&#39;title&#39;: &#39;Dead Poets Society&#39;, &#39;year&#39;: &#39;1989&#39;},
    {&#39;title&#39;: &#39;A Perfect World&#39;, &#39;year&#39;: &#39;1993&#39;},
    {&#39;title&#39;: &#39;Leon&#39;, &#39;year&#39;: &#39;1994&#39;},
    {&#39;title&#39;: &#39;Mahjong&#39;, &#39;year&#39;: &#39;1996&#39;},
    {&#39;title&#39;: &#39;Swallowtail Butterfly&#39;, &#39;year&#39;: &#39;1996&#39;},
    {&#39;title&#39;: &#39;King of Comedy&#39;, &#39;year&#39;: &#39;1999&#39;},
    {&#39;title&#39;: &#39;Devils on the Doorstep&#39;, &#39;year&#39;: &#39;1999&#39;},
    {&#39;title&#39;: &#39;WALL-E&#39;, &#39;year&#39;: &#39;2008&#39;},
    {&#39;title&#39;: &#39;The Pork of Music&#39;, &#39;year&#39;: &#39;2012&#39;},
]</pre>
<h2>
	渲染主页模板<br />
</h2>
<p>
	使用&nbsp;<code>render_template()</code>&nbsp;函数可以把模板渲染出来，必须传入的参数为模板文件名（相对于 templates 根目录的文件路径），这里即&nbsp;<code>&#39;index.html&#39;</code>。为了让模板正确渲染，我们还要把模板内部使用的变量通过关键字参数传入这个函数，如下所示：
</p>
<p>
	app.py：返回渲染好的模板作为响应
</p>
<pre>
from flask import Flask, render_template

# ...

@app.route(&#39;/&#39;)
def index():
    return render_template(&#39;index.html&#39;, name=name, movies=movies)</pre>
<p>
	为了更好的表示这个视图函数的作用，我们把原来的函数名&nbsp;<code>hello</code>&nbsp;改为&nbsp;<code>index</code>，意思是&ldquo;索引&rdquo;，即主页。
</p>
<p>
	在传入<code>&nbsp;render_template()</code>&nbsp;函数的关键字参数中，左边的&nbsp;<code>movies</code>&nbsp;是模板中使用的变量名称，右边的&nbsp;<code>movies</code>&nbsp;则是该变量指向的实际对象。这里传入模板的&nbsp;<code>name</code>&nbsp;是字符串，<code>movies</code>&nbsp;是列表，但能够在模板里使用的不只这两种 Python 数据结构，你也可以传入元组、字典、函数等。
</p>
<p>
	<code>render_template()</code>&nbsp;函数在调用时会识别并执行 index.html 里所有的 Jinja2 语句，返回渲染好的模板内容。在返回的页面中，变量会被替换为实际的值（包括定界符），语句（及定界符）则会在执行后被移除（注释也会一并移除）。
</p>
<p>
	现在访问&nbsp;<a href="http://localhost:5000/" rel="nofollow">http://localhost:5000/</a>&nbsp;看到的程序主页如下图所示：
</p>
<p>
	<a href="https://github.com/greyli/flask-tutorial/blob/master/chapters/images/3-1.png" rel="noopener noreferrer" target="_blank"><img alt="3-1" src="https://github.com/greyli/flask-tutorial/raw/master/chapters/images/3-1.png" /></a>
</p>
<h2>
	本章小结<br />
</h2>
<p>
	这一章我们编写了一个简单的主页。结束前，让我们提交代码：
</p>
<pre>
$ git add .
$ git commit -m &quot;Add index page&quot;
$ git push</pre>
<p>
	<strong>提示</strong> 你可以在 GitHub 上查看本书示例程序的对应 commit：<a href="https://github.com/greyli/watchlist/commit/8537d98bdd7828b1f7aa2431bbd5a16e757a3cc4" spellcheck="false">8537d98</a>。
</p>
<h2>
	进阶提示<br />
</h2>
<ul>
<li>
		使用&nbsp;<a href="https://github.com/joke2k/faker">Faker</a>&nbsp;可以实现自动生成虚拟数据，它支持丰富的数据类型，比如时间、人名、地名、随机字符等等&hellip;&hellip;
	</li>
<li>
		除了过滤器，Jinja2 还在模板中提供了一些测试器、全局函数可以使用；除此之外，还有更丰富的控制结构支持，有一些我们会在后面学习到，更多的内容则可以访问&nbsp;<a href="http://jinja.pocoo.org/docs/2.10/templates/" rel="nofollow">Jinja2 文档</a>学习。
	</li>
<li>
		如果你是<a href="http://helloflask.com/book/" rel="nofollow">《Flask Web 开发实战》</a>的读者，模板相关内容可以在第 3 章《模板》找到，Faker 相关内容可以在第 7 章找到。
	</li>
<li>
		本书主页 &amp; 相关资源索引：<a data-editable="true" data-offset-key="eh3t9-1-0" href="http://helloflask.com/tutorial" target="_blank">http://helloflask.com/tutorial</a>。
	</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://greyli.com/flask-tutorial-chapter-3-template/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
