模板¶
你已经为你的应用编写了认证相关的视图,但是如果你运行服务器并尝试访问任何一个 URL,你会看到一个 TemplateNotFound
错误。这是因为视图会调用 render_template()
,但是你还没有写任何模板。模板文件将会被保存到 flaskr
包内的 templates
目录中。
模板是包含静态数据以及动态数据占位符的文件。一个模板被使用特定的数据渲染产生一个最终文档。Flask 使用 Jinja 模板库来渲染模板。
在你的应用中,你将使用模板来渲染将会显示在用户浏览器中的 HTML 文件。在 Flask 中,Jinja 被配置为 自动转义(autoescape) 任何在 HTML 模板中渲染的数据。这意味着渲染用户输入数据是安全的;任何用户输入的可能弄乱 HTML 的字符,比如 <
和 >
将会被使用 安全 值 转义,以便让它们在浏览器看起来相同但不会产生不需要的效果。
Jinja 的语法和行为和 Python 差不多。特殊的定界符用来区分 Jinja 语法和模板中的静态数据。{{
和 }}
之间的任何东西都是一个将会输出到最终文档的表达式。{%
和 %}
表示一个控制流语句,比如 if
和 for
。和 Python 不同的是,块是由开始和结束标签表示的,而不是缩进,因为块内的静态文本可能会改变缩进。
基本布局¶
应用中的每个页面都将会有相同的基本布局,围绕着不同的主体。每一个模板都将 扩展(extend) 一个基础模板并覆盖特定的部分,而不是在每个模板中编写整个 HTML 结构。
<!doctype html>
<title>{% block title %}{% endblock %} - Flaskr</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<nav>
<h1>Flaskr</h1>
<ul>
{% if g.user %}
<li><span>{{ g.user['username'] }}</span>
<li><a href="{{ url_for('auth.logout') }}">Log Out</a>
{% else %}
<li><a href="{{ url_for('auth.register') }}">Register</a>
<li><a href="{{ url_for('auth.login') }}">Log In</a>
{% endif %}
</ul>
</nav>
<section class="content">
<header>
{% block header %}{% endblock %}
</header>
{% for message in get_flashed_messages() %}
<div class="flash">{{ message }}</div>
{% endfor %}
{% block content %}{% endblock %}
</section>
g
在模板中是自动可用的。基于 g.user
是否被设定(load_logged_in_user
),要么显示用户名和登出链接,要么显示注册和登录链接。url_for()
在模板中也是自动可用的,它被用来生成指向视图的 URL,而不是手动写出来。
在页面标题之后,页面内容之前,模板迭代了 get_flashed_messages()
返回的每一条消息。你在视图里使用 flash()
来发送错误消息,而这些就是把它们显示出来的代码。
这里定义了三个将会被在其他模板内覆盖的块:
{% block title %}
将会改变显示在浏览器标签页的标题和窗口标题。{% block header %}
类似title
但会改变页面上显示的标题。{% block content %}
是每一个页面的内容所在,比如登录表单或博客文章。
基础模板直接放到 templates
目录下。为了保持其他模板组织有序,蓝图的模板将会放到一个和蓝图名称相同的目录下。
注册¶
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Register{% endblock %}</h1>
{% endblock %}
{% block content %}
<form method="post">
<label for="username">Username</label>
<input name="username" id="username" required>
<label for="password">Password</label>
<input type="password" name="password" id="password" required>
<input type="submit" value="Register">
</form>
{% endblock %}
{% extends 'base.html' %}
告诉 Jinja 这个模板应该替换基础模板中的块。所有被渲染的内容必须放到 {% block %}
标签内,它们会覆盖基础模板中的块。
这里使用了一个有用的模式,即把 {% block title %}
放到 {% block header %}
内。这会设置 title 块然后输出它的值到 header 块内,因此窗口和页面可以分享相同的标题而不用重复写两次。
这里的 input
标签使用了 required
属性。这告诉浏览器只有这些字段被填写时才提交表单。如果用户在使用不支持这个属性的旧浏览器,或是在使用其他非浏览器工具发出请求,你仍然要在 Flask 视图内验证这些数据。重要的是,即使客户端也处理一些验证,也一定总是在服务器上完整验证这些数据。
登录¶
除了标题和提交按钮,这和注册模板相同。
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Log In{% endblock %}</h1>
{% endblock %}
{% block content %}
<form method="post">
<label for="username">Username</label>
<input name="username" id="username" required>
<label for="password">Password</label>
<input type="password" name="password" id="password" required>
<input type="submit" value="Log In">
</form>
{% endblock %}
注册一个用户¶
现在认证模板已经编写好了,你可以注册一个用户。确保服务器仍在运行(如果没有就执行 flask run
),然后访问 http://127.0.0.1:5000/auth/register。
尝试在没有填写表单的情况下点击“Register”按钮,这时浏览器会显示一个错误消息。尝试从 register.html
模板移除 required
属性然后再次点击“Register”按钮。现在浏览器不会显示错误,而是会重载页面,并显示在视图中通过 flash()
发送的错误消息。
填写一个用户名和密码,你会被重定向到登录页面。尝试输入一个不正确的用户名,或是正确的用户名和错误的密码。如果你登录了,你会得到一个错误,因为重定向的目标路由 index
目前还不存在。
继续阅读 静态文件。