模板

你已经为你的应用编写了认证相关的视图,但是如果你运行服务器并尝试访问任何一个 URL,你会看到一个 TemplateNotFound 错误。这是因为视图会调用 render_template(),但是你还没有写任何模板。模板文件将会被保存到 flaskr 包内的 templates 目录中。

模板是包含静态数据以及动态数据占位符的文件。一个模板被使用特定的数据渲染产生一个最终文档。Flask 使用 Jinja 模板库来渲染模板。

在你的应用中,你将使用模板来渲染将会显示在用户浏览器中的 HTML 文件。在 Flask 中,Jinja 被配置为 自动转义(autoescape) 任何在 HTML 模板中渲染的数据。这意味着渲染用户输入数据是安全的;任何用户输入的可能弄乱 HTML 的字符,比如 <> 将会被使用 安全转义,以便让它们在浏览器看起来相同但不会产生不需要的效果。

Jinja 的语法和行为和 Python 差不多。特殊的定界符用来区分 Jinja 语法和模板中的静态数据。{{}} 之间的任何东西都是一个将会输出到最终文档的表达式。{%%} 表示一个控制流语句,比如 iffor。和 Python 不同的是,块是由开始和结束标签表示的,而不是缩进,因为块内的静态文本可能会改变缩进。

基本布局

应用中的每个页面都将会有相同的基本布局,围绕着不同的主体。每一个模板都将 扩展(extend) 一个基础模板并覆盖特定的部分,而不是在每个模板中编写整个 HTML 结构。

flaskr/templates/base.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() 来发送错误消息,而这些就是把它们显示出来的代码。

这里定义了三个将会被在其他模板内覆盖的块:

  1. {% block title %} 将会改变显示在浏览器标签页的标题和窗口标题。

  2. {% block header %} 类似 title 但会改变页面上显示的标题。

  3. {% block content %} 是每一个页面的内容所在,比如登录表单或博客文章。

基础模板直接放到 templates 目录下。为了保持其他模板组织有序,蓝图的模板将会放到一个和蓝图名称相同的目录下。

注册

flaskr/templates/auth/register.html
{% 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 视图内验证这些数据。重要的是,即使客户端也处理一些验证,也一定总是在服务器上完整验证这些数据。

登录

除了标题和提交按钮,这和注册模板相同。

flaskr/templates/auth/login.html
{% 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 目前还不存在。

继续阅读 静态文件