[14] [烧瓶里的部落格] 04. Templates 模板

我们已经写好验证的 views,但是如果现在启动服务的话
无论开启哪个 URL,都会看到一个TemplateNotFound错误
这是因为 view 使用了render_template(),但是 template 档案还没有写
模板档案会存放在 flaskr 资料夹内的templates文件夹内

Flask 使用Jinja模板引擎来渲染模板,范例中会使用模板来显示在使用者浏览器中的 HTML

在 Flask 中, Jinja 被设定为自动转译 HTML 模板中的任何资料,这代表直接渲染内容是安全的
任何使用者输入的可能被浏览器执行的内容,如<>,会被转译成安全的值
转译的结果在浏览器中看起来一样,但是不会被浏览器执行

Jinja 看上去以及执行起来很像 Python
在 Jinja 中,任何位於{{}}之间的变数会被输出成文字
程序判断式则是放在{%%}之间,例如iffor

与 Python 不同,不是使用缩排分隔,而是使用特殊 tag 分隔
因为区块内的渲染出来的结果可能有自己的缩排

基本 Layout

与其在不同的页面中重写整个页面的 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中进行)
选择要显示使用者名称和登出按钮,还是注册和登入按钮?
而用於生成 view 网址的url_for()也是自动可用的,不需要手动指定

在页面标题下面,content 内容的前面,模板会循环显示 get_flashed_messages() 回传的每个消息
可以用这个方式把之前在 view 中使用 flash() 保存的错误讯息显示出来

There are three blocks defined here that will be overridden in the other templates:

模板中定义了三个 block,这些区块内容会被其他模板复写

  1. {% block title %}会改变当前浏览器分页的 title

  2. {% block header %}改变页面的标题

  3. {% block content %}每个页面的具体内容,例如登入表单或者部落格文章

其他模板直接放在templates资料夹内
为了更好地管理档案,属於某个 blueprints 的模板会被放在与同名的资料夹内

注册

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 宣告这个模板继承 base.html
并且需要载入相应的 block,所有复写的内容必须位於{% block %}标签中

一个好用的模式是把{% block title %}放在{% block header %}内部
这样不但可以设定 title block,还可以把其值作为 header block 的内容,一举两得!

inputtag 使用了required属性,告诉浏览器这些输入框是必填的
如果使用者使用不支援持这个属性的旧版浏览器,或者不是用浏览器发出的请求
那麽你还是要在 view 中验证输入资料
即使前端已经做了一些验证,後端还是要完全检查,这一点非常重要!

登入

这个模板除了标题和送出按钮外,和注册模板相同

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

注册一个使用者

现在验证模板已写好,可以来测试了
启动测试环境之後开启http://127.0.0.1:5000/auth/register页面

在不填写表单的情况,尝试点击「Register」按钮,浏览器会显示出错信息

尝试在register.html中删除required属性後再次点击「Register」按钮
浏览器不会显示错误,而页面会重新载入并显示来自於 view 中 flash() 错误讯息

填写使用者名称和密码後会重定导向到登录页面
尝试输入错误的使用者名称,或者输入正确的使用者名称和错误的密码

真实情况下不要这麽做!会引发资安疑虑,弱点扫描不会过
因为提示骇客错误是来自哪个栏位,接着就可以使用枚举法暴力破解

如果登录成功,那麽会看到一个错误讯息
因为我们还没有写登入後要转向的 view:index


<<:  Day15:全端工程师的工作内容?(下)

>>:  Spring Framework X Kotlin Day 23 Integration Test

[DAY 17] VAE 简介

前言 我们已经知道了可以用一个 Auto Encoder-Decoder 的结构来学习记忆 Inpu...

Day30 阿里云30後结语

结语: 嗨大家,这30天的铁人赛就在今天要画上结尾了。今天就来跟各位聊聊这30天挑战的一些想法跟可以...

Day 6 - Using ASHX File for User Authorization Management with ASP.NET Web Forms C# 使用泛型处理常式进行权限分流

=x= 🌵 网页操作权限分流处理及 Yacht Manager - Master Page 後台主版...

DAY1- 写一个自己都喜欢的 side project

写一个自己都喜欢的 side project,是这次铁人赛系列文章的宗旨。 为什麽要写一个自己都喜欢...

伸缩自如的Flask [day5] session

假设,你今天写了一个页面或是储存了一些简单的状态或资讯,call了另外一个API或是跳转到不同页面并...